diff --git a/generators/browser-compatible-base/package.json b/generators/browser-compatible-base/package.json index 116100977e0f..611976ab4c76 100644 --- a/generators/browser-compatible-base/package.json +++ b/generators/browser-compatible-base/package.json @@ -33,7 +33,7 @@ }, "dependencies": { "@fern-api/core-utils": "workspace:*", - "@fern-api/dynamic-ir-sdk": "^61.7.0", + "@fern-api/dynamic-ir-sdk": "^62.3.0", "@fern-api/fern-definition-schema": "workspace:*", "@fern-api/logger": "workspace:*", "@fern-api/path-utils": "workspace:*", diff --git a/generators/csharp/base/package.json b/generators/csharp/base/package.json index 08bdce0591be..9853693bac4a 100644 --- a/generators/csharp/base/package.json +++ b/generators/csharp/base/package.json @@ -37,7 +37,7 @@ "@fern-api/csharp-codegen": "workspace:*", "@fern-api/fs-utils": "workspace:*", "@fern-api/logging-execa": "workspace:*", - "@fern-fern/ir-sdk": "^61.7.0", + "@fern-fern/ir-sdk": "^62.3.0", "@types/lodash-es": "^4.17.12", "@types/node": "18.15.3", "depcheck": "^1.4.7", diff --git a/generators/csharp/codegen/package.json b/generators/csharp/codegen/package.json index dfc2ce065294..acc63501e656 100644 --- a/generators/csharp/codegen/package.json +++ b/generators/csharp/codegen/package.json @@ -33,8 +33,8 @@ "dependencies": { "@fern-api/browser-compatible-base-generator": "workspace:*", "@fern-api/core-utils": "workspace:*", - "@fern-api/dynamic-ir-sdk": "^61.7.0", - "@fern-fern/ir-sdk": "^61.7.0", + "@fern-api/dynamic-ir-sdk": "^62.3.0", + "@fern-fern/ir-sdk": "^62.3.0", "zod": "^3.22.3" }, "devDependencies": { diff --git a/generators/csharp/codegen/src/context/generation-info.ts b/generators/csharp/codegen/src/context/generation-info.ts index cfe7af32c891..ed9cf671b8e3 100644 --- a/generators/csharp/codegen/src/context/generation-info.ts +++ b/generators/csharp/codegen/src/context/generation-info.ts @@ -384,7 +384,9 @@ export class Generation { }), methods: lazy({ mockOauth: () => "MockOAuthEndpoint", - getAccessTokenAsync: () => "GetAccessTokenAsync" + mockInferredAuth: () => "MockInferredAuthEndpoint", + getAccessTokenAsync: () => "GetAccessTokenAsync", + getAuthHeadersAsync: () => "GetAuthHeadersAsync" }), variables: lazy({ client: () => "client", @@ -686,6 +688,12 @@ export class Generation { origin: this.model.staticExplicit("OAuthTokenProvider"), namespace: this.namespaces.core }), + /** Inferred auth token provider for authentication */ + InferredAuthTokenProvider: () => + this.csharp.classReference({ + origin: this.model.staticExplicit("InferredAuthTokenProvider"), + namespace: this.namespaces.core + }), /** Converter for Protocol Buffer types */ ProtoConverter: () => this.csharp.classReference({ diff --git a/generators/csharp/dynamic-snippets/package.json b/generators/csharp/dynamic-snippets/package.json index 86526ccc9069..f06a8f884a27 100644 --- a/generators/csharp/dynamic-snippets/package.json +++ b/generators/csharp/dynamic-snippets/package.json @@ -36,7 +36,7 @@ "@fern-api/configs": "workspace:*", "@fern-api/core-utils": "workspace:*", "@fern-api/csharp-codegen": "workspace:*", - "@fern-api/dynamic-ir-sdk": "61.4.0", + "@fern-api/dynamic-ir-sdk": "^62.3.0", "@fern-api/path-utils": "workspace:*", "@types/lodash-es": "^4.17.12", "@types/node": "18.15.3", diff --git a/generators/csharp/dynamic-snippets/src/EndpointSnippetGenerator.ts b/generators/csharp/dynamic-snippets/src/EndpointSnippetGenerator.ts index 47ef5a269036..a9022fdf355c 100644 --- a/generators/csharp/dynamic-snippets/src/EndpointSnippetGenerator.ts +++ b/generators/csharp/dynamic-snippets/src/EndpointSnippetGenerator.ts @@ -175,6 +175,8 @@ export class EndpointSnippetGenerator extends WithGeneration { snippet: FernIr.dynamic.EndpointSnippetRequest; }): NamedArgument[] { const authArgs: NamedArgument[] = []; + + // Check if the API uses inferred auth by looking for a token endpoint if (endpoint.auth != null) { if (snippet.auth != null) { authArgs.push(...this.getConstructorAuthArgs({ auth: endpoint.auth, values: snippet.auth })); @@ -324,8 +326,7 @@ export class EndpointSnippetGenerator extends WithGeneration { case "oauth": return values.type === "oauth" ? this.getConstructorOAuthArgs({ auth, values }) : []; case "inferred": - this.addWarning("The C# SDK Generator does not support Inferred auth scheme yet"); - return []; + return values.type === "inferred" ? this.getConstructorInferredAuthArgs({ auth, values }) : []; default: assertNever(auth); } @@ -411,6 +412,41 @@ export class EndpointSnippetGenerator extends WithGeneration { ]; } + private getConstructorInferredAuthArgs({ + auth, + values + }: { + auth: FernIr.dynamic.InferredAuth; + values: FernIr.dynamic.InferredAuthValues; + }): NamedArgument[] { + const args: NamedArgument[] = []; + + // Use parameters from the IR + if (auth.parameters != null) { + for (const param of auth.parameters) { + const wireValue = param.name.wireValue; + const value = values.values?.[wireValue]; + + args.push({ + name: this.context.getParameterName(param.name.name), + assignment: this.context.dynamicLiteralMapper.convert({ + typeReference: param.typeReference, + value: value, + fallbackToDefault: wireValue + }) + }); + } + } else { + this.context.errors.add({ + severity: Severity.Critical, + message: + "Inferred auth parameters are not defined in the IR. Please ensure you're using IR version 62.3.0 or later." + }); + } + + return args; + } + private getConstructorHeaderArgs({ headers, values diff --git a/generators/csharp/model/package.json b/generators/csharp/model/package.json index ee0c44cb9855..e685a4dbf865 100644 --- a/generators/csharp/model/package.json +++ b/generators/csharp/model/package.json @@ -46,7 +46,7 @@ "@fern-api/csharp-codegen": "workspace:*", "@fern-api/csharp-formatter": "workspace:*", "@fern-api/fs-utils": "workspace:*", - "@fern-fern/ir-sdk": "^61.7.0", + "@fern-fern/ir-sdk": "^62.3.0", "@types/node": "18.15.3", "depcheck": "^1.4.7", "typescript": "5.9.3", diff --git a/generators/csharp/model/versions.yml b/generators/csharp/model/versions.yml index eb97c2cd161b..ae92ec45a497 100644 --- a/generators/csharp/model/versions.yml +++ b/generators/csharp/model/versions.yml @@ -1,4 +1,11 @@ # yaml-language-server: $schema=../../../fern-versions-yml.schema.json +- version: 0.0.6 + changelogEntry: + - type: internal + summary: Upgrade to IR version 61. + createdAt: "2025-12-11" + irVersion: 62 + - version: 0.0.5 changelogEntry: - type: chore diff --git a/generators/csharp/sdk/package.json b/generators/csharp/sdk/package.json index 612c2c2e9ec7..b4013dcd5691 100644 --- a/generators/csharp/sdk/package.json +++ b/generators/csharp/sdk/package.json @@ -50,7 +50,7 @@ "@fern-api/logger": "workspace:*", "@fern-fern/generator-cli-sdk": "^0.1.5", "@fern-fern/generator-exec-sdk": "^0.0.1167", - "@fern-fern/ir-sdk": "^61.7.0", + "@fern-fern/ir-sdk": "^62.3.0", "@types/node": "18.15.3", "@types/url-join": "4.0.1", "depcheck": "^1.4.7", diff --git a/generators/csharp/sdk/src/SdkGeneratorCli.ts b/generators/csharp/sdk/src/SdkGeneratorCli.ts index dba493885a78..b838cf08ee81 100644 --- a/generators/csharp/sdk/src/SdkGeneratorCli.ts +++ b/generators/csharp/sdk/src/SdkGeneratorCli.ts @@ -20,6 +20,7 @@ import { BaseExceptionGenerator } from "./error/BaseExceptionGenerator"; import { CustomExceptionInterceptorGenerator } from "./error/CustomExceptionInterceptorGenerator"; import { ErrorGenerator } from "./error/ErrorGenerator"; import { generateSdkTests } from "./generateSdkTests"; +import { InferredAuthTokenProviderGenerator } from "./inferred-auth/InferredAuthTokenProviderGenerator"; import { OauthTokenProviderGenerator } from "./oauth/OauthTokenProviderGenerator"; import { BaseOptionsGenerator } from "./options/BaseOptionsGenerator"; import { ClientOptionsGenerator } from "./options/ClientOptionsGenerator"; @@ -242,6 +243,15 @@ export class SdkGeneratorCLI extends AbstractCsharpGeneratorCli { context.project.addSourceFiles(oauthTokenProvider.generate()); } + const inferred = context.getInferredAuth(); + if (inferred != null) { + const inferredAuthTokenProvider = new InferredAuthTokenProviderGenerator({ + context, + scheme: inferred + }); + context.project.addSourceFiles(inferredAuthTokenProvider.generate()); + } + const testGenerator = new TestFileGenerator(context); const test = testGenerator.generate(); context.project.addTestFiles(test); @@ -282,7 +292,10 @@ export class SdkGeneratorCLI extends AbstractCsharpGeneratorCli { context.logger.debug("No snippets were produced; skipping README.md generation."); return; } - const content = await context.generatorAgent.generateReadme({ context, endpointSnippets }); + const content = await context.generatorAgent.generateReadme({ + context, + endpointSnippets + }); const otherPath = context.settings.outputPath.other; context.project.addRawFiles( new File(context.generatorAgent.README_FILENAME, RelativeFilePath.of(otherPath), content) diff --git a/generators/csharp/sdk/src/SdkGeneratorContext.ts b/generators/csharp/sdk/src/SdkGeneratorContext.ts index 9f69746ef9fe..10fa122b16f9 100644 --- a/generators/csharp/sdk/src/SdkGeneratorContext.ts +++ b/generators/csharp/sdk/src/SdkGeneratorContext.ts @@ -13,6 +13,7 @@ import { FernFilepath, HttpEndpoint, HttpService, + InferredAuthScheme, IntermediateRepresentation, Name, NameAndWireValue, @@ -323,6 +324,15 @@ export class SdkGeneratorContext extends GeneratorContext { return undefined; } + public getInferredAuth(): InferredAuthScheme | undefined { + for (const scheme of this.ir.auth.schemes) { + if (scheme.type === "inferred") { + return scheme; + } + } + return undefined; + } + public resolveEndpoint(service: HttpService, endpointId: EndpointId): HttpEndpoint { const httpEndpoint = service.endpoints.find((endpoint) => endpoint.id === endpointId); if (httpEndpoint == null) { diff --git a/generators/csharp/sdk/src/endpoint/AbstractEndpointGenerator.ts b/generators/csharp/sdk/src/endpoint/AbstractEndpointGenerator.ts index 34ea222fcc04..b1a497466149 100644 --- a/generators/csharp/sdk/src/endpoint/AbstractEndpointGenerator.ts +++ b/generators/csharp/sdk/src/endpoint/AbstractEndpointGenerator.ts @@ -8,7 +8,9 @@ import { EndpointSignatureInfo } from "./EndpointSignatureInfo"; import { getEndpointRequest } from "./utils/getEndpointRequest"; import { getEndpointReturnType } from "./utils/getEndpointReturnType"; -type PagingEndpoint = HttpEndpoint & { pagination: NonNullable }; +type PagingEndpoint = HttpEndpoint & { + pagination: NonNullable; +}; export abstract class AbstractEndpointGenerator extends WithGeneration { private exampleGenerator: ExampleGenerator; @@ -69,10 +71,17 @@ export abstract class AbstractEndpointGenerator extends WithGeneration { endpoint: HttpEndpoint; endpointType: "unpaged" | "paged"; }): EndpointSignatureInfo { - const request = getEndpointRequest({ context: this.context, endpoint, serviceId }); + const request = getEndpointRequest({ + context: this.context, + endpoint, + serviceId + }); const requestParameter = request != null - ? this.csharp.parameter({ type: request.getParameterType(), name: request.getParameterName() }) + ? this.csharp.parameter({ + type: request.getParameterType(), + name: request.getParameterName() + }) : undefined; const { pathParameters, pathParameterReferences } = this.getAllPathParameters({ endpoint, @@ -154,7 +163,9 @@ export abstract class AbstractEndpointGenerator extends WithGeneration { this.csharp.parameter({ docs: pathParam.docs, name: parameterName, - type: this.context.csharpTypeMapper.convert({ reference: pathParam.valueType }) + type: this.context.csharpTypeMapper.convert({ + reference: pathParam.valueType + }) }) ); } diff --git a/generators/csharp/sdk/src/endpoint/grpc/GrpcEndpointGenerator.ts b/generators/csharp/sdk/src/endpoint/grpc/GrpcEndpointGenerator.ts index e5717e933ea9..c048b219ce2c 100644 --- a/generators/csharp/sdk/src/endpoint/grpc/GrpcEndpointGenerator.ts +++ b/generators/csharp/sdk/src/endpoint/grpc/GrpcEndpointGenerator.ts @@ -24,7 +24,10 @@ export class GrpcEndpointGenerator extends AbstractEndpointGenerator { cls: ast.Class, { serviceId, endpoint, rawGrpcClientReference, grpcClientInfo }: GrpcEndpointGenerator.Args ) { - const endpointSignatureInfo = this.getEndpointSignatureInfo({ serviceId, endpoint }); + const endpointSignatureInfo = this.getEndpointSignatureInfo({ + serviceId, + endpoint + }); const parameters = [...endpointSignatureInfo.baseParameters]; parameters.push( this.csharp.parameter({ @@ -57,7 +60,10 @@ export class GrpcEndpointGenerator extends AbstractEndpointGenerator { summary: endpoint.docs, return_: endpointSignatureInfo.returnType, body: this.settings.includeExceptionHandler - ? this.wrapWithExceptionHandler({ body, returnType: endpointSignatureInfo.returnType }) + ? this.wrapWithExceptionHandler({ + body, + returnType: endpointSignatureInfo.returnType + }) : body, codeExample: snippet?.endpointCall }); diff --git a/generators/csharp/sdk/src/endpoint/http/HttpEndpointGenerator.ts b/generators/csharp/sdk/src/endpoint/http/HttpEndpointGenerator.ts index d2c1a102a204..11d17c9eaadb 100644 --- a/generators/csharp/sdk/src/endpoint/http/HttpEndpointGenerator.ts +++ b/generators/csharp/sdk/src/endpoint/http/HttpEndpointGenerator.ts @@ -106,7 +106,10 @@ export class HttpEndpointGenerator extends AbstractEndpointGenerator { rawClient: RawClient; } ) { - const endpointSignatureInfo = this.getUnpagedEndpointSignatureInfo({ serviceId, endpoint }); + const endpointSignatureInfo = this.getUnpagedEndpointSignatureInfo({ + serviceId, + endpoint + }); const parameters = [...endpointSignatureInfo.baseParameters]; parameters.push(this.getRequestOptionsParameter({ endpoint })); parameters.push( @@ -365,7 +368,9 @@ export class HttpEndpointGenerator extends AbstractEndpointGenerator { value._visit({ json: (jsonChunk) => { readLineFromResponse(); - const payloadType = context.csharpTypeMapper.convert({ reference: jsonChunk.payload }); + const payloadType = context.csharpTypeMapper.convert({ + reference: jsonChunk.payload + }); deserializeJsonChunk( payloadType, context.generation.Types.JsonUtils, @@ -389,7 +394,9 @@ export class HttpEndpointGenerator extends AbstractEndpointGenerator { writer.popScope(); }, sse: (sseChunk) => { - const payloadType = context.csharpTypeMapper.convert({ reference: sseChunk.payload }); + const payloadType = context.csharpTypeMapper.convert({ + reference: sseChunk.payload + }); writer.writeLine(`if (${names.variables.response}.StatusCode is >= 200 and < 400)`); writer.pushScope(); @@ -445,7 +452,9 @@ export class HttpEndpointGenerator extends AbstractEndpointGenerator { writer.popScope(); }, json: (reference) => { - const astType = this.context.csharpTypeMapper.convert({ reference: reference.responseBodyType }); + const astType = this.context.csharpTypeMapper.convert({ + reference: reference.responseBodyType + }); writer.writeLine(`if (${this.names.variables.response}.StatusCode is >= 200 and < 400)`); writer.pushScope(); @@ -511,9 +520,14 @@ export class HttpEndpointGenerator extends AbstractEndpointGenerator { } ) { this.assertHasPagination(endpoint); - const endpointSignatureInfo = this.getEndpointSignatureInfo({ serviceId, endpoint }); + const endpointSignatureInfo = this.getEndpointSignatureInfo({ + serviceId, + endpoint + }); const parameters = [...endpointSignatureInfo.baseParameters]; - const optionsParamName = this.getRequestOptionsParamNameForEndpoint({ endpoint }); + const optionsParamName = this.getRequestOptionsParamNameForEndpoint({ + endpoint + }); const requestOptionsParam = this.getRequestOptionsParameter({ endpoint }); parameters.push(requestOptionsParam); parameters.push( @@ -529,7 +543,10 @@ export class HttpEndpointGenerator extends AbstractEndpointGenerator { const snippet = this.getHttpPagerMethodSnippet({ endpoint }); const body = this.csharp.codeblock((writer) => { const requestParameter = endpointSignatureInfo.requestParameter; - const unpagedEndpointResponseType = getEndpointReturnType({ context: this.context, endpoint }); + const unpagedEndpointResponseType = getEndpointReturnType({ + context: this.context, + endpoint + }); if (!unpagedEndpointResponseType) { throw new Error("Internal error; a response type is required for pagination endpoints"); } @@ -844,10 +861,18 @@ export class HttpEndpointGenerator extends AbstractEndpointGenerator { rawClientReference: string; writer: Writer; }): void { - const endpointSignatureInfo = this.getEndpointSignatureInfo({ serviceId, endpoint }); - const optionsParamName = this.getRequestOptionsParamNameForEndpoint({ endpoint }); + const endpointSignatureInfo = this.getEndpointSignatureInfo({ + serviceId, + endpoint + }); + const optionsParamName = this.getRequestOptionsParamNameForEndpoint({ + endpoint + }); const itemType = this.getPaginationItemType(endpoint); - const unpagedEndpointResponseType = getEndpointReturnType({ context: this.context, endpoint }); + const unpagedEndpointResponseType = getEndpointReturnType({ + context: this.context, + endpoint + }); if (!unpagedEndpointResponseType) { throw new Error("Internal error; a response type is required for pagination endpoints"); } @@ -940,7 +965,10 @@ export class HttpEndpointGenerator extends AbstractEndpointGenerator { : fail(`Expected ClassReference, got ${encType.fullyQualifiedName}`); if (!propertyPath || propertyPath.length === 0) { - return { code: this.csharp.getPropertyName(enclosingType, property), enclosingType }; + return { + code: this.csharp.getPropertyName(enclosingType, property), + enclosingType + }; } let optional = ""; @@ -951,7 +979,9 @@ export class HttpEndpointGenerator extends AbstractEndpointGenerator { const propertyName = this.csharp.getPropertyName(enclosingType, val); // get the type of the current property - let typeOfValue = this.context.csharpTypeMapper.convert({ reference: val.type }); + let typeOfValue = this.context.csharpTypeMapper.convert({ + reference: val.type + }); optional = allowOptional && is.Optional(typeOfValue) ? "?" : ""; typeOfValue = typeOfValue.asNonOptional(); @@ -1098,7 +1128,11 @@ export class HttpEndpointGenerator extends AbstractEndpointGenerator { }): ast.MethodInvocation | undefined { const service = this.context.getHttpService(serviceId) ?? fail(`Service with id ${serviceId} not found`); const serviceFilePath = service.name.fernFilepath; - const args = this.getNonEndpointArguments({ endpoint, example, parseDatetimes }); + const args = this.getNonEndpointArguments({ + endpoint, + example, + parseDatetimes + }); const endpointRequestSnippet = this.getEndpointRequestSnippet(example, endpoint, serviceId, parseDatetimes); if (endpointRequestSnippet != null) { args.push(endpointRequestSnippet); diff --git a/generators/csharp/sdk/src/endpoint/http/RawClient.ts b/generators/csharp/sdk/src/endpoint/http/RawClient.ts index 7df06671ed9f..f44acdbb750d 100644 --- a/generators/csharp/sdk/src/endpoint/http/RawClient.ts +++ b/generators/csharp/sdk/src/endpoint/http/RawClient.ts @@ -220,7 +220,9 @@ export class RawClient extends WithGeneration { propertyName = property.value.key.name.pascalCase.safeName; partName = property.value.key.wireValue; contentType = property.value.contentType; - csharpType = this.context.csharpTypeMapper.convertFromFileProperty({ property: property.value }); + csharpType = this.context.csharpTypeMapper.convertFromFileProperty({ + property: property.value + }); break; case "bodyProperty": { propertyName = property.name.name.pascalCase.safeName; diff --git a/generators/csharp/sdk/src/endpoint/request/ReferencedEndpointRequest.ts b/generators/csharp/sdk/src/endpoint/request/ReferencedEndpointRequest.ts index 91e36a3d27ad..757c531a2a30 100644 --- a/generators/csharp/sdk/src/endpoint/request/ReferencedEndpointRequest.ts +++ b/generators/csharp/sdk/src/endpoint/request/ReferencedEndpointRequest.ts @@ -23,7 +23,9 @@ export class ReferencedEndpointRequest extends EndpointRequest { } public getParameterType(): ast.Type { - return this.context.csharpTypeMapper.convert({ reference: this.requestBodyShape }); + return this.context.csharpTypeMapper.convert({ + reference: this.requestBodyShape + }); } public getQueryParameterCodeBlock(): QueryParameterCodeBlock | undefined { diff --git a/generators/csharp/sdk/src/endpoint/request/WrappedEndpointRequest.ts b/generators/csharp/sdk/src/endpoint/request/WrappedEndpointRequest.ts index 6611ac31c50a..bb156ce6acef 100644 --- a/generators/csharp/sdk/src/endpoint/request/WrappedEndpointRequest.ts +++ b/generators/csharp/sdk/src/endpoint/request/WrappedEndpointRequest.ts @@ -159,7 +159,12 @@ export class WrappedEndpointRequest extends EndpointRequest { const headerReference = `${this.getParameterName()}.${header.name.name.pascalCase.safeName}`; writer.controlFlow("if", this.csharp.codeblock(`${headerReference} != null`)); writer.write(`${this.names.variables.headers}["${header.name.wireValue}"] = `); - writer.writeNodeStatement(this.stringify({ reference: header.valueType, name: header.name.name })); + writer.writeNodeStatement( + this.stringify({ + reference: header.valueType, + name: header.name.name + }) + ); writer.endControlFlow(); } }), @@ -287,7 +292,9 @@ export class WrappedEndpointRequest extends EndpointRequest { return false; } case "primitive": { - const csharpType = this.context.csharpTypeMapper.convert({ reference: typeReference }); + const csharpType = this.context.csharpTypeMapper.convert({ + reference: typeReference + }); return is.Primitive.string(csharpType); } case "unknown": { @@ -303,7 +310,9 @@ export class WrappedEndpointRequest extends EndpointRequest { return true; } if (typeReference.container.type === "nullable") { - return this.isOptional({ typeReference: typeReference.container.nullable }); + return this.isOptional({ + typeReference: typeReference.container.nullable + }); } return false; case "named": { @@ -326,7 +335,9 @@ export class WrappedEndpointRequest extends EndpointRequest { switch (typeReference.type) { case "container": if (typeReference.container.type === "optional") { - return this.isNullable({ typeReference: typeReference.container.optional }); + return this.isNullable({ + typeReference: typeReference.container.optional + }); } if (typeReference.container.type === "nullable") { return true; @@ -438,13 +449,19 @@ export class WrappedEndpointRequest extends EndpointRequest { switch (typeReference.type) { case "container": if (typeReference.container.type === "optional") { - return this.isDateOrDateTime({ type, typeReference: typeReference.container.optional }); + return this.isDateOrDateTime({ + type, + typeReference: typeReference.container.optional + }); } return false; case "named": { const declaration = this.model.dereferenceType(typeReference.typeId).typeDeclaration; if (declaration.shape.type === "alias") { - return this.isDateOrDateTime({ type, typeReference: declaration.shape.aliasOf }); + return this.isDateOrDateTime({ + type, + typeReference: declaration.shape.aliasOf + }); } return false; } @@ -461,7 +478,9 @@ export class WrappedEndpointRequest extends EndpointRequest { switch (typeReference.type) { case "container": if (typeReference.container.type === "optional") { - return this.isEnum({ typeReference: typeReference.container.optional }); + return this.isEnum({ + typeReference: typeReference.container.optional + }); } return false; case "named": { diff --git a/generators/csharp/sdk/src/endpoint/snippets/EndpointSnippetsGenerator.ts b/generators/csharp/sdk/src/endpoint/snippets/EndpointSnippetsGenerator.ts index 712bee2fcce4..97d3458c1b5c 100644 --- a/generators/csharp/sdk/src/endpoint/snippets/EndpointSnippetsGenerator.ts +++ b/generators/csharp/sdk/src/endpoint/snippets/EndpointSnippetsGenerator.ts @@ -49,7 +49,11 @@ export class EndpointSnippetsGenerator extends WithGeneration { if (endpoint.pagination || isStreaming) { endpointIdsWithGenerator.push({ id: endpoint.id, isPager: true }); } - return endpointIdsWithGenerator.map(({ id }) => ({ id, endpoint, serviceId })); + return endpointIdsWithGenerator.map(({ id }) => ({ + id, + endpoint, + serviceId + })); }) ); diff --git a/generators/csharp/sdk/src/endpoint/utils/getEndpointReturnType.ts b/generators/csharp/sdk/src/endpoint/utils/getEndpointReturnType.ts index 36e460a5da2a..5bdbd7b683ec 100644 --- a/generators/csharp/sdk/src/endpoint/utils/getEndpointReturnType.ts +++ b/generators/csharp/sdk/src/endpoint/utils/getEndpointReturnType.ts @@ -38,7 +38,10 @@ export function getEndpointReturnType({ // (this requires that consumers use `for await()` to consume the result, regardless if they are streaming or not) streamParameter: (reference) => reference.streamResponse._visit(streamResultType), fileDownload: () => context.System.IO.Stream.asFullyQualified(), - json: (reference) => context.csharpTypeMapper.convert({ reference: reference.responseBodyType }), + json: (reference) => + context.csharpTypeMapper.convert({ + reference: reference.responseBodyType + }), text: () => context.generation.Primitive.string, bytes: () => undefined, _other: () => undefined diff --git a/generators/csharp/sdk/src/error/BaseApiExceptionGenerator.ts b/generators/csharp/sdk/src/error/BaseApiExceptionGenerator.ts index 393b790317f4..0cea3b7251d2 100644 --- a/generators/csharp/sdk/src/error/BaseApiExceptionGenerator.ts +++ b/generators/csharp/sdk/src/error/BaseApiExceptionGenerator.ts @@ -10,8 +10,14 @@ export class BaseApiExceptionGenerator extends FileGenerator { parentClassReference: this.Types.BaseException, primaryConstructor: { parameters: [ - this.csharp.parameter({ name: "message", type: this.Primitive.string }), - this.csharp.parameter({ name: "statusCode", type: this.Primitive.integer }), + this.csharp.parameter({ + name: "message", + type: this.Primitive.string + }), + this.csharp.parameter({ + name: "statusCode", + type: this.Primitive.integer + }), this.csharp.parameter({ name: "body", type: this.Primitive.object }) ], superClassArguments: [this.csharp.codeblock("message")] diff --git a/generators/csharp/sdk/src/error/BaseExceptionGenerator.ts b/generators/csharp/sdk/src/error/BaseExceptionGenerator.ts index 1c9bac915a59..8cfe3e8da4ee 100644 --- a/generators/csharp/sdk/src/error/BaseExceptionGenerator.ts +++ b/generators/csharp/sdk/src/error/BaseExceptionGenerator.ts @@ -10,7 +10,10 @@ export class BaseExceptionGenerator extends FileGenerator { parentClassReference: this.System.Exception, primaryConstructor: { parameters: [ - this.csharp.parameter({ name: "message", type: this.Primitive.string }), + this.csharp.parameter({ + name: "message", + type: this.Primitive.string + }), this.csharp.parameter({ name: "innerException", type: this.System.Exception.asOptional(), diff --git a/generators/csharp/sdk/src/error/CustomExceptionInterceptorGenerator.ts b/generators/csharp/sdk/src/error/CustomExceptionInterceptorGenerator.ts index 87cf5d169bab..8933e2b89ae7 100644 --- a/generators/csharp/sdk/src/error/CustomExceptionInterceptorGenerator.ts +++ b/generators/csharp/sdk/src/error/CustomExceptionInterceptorGenerator.ts @@ -14,7 +14,12 @@ export class CustomExceptionInterceptorGenerator extends FileGenerator { writer.writeLine("// TODO: Implement your exception capturing logic here."); diff --git a/generators/csharp/sdk/src/error/ErrorGenerator.ts b/generators/csharp/sdk/src/error/ErrorGenerator.ts index 36ccf0dd4a5c..b84e05372a4d 100644 --- a/generators/csharp/sdk/src/error/ErrorGenerator.ts +++ b/generators/csharp/sdk/src/error/ErrorGenerator.ts @@ -20,7 +20,9 @@ export class ErrorGenerator extends FileGenerator { + private classReference: ast.ClassReference; + private scheme: InferredAuthScheme; + private tokenEndpointHttpService: HttpService; + private tokenEndpointReference: EndpointReference; + private tokenEndpoint: HttpEndpoint; + private cls: ast.Class; + private clientField: ast.Field; + private bufferInMinutesField: ast.Field | undefined; + private cachedHeadersField: ast.Field | undefined; + private expiresAtField: ast.Field | undefined; + private lockField: ast.Field | undefined; + private expiryProperty: ResponseProperty | undefined; + private requestType: ast.ClassReference; + private credentialFields = new Map(); + + constructor({ context, scheme }: InferredAuthTokenProviderGenerator.Args) { + super(context); + this.scheme = scheme; + this.classReference = this.Types.InferredAuthTokenProvider; + this.tokenEndpointReference = this.scheme.tokenEndpoint.endpoint; + this.tokenEndpointHttpService = + this.context.getHttpService(this.tokenEndpointReference.serviceId) ?? + fail(`Service with id ${this.tokenEndpointReference.serviceId} not found`); + this.expiryProperty = this.scheme.tokenEndpoint.expiryProperty; + + this.tokenEndpoint = this.context.resolveEndpoint( + this.tokenEndpointHttpService, + this.tokenEndpointReference.endpointId + ); + + this.requestType = this.getRequestType(); + + this.cls = this.csharp.class_({ + reference: this.classReference, + partial: true, + access: ast.Access.Internal + }); + + this.clientField = this.cls.addField({ + origin: this.cls.explicit("_client"), + access: ast.Access.Private, + type: this.context.getSubpackageClassReferenceForServiceId(this.tokenEndpointReference.serviceId) + }); + + if (this.expiryProperty != null) { + this.bufferInMinutesField = this.cls.addField({ + origin: this.cls.explicit("BufferInMinutes"), + access: ast.Access.Private, + const_: true, + type: this.Primitive.double, + initializer: this.csharp.codeblock("2") + }); + + this.cachedHeadersField = this.cls.addField({ + origin: this.cls.explicit("_cachedHeaders"), + access: ast.Access.Private, + type: this.Collection.idictionary(this.Primitive.string, this.Primitive.string).asOptional() + }); + + this.expiresAtField = this.cls.addField({ + origin: this.cls.explicit("_expiresAt"), + access: ast.Access.Private, + type: this.Value.dateTime.asOptional() + }); + + this.lockField = this.cls.addField({ + origin: this.cls.explicit("_lock"), + access: ast.Access.Private, + readonly: true, + type: this.csharp.classReference({ + name: "SemaphoreSlim", + namespace: "System.Threading" + }), + initializer: this.csharp.codeblock("new SemaphoreSlim(1, 1)") + }); + } else { + this.expiresAtField = undefined; + } + + this.collectCredentialProperties(); + + const ctor = this.cls.addConstructor({ access: ast.Access.Internal }); + + for (const [parameter, { field }] of this.credentialFields.entries()) { + ctor.body.assign(field, ctor.addParameter({ name: parameter, type: field.type })); + } + + ctor.body.assign(this.clientField, ctor.addParameter({ name: "client", type: this.clientField.type })); + + this.cls.addMethod({ + access: ast.Access.Internal, + isAsync: true, + name: this.names.methods.getAuthHeadersAsync, + body: this.getAuthHeadersBody(), + return_: this.Collection.idictionary(this.Primitive.string, this.Primitive.string) + }); + } + + public doGenerate(): CSharpFile { + return new CSharpFile({ + clazz: this.cls, + directory: this.context.getCoreDirectory(), + allNamespaceSegments: this.context.getAllNamespaceSegments(), + allTypeClassReferences: this.context.getAllTypeClassReferences(), + namespace: this.namespaces.root, + generation: this.generation + }); + } + + protected getFilepath(): RelativeFilePath { + return join(this.context.getCoreDirectory(), RelativeFilePath.of(`${this.classReference.name}.cs`)); + } + + private collectCredentialProperties(): void { + const credentials = collectInferredAuthCredentials(this.context, this.tokenEndpoint); + + for (const credential of credentials) { + const typeRef = this.context.csharpTypeMapper.convert({ + reference: credential.typeReference + }); + + const field = this.cls.addField({ + origin: this.cls.explicit(this.format.private(credential.camelName)), + access: ast.Access.Private, + type: typeRef + }); + + this.credentialFields.set(credential.camelName, { + field, + pascalName: credential.pascalName, + isOptional: credential.isOptional + }); + } + + if (this.credentialFields.size === 0) { + this.context.logger.warn( + `Inferred auth token endpoint has no credential parameters (headers or body properties). ` + + `The token provider will be created but may not work as expected.` + ); + } + } + + private getAuthHeadersBody(): ast.CodeBlock { + const authenticatedHeaders = this.scheme.tokenEndpoint.authenticatedRequestHeaders; + + if (this.expiryProperty == null) { + return this.csharp.codeblock((writer) => { + writer.writeNodeStatement( + this.csharp.codeblock((writer) => { + writer.write("var tokenResponse = "); + writer.writeNode( + this.csharp.invokeMethod({ + async: true, + method: `${this.clientField.name}.${this.context.getEndpointMethodName(this.tokenEndpoint)}`, + arguments_: [ + this.csharp.instantiateClass({ + classReference: this.requestType, + arguments_: this.buildRequestArguments() + }) + ] + }) + ); + }) + ); + + writer.writeTextStatement("var headers = new Dictionary()"); + + for (const authHeader of authenticatedHeaders) { + const headerName = authHeader.headerName; + const valuePrefix = authHeader.valuePrefix ?? ""; + const accessorChain = this.buildResponsePropertyAccessor( + "tokenResponse", + authHeader.responseProperty + ); + + if (valuePrefix !== "") { + writer.writeTextStatement(`headers["${headerName}"] = $"${valuePrefix}{${accessorChain}}"`); + } else { + writer.writeTextStatement(`headers["${headerName}"] = ${accessorChain}`); + } + } + + writer.writeTextStatement("return headers"); + }); + } + + if ( + this.cachedHeadersField == null || + this.expiresAtField == null || + this.lockField == null || + this.bufferInMinutesField == null || + this.expiryProperty == null + ) { + throw new Error("Caching fields should be defined when expiryProperty is not null"); + } + + const cachedHeadersField = this.cachedHeadersField; + const expiresAtField = this.expiresAtField; + const lockField = this.lockField; + const bufferInMinutesField = this.bufferInMinutesField; + const expiryProperty = this.expiryProperty; + + return this.csharp.codeblock((writer) => { + writer.controlFlow( + "if", + this.csharp.codeblock((writer) => { + writer.write(`${cachedHeadersField.name} == null`); + writer.write(` || DateTime.UtcNow >= ${expiresAtField.name}`); + }) + ); + + writer.writeTextStatement(`await ${lockField.name}.WaitAsync().ConfigureAwait(false)`); + writer.writeLine("try"); + writer.pushScope(); + + writer.controlFlow( + "if", + this.csharp.codeblock((writer) => { + writer.write(`${cachedHeadersField.name} == null`); + writer.write(` || DateTime.UtcNow >= ${expiresAtField.name}`); + }) + ); + + writer.writeLine("try"); + writer.pushScope(); + + writer.writeNodeStatement( + this.csharp.codeblock((writer) => { + writer.write("var tokenResponse = "); + writer.writeNode( + this.csharp.invokeMethod({ + async: true, + method: `${this.clientField.name}.${this.context.getEndpointMethodName(this.tokenEndpoint)}`, + arguments_: [ + this.csharp.instantiateClass({ + classReference: this.requestType, + arguments_: this.buildRequestArguments() + }) + ] + }) + ); + }) + ); + + writer.writeTextStatement(`${cachedHeadersField.name} = new Dictionary()`); + + for (const authHeader of authenticatedHeaders) { + const headerName = authHeader.headerName; + const valuePrefix = authHeader.valuePrefix ?? ""; + const accessorChain = this.buildResponsePropertyAccessor("tokenResponse", authHeader.responseProperty); + + if (valuePrefix !== "") { + writer.writeTextStatement( + `${cachedHeadersField.name}["${headerName}"] = $"${valuePrefix}{${accessorChain}}"` + ); + } else { + writer.writeTextStatement(`${cachedHeadersField.name}["${headerName}"] = ${accessorChain}`); + } + } + + const expiryAccessor = this.buildResponsePropertyAccessor("tokenResponse", expiryProperty); + writer.writeTextStatement( + `${expiresAtField.name} = DateTime.UtcNow.AddSeconds(${expiryAccessor}).AddMinutes(-${bufferInMinutesField.name})` + ); + + writer.popScope(); + writer.writeLine("catch"); + writer.pushScope(); + writer.writeTextStatement(`${cachedHeadersField.name} = null`); + writer.writeTextStatement(`${expiresAtField.name} = null`); + writer.writeTextStatement("throw"); + writer.popScope(); + + writer.endControlFlow(); + + writer.popScope(); + writer.writeLine("finally"); + writer.pushScope(); + writer.writeTextStatement(`${lockField.name}.Release()`); + writer.popScope(); + + writer.endControlFlow(); + + writer.writeTextStatement(`return ${cachedHeadersField.name}`); + }); + } + + private buildRequestArguments(): { + name: string; + assignment: ast.CodeBlock; + }[] { + const arguments_: { name: string; assignment: ast.CodeBlock }[] = []; + + for (const { field, pascalName } of this.credentialFields.values()) { + arguments_.push({ + name: pascalName, + assignment: this.csharp.codeblock(field.name) + }); + } + + return arguments_; + } + + private buildResponsePropertyAccessor(responseVar: string, responseProperty: ResponseProperty): string { + let accessor = responseVar; + + if (responseProperty.propertyPath != null && responseProperty.propertyPath.length > 0) { + for (const pathElement of responseProperty.propertyPath) { + accessor += `.${pathElement.name.pascalCase.safeName}`; + } + } + + accessor += `.${responseProperty.property.name.name.pascalCase.safeName}`; + + return accessor; + } + + private getRequestType(): ast.ClassReference { + const requestWrapper = this.requestWrapperName(); + if (requestWrapper) { + return this.context.getRequestWrapperReference(this.tokenEndpointReference.serviceId, requestWrapper); + } + const reference = this.requestTypeReference(); + if (reference) { + const typeRef = this.context.csharpTypeMapper.convert({ reference }); + if (is.ClassReference(typeRef)) { + return typeRef; + } + throw new Error( + `Failed to get request class reference for inferred auth token endpoint. ` + + `Expected ClassReference but got ${typeRef.constructor.name}. ` + + `Service ID: ${this.tokenEndpointReference.serviceId}, ` + + `Endpoint ID: ${this.tokenEndpointReference.endpointId}` + ); + } + throw new Error( + `Failed to get request type reference for inferred auth token endpoint. ` + + `Service ID: ${this.tokenEndpointReference.serviceId}, ` + + `Endpoint ID: ${this.tokenEndpointReference.endpointId}. ` + + `The token endpoint must have either an SDK request wrapper or a request body.` + ); + } + + private requestWrapperName() { + return ( + this.tokenEndpoint.sdkRequest?.shape._visit({ + _other: () => undefined, + justRequestBody: () => undefined, + wrapper: (value) => value.wrapperName + }) ?? + this.tokenEndpoint.requestBody?._visit({ + _other: () => undefined, + reference: () => undefined, + fileUpload: () => undefined, + bytes: () => undefined, + inlinedRequestBody: (value) => value.name + }) + ); + } + + private requestTypeReference() { + return ( + this.tokenEndpoint.sdkRequest?.shape._visit({ + _other: () => undefined, + wrapper: () => undefined, + justRequestBody: (value) => + value._visit({ + typeReference: (value) => value.requestBodyType, + bytes: () => undefined, + _other: () => undefined + }) + }) ?? + this.tokenEndpoint.requestBody?._visit({ + _other: () => undefined, + inlinedRequestBody: () => undefined, + fileUpload: () => undefined, + bytes: () => undefined, + reference: (value) => value.requestBodyType + }) + ); + } +} diff --git a/generators/csharp/sdk/src/oauth/OauthTokenProviderGenerator.ts b/generators/csharp/sdk/src/oauth/OauthTokenProviderGenerator.ts index df69bb8b4f91..ecc70ba8d1c4 100644 --- a/generators/csharp/sdk/src/oauth/OauthTokenProviderGenerator.ts +++ b/generators/csharp/sdk/src/oauth/OauthTokenProviderGenerator.ts @@ -105,7 +105,9 @@ export class OauthTokenProviderGenerator extends FileGenerator writer.writeLine(); writer.pushScope(); for (const header of this.context.getIdempotencyHeaders()) { - const type = this.context.csharpTypeMapper.convert({ reference: header.valueType }); + const type = this.context.csharpTypeMapper.convert({ + reference: header.valueType + }); const isString = is.Primitive.string(type.asNonOptional()); const toString = isString ? "" : ".ToString()"; // In header values, we only accept simple types, so we can assume that none are nullable (apart from string), diff --git a/generators/csharp/sdk/src/reference/buildReference.ts b/generators/csharp/sdk/src/reference/buildReference.ts index ac16526c54c0..85d86b131408 100644 --- a/generators/csharp/sdk/src/reference/buildReference.ts +++ b/generators/csharp/sdk/src/reference/buildReference.ts @@ -15,7 +15,11 @@ export function buildReference({ context }: { context: SdkGeneratorContext }): R const section = isRootServiceId({ context, serviceId }) ? builder.addRootSection() : builder.addSection({ title: getSectionTitle({ service }) }); - const endpoints = getEndpointReferencesForService({ context, serviceId, service }); + const endpoints = getEndpointReferencesForService({ + context, + serviceId, + service + }); for (const endpoint of endpoints) { hasEndpoints = true; section.addEndpoint(endpoint); @@ -98,7 +102,10 @@ function getEndpointReference({ } }, { - text: getReferenceEndpointInvocationParameters({ context, endpointSignatureInfo }) + text: getReferenceEndpointInvocationParameters({ + context, + endpointSignatureInfo + }) } ], returnValue: diff --git a/generators/csharp/sdk/src/root-client/RootClientGenerator.ts b/generators/csharp/sdk/src/root-client/RootClientGenerator.ts index dc769dc6d185..0214e4c20c6b 100644 --- a/generators/csharp/sdk/src/root-client/RootClientGenerator.ts +++ b/generators/csharp/sdk/src/root-client/RootClientGenerator.ts @@ -6,6 +6,7 @@ import { join, RelativeFilePath } from "@fern-api/fs-utils"; import { AuthScheme, HttpHeader, + InferredAuthScheme, Literal, OAuthScheme, PrimitiveTypeV1, @@ -16,6 +17,7 @@ import { } from "@fern-fern/ir-sdk/api"; import { RawClient } from "../endpoint/http/RawClient"; import { SdkGeneratorContext } from "../SdkGeneratorContext"; +import { collectInferredAuthCredentials } from "../utils/inferredAuthUtils"; import { WebSocketClientGenerator } from "../websocket/WebsocketClientGenerator"; const GetFromEnvironmentOrThrow = "GetFromEnvironmentOrThrow"; @@ -49,10 +51,12 @@ export class RootClientGenerator extends FileGenerator { + writer.write( + `clientOptions.Headers["${headerName}"] = new Func>(async () => (await inferredAuthProvider.${this.names.methods.getAuthHeadersAsync}().ConfigureAwait(false)).First().Value);` + ); + }) + ); + } + } + innerWriter.writeLine(`${this.members.clientName} = `); innerWriter.writeNodeStatement( this.csharp.instantiateClass({ @@ -382,7 +429,6 @@ export class RootClientGenerator extends FileGenerator credential.camelName); + } } diff --git a/generators/csharp/sdk/src/test-generation/mock-server/BaseMockServerTestGenerator.ts b/generators/csharp/sdk/src/test-generation/mock-server/BaseMockServerTestGenerator.ts index 5662b1fe3257..5e868c221a9f 100644 --- a/generators/csharp/sdk/src/test-generation/mock-server/BaseMockServerTestGenerator.ts +++ b/generators/csharp/sdk/src/test-generation/mock-server/BaseMockServerTestGenerator.ts @@ -2,7 +2,7 @@ import { NamedArgument } from "@fern-api/base-generator"; import { CSharpFile, FileGenerator } from "@fern-api/csharp-base"; import { ast, Writer } from "@fern-api/csharp-codegen"; import { join, RelativeFilePath } from "@fern-api/fs-utils"; -import { ExampleEndpointCall, Name, OAuthScheme } from "@fern-fern/ir-sdk/api"; +import { ExampleEndpointCall, InferredAuthScheme, Name, OAuthScheme } from "@fern-fern/ir-sdk/api"; import { fail } from "assert"; import { MultiUrlEnvironmentGenerator } from "../../environment/MultiUrlEnvironmentGenerator"; import { RootClientGenerator } from "../../root-client/RootClientGenerator"; @@ -77,6 +77,7 @@ export class BaseMockServerTestGenerator extends FileGenerator { throw new Error("Internal error; Unexpected environment type"); } - }) ?? { name: "BaseUrl", assignment: this.csharp.codeblock("Server.Urls[0]") }, + }) ?? { + name: "BaseUrl", + assignment: this.csharp.codeblock("Server.Urls[0]") + }, { name: "MaxRetries", assignment: this.csharp.codeblock("0") } ] }) @@ -135,6 +139,14 @@ export class BaseMockServerTestGenerator extends FileGenerator { + // token endpoint + const tokenEndpointReference = scheme.tokenEndpoint.endpoint; + const tokenEndpointHttpService = + this.context.getHttpService(tokenEndpointReference.serviceId) ?? + fail(`Service with id ${tokenEndpointReference.serviceId} not found`); + const tokenHttpEndpoint = this.context.resolveEndpoint( + tokenEndpointHttpService, + tokenEndpointReference.endpointId + ); + const tokenAllExamples = [ + ...tokenHttpEndpoint.autogeneratedExamples, + ...tokenHttpEndpoint.userSpecifiedExamples + ].map((example) => example.example); + // TODO: support other response body types + const tokenUseableExamples = tokenAllExamples.filter((example): example is ExampleEndpointCall => { + const response = example?.response; + return response?.type === "ok" && response.value.type === "body"; + }); + + // Update example values to match the placeholder values used in client instantiation + tokenUseableExamples.forEach((example) => { + // Update header values in the example + for (const exampleHeader of [ + ...(example.serviceHeaders ?? []), + ...(example.endpointHeaders ?? []) + ]) { + const matchingHeader = tokenHttpEndpoint.headers.find( + (h) => h.name.wireValue === exampleHeader.name.wireValue + ); + if ( + matchingHeader && + !( + matchingHeader.valueType.type === "container" && + matchingHeader.valueType.container.type === "literal" + ) + ) { + // Update the example header value to match what the client sends (wireValue) + exampleHeader.value = { + ...exampleHeader.value, + jsonExample: matchingHeader.name.wireValue + }; + } + } + + // Update body property values in the JSON example + const jsonExample = example.request?.jsonExample as Record | undefined; + if (jsonExample && tokenHttpEndpoint.requestBody?.type === "inlinedRequestBody") { + for (const prop of tokenHttpEndpoint.requestBody.properties) { + if (prop.valueType.type === "container" && prop.valueType.container.type === "literal") { + continue; + } + deepSetProperty(jsonExample, [], prop.name.name, prop.name.wireValue); + } + } + }); + + writer.writeNode( + this.mockEndpointGenerator.generateForExamples(tokenHttpEndpoint, tokenUseableExamples) + ); + }) + }); + } + protected generateMockAuthMethod(scheme: OAuthScheme, cls: ast.Class) { const shouldScope = !!scheme.configuration.refreshEndpoint; cls.addMethod({ diff --git a/generators/csharp/sdk/src/utils/inferredAuthUtils.ts b/generators/csharp/sdk/src/utils/inferredAuthUtils.ts new file mode 100644 index 000000000000..7cc2452275d7 --- /dev/null +++ b/generators/csharp/sdk/src/utils/inferredAuthUtils.ts @@ -0,0 +1,99 @@ +import { HttpEndpoint, TypeReference } from "@fern-fern/ir-sdk/api"; +import { SdkGeneratorContext } from "../SdkGeneratorContext"; +import { getRequestBodyProperties } from "./requestBodyUtils"; + +export interface InferredAuthCredential { + /** + * The field name in camelCase (used for private fields and constructor parameters) + */ + camelName: string; + /** + * The property name in PascalCase (used for request object initializers) + */ + pascalName: string; + /** + * The wire name (used for examples and mock server expectations) + */ + wireValue: string; + /** + * The type reference from the IR + */ + typeReference: TypeReference; + /** + * Optional documentation + */ + docs?: string; + /** + * Whether this credential comes from a header (true) or body property (false) + */ + isFromHeader: boolean; + /** + * Whether the field is optional + */ + isOptional: boolean; +} + +/** + * Collects all credential parameters from a token endpoint's headers and body. + * This includes both required and optional parameters, but excludes literal types. + * + * @param context - The SDK generator context + * @param tokenEndpoint - The token endpoint to extract credentials from + * @returns Array of credential information, with headers first, then body properties + */ +export function collectInferredAuthCredentials( + context: SdkGeneratorContext, + tokenEndpoint: HttpEndpoint +): InferredAuthCredential[] { + const credentials: InferredAuthCredential[] = []; + const seenNames = new Set(); + + for (const header of tokenEndpoint.headers) { + if (header.valueType.type === "container" && header.valueType.container.type === "literal") { + continue; + } + + const camelName = header.name.name.camelCase.unsafeName; + const typeRef = context.csharpTypeMapper.convert({ + reference: header.valueType + }); + + credentials.push({ + camelName, + pascalName: header.name.name.pascalCase.safeName, + wireValue: header.name.wireValue, + typeReference: header.valueType, + docs: header.docs, + isFromHeader: true, + isOptional: typeRef.isOptional + }); + + seenNames.add(camelName); + } + + for (const prop of getRequestBodyProperties(context, tokenEndpoint.requestBody)) { + const camelName = prop.name.name.camelCase.unsafeName; + + if (seenNames.has(camelName)) { + continue; + } + + const typeRef = context.csharpTypeMapper.convert({ + reference: prop.valueType + }); + + credentials.push({ + camelName, + pascalName: prop.name.name.pascalCase.safeName, + wireValue: prop.name.wireValue, + typeReference: prop.valueType, + docs: prop.docs, + isFromHeader: false, + isOptional: typeRef.isOptional + }); + + seenNames.add(camelName); + } + + return credentials; +} diff --git a/generators/csharp/sdk/src/utils/requestBodyUtils.ts b/generators/csharp/sdk/src/utils/requestBodyUtils.ts new file mode 100644 index 000000000000..a07962666c2e --- /dev/null +++ b/generators/csharp/sdk/src/utils/requestBodyUtils.ts @@ -0,0 +1,70 @@ +import { HttpRequestBody, TypeReference } from "@fern-fern/ir-sdk/api"; +import { SdkGeneratorContext } from "../SdkGeneratorContext"; + +export interface RequestBodyProperty { + name: { + wireValue: string; + name: { + camelCase: { unsafeName: string }; + pascalCase: { safeName: string }; + }; + }; + valueType: TypeReference; + docs?: string; +} + +/** + * Extracts non-literal properties from a request body (handles both inline and referenced types). + * Returns an array of properties that can be used as credentials or parameters. + */ +export function getRequestBodyProperties( + context: SdkGeneratorContext, + requestBody: HttpRequestBody | undefined +): RequestBodyProperty[] { + if (!requestBody) { + return []; + } + + const properties: RequestBodyProperty[] = []; + + requestBody._visit({ + inlinedRequestBody: (inlinedRequestBody) => { + for (const prop of inlinedRequestBody.properties) { + // Skip literal types - they are hardcoded in the request + if (prop.valueType.type === "container" && prop.valueType.container.type === "literal") { + continue; + } + properties.push(prop); + } + }, + reference: (reference) => { + // Handle referenced request body types by resolving the type and extracting properties + if (reference.requestBodyType.type === "named") { + const { typeDeclaration } = context.model.dereferenceType(reference.requestBodyType.typeId); + + if (typeDeclaration.shape.type === "object") { + const objectShape = typeDeclaration.shape; + // Process all properties from the referenced object type + for (const prop of objectShape.properties) { + // Skip literal types - they are hardcoded in the request + if (prop.valueType.type === "container" && prop.valueType.container.type === "literal") { + continue; + } + properties.push(prop); + } + } + } + }, + fileUpload: () => { + // File uploads are not supported for token endpoints + }, + bytes: () => { + // Bytes are not supported for token endpoints + }, + _other: () => { + // Other request body types are not supported + } + }); + + return properties; +} diff --git a/generators/csharp/sdk/src/websocket/WebsocketClientGenerator.ts b/generators/csharp/sdk/src/websocket/WebsocketClientGenerator.ts index b8dcf8b87668..aa505b16dc7f 100644 --- a/generators/csharp/sdk/src/websocket/WebsocketClientGenerator.ts +++ b/generators/csharp/sdk/src/websocket/WebsocketClientGenerator.ts @@ -206,7 +206,11 @@ export class WebSocketClientGenerator extends WithGeneration { /** The default environment URL for WebSocket connections */ private defaultEnvironment: string | undefined; /** Array of available environments with their URLs and names */ - private environments: Array<{ environment: string; name: string; url: string }> = []; + private environments: Array<{ + environment: string; + name: string; + url: string; + }> = []; /** * Determines if multiple environments are available. @@ -236,7 +240,9 @@ export class WebSocketClientGenerator extends WithGeneration { const environmentsClass = this.csharp.class_({ origin: this.classReference.explicit("Environments"), static_: true, - doc: this.csharp.xmlDocBlockOf({ summary: "Selectable endpoint URLs for the API client" }), + doc: this.csharp.xmlDocBlockOf({ + summary: "Selectable endpoint URLs for the API client" + }), namespace: this.classReference.namespace, enclosingType: this.classReference, access: ast.Access.Public @@ -245,7 +251,12 @@ export class WebSocketClientGenerator extends WithGeneration { access: ast.Access.Internal, type: ast.MethodType.STATIC, name: "getBaseUrl", - parameters: [this.csharp.parameter({ name: "environment", type: this.Primitive.string })], + parameters: [ + this.csharp.parameter({ + name: "environment", + type: this.Primitive.string + }) + ], return_: this.Primitive.string, body: this.csharp.codeblock((writer) => { writer.write("switch(environment) {"); @@ -353,7 +364,9 @@ export class WebSocketClientGenerator extends WithGeneration { for (const queryParameter of this.websocketChannel.queryParameters) { // add to the options class - const type = this.context.csharpTypeMapper.convert({ reference: queryParameter.valueType }); + const type = this.context.csharpTypeMapper.convert({ + reference: queryParameter.valueType + }); optionsClass.addField({ origin: queryParameter, @@ -367,7 +380,9 @@ export class WebSocketClientGenerator extends WithGeneration { } for (const pathParameter of this.websocketChannel.pathParameters) { - const type = this.context.csharpTypeMapper.convert({ reference: pathParameter.valueType }); + const type = this.context.csharpTypeMapper.convert({ + reference: pathParameter.valueType + }); optionsClass.addField({ origin: pathParameter, access: ast.Access.Public, @@ -443,7 +458,9 @@ export class WebSocketClientGenerator extends WithGeneration { cls.addField({ origin: pathParameter, access: ast.Access.Public, - type: this.context.csharpTypeMapper.convert({ reference: pathParameter.valueType }), + type: this.context.csharpTypeMapper.convert({ + reference: pathParameter.valueType + }), summary: pathParameter.docs ?? "", accessors: { get: (writer) => { @@ -462,7 +479,9 @@ export class WebSocketClientGenerator extends WithGeneration { cls.addField({ origin: queryParameter, access: ast.Access.Public, - type: this.context.csharpTypeMapper.convert({ reference: queryParameter.valueType }), + type: this.context.csharpTypeMapper.convert({ + reference: queryParameter.valueType + }), summary: queryParameter.docs ?? "", accessors: { get: (writer) => { @@ -644,10 +663,14 @@ export class WebSocketClientGenerator extends WithGeneration { for (const each of this.websocketChannel.messages) { if (each.origin === "server" && each.body.type === "reference") { const reference = each.body.bodyType; - const type = this.context.csharpTypeMapper.convert({ reference: each.body.bodyType }); + const type = this.context.csharpTypeMapper.convert({ + reference: each.body.bodyType + }); if (each.body.type === "reference") { const reference = each.body.bodyType; - const type = this.context.csharpTypeMapper.convert({ reference: each.body.bodyType }); + const type = this.context.csharpTypeMapper.convert({ + reference: each.body.bodyType + }); // if the result is a oneof, we will expand it into multiple if (is.OneOf.OneOf(type)) { @@ -690,7 +713,9 @@ export class WebSocketClientGenerator extends WithGeneration { .map((each) => { if (each.body.type === "reference") { const bodyType = each.body.bodyType; - let type = this.context.csharpTypeMapper.convert({ reference: each.body.bodyType }); + let type = this.context.csharpTypeMapper.convert({ + reference: each.body.bodyType + }); // if the body type is just a string, this is probably a binary message... if (bodyType.type === "primitive" && bodyType.primitive.v2?.type === "string") { @@ -732,7 +757,9 @@ export class WebSocketClientGenerator extends WithGeneration { override: true, isAsync: true, name: "OnTextMessage", - doc: this.csharp.xmlDocBlockOf({ summary: "Dispatches incoming WebSocket messages" }), + doc: this.csharp.xmlDocBlockOf({ + summary: "Dispatches incoming WebSocket messages" + }), parameters: [ this.csharp.parameter({ name: "stream", @@ -800,7 +827,9 @@ export class WebSocketClientGenerator extends WithGeneration { access: ast.Access.Protected, override: true, name: "DisposeEvents", - doc: this.csharp.xmlDocBlockOf({ summary: "Disposes of event subscriptions" }), + doc: this.csharp.xmlDocBlockOf({ + summary: "Disposes of event subscriptions" + }), parameters: [], body: this.csharp.codeblock((writer) => { // @@ -833,7 +862,9 @@ export class WebSocketClientGenerator extends WithGeneration { type: each.type }) ], - doc: this.csharp.xmlDocBlockOf({ summary: `Sends a ${each.name} message to the server` }), + doc: this.csharp.xmlDocBlockOf({ + summary: `Sends a ${each.name} message to the server` + }), body: this.csharp.codeblock((writer) => { writer.writeLine(`await SendInstant(`); diff --git a/generators/csharp/sdk/src/wrapped-request/WrappedRequestGenerator.ts b/generators/csharp/sdk/src/wrapped-request/WrappedRequestGenerator.ts index 1a46dbd9a577..6b9fd394b8b6 100644 --- a/generators/csharp/sdk/src/wrapped-request/WrappedRequestGenerator.ts +++ b/generators/csharp/sdk/src/wrapped-request/WrappedRequestGenerator.ts @@ -55,13 +55,23 @@ export class WrappedRequestGenerator extends FileGenerator { - const type = this.context.csharpTypeMapper.convert({ reference: reference.requestBodyType }); + const type = this.context.csharpTypeMapper.convert({ + reference: reference.requestBodyType + }); const useRequired = !type.isOptional; class_.addField({ origin: this.wrapper.bodyKey, @@ -216,7 +230,12 @@ export class WrappedRequestGenerator extends FileGenerator & { method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH"; }; -export type Auth = dynamic.Auth.Basic | dynamic.Auth.Bearer | dynamic.Auth.Header | dynamic.Auth.Oauth; +export type Auth = + | dynamic.Auth.Basic + | dynamic.Auth.Bearer + | dynamic.Auth.Header + | dynamic.Auth.Oauth + | dynamic.Auth.Inferred; export type AuthValues = | dynamic.AuthValues.Basic | dynamic.AuthValues.Bearer | dynamic.AuthValues.Header - | dynamic.AuthValues.Oauth; + | dynamic.AuthValues.Oauth + | dynamic.AuthValues.Inferred; export function convertEndpoints(endpoints: Record): Record { return Object.fromEntries( @@ -74,10 +80,7 @@ function convertExampleAuth(auth: dynamic.AuthValues | undefined): AuthValues | if (auth == null) { return undefined; } - if (auth.type === "inferred") { - return undefined; - } - + // Keep inferred auth - don't strip it out return auth as AuthValues; } @@ -85,9 +88,6 @@ function convertEndpointAuth(auth: dynamic.Auth | undefined): Auth | undefined { if (auth == null) { return undefined; } - if (auth.type === "inferred") { - return undefined; - } - + // Keep inferred auth - don't strip it out return auth as Auth; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 42cecdc6a14c..2a8b4aa87b2f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -157,8 +157,8 @@ importers: specifier: workspace:* version: link:../../packages/commons/core-utils '@fern-api/dynamic-ir-sdk': - specifier: ^61.7.0 - version: 61.7.0 + specifier: ^62.3.0 + version: 62.3.0 '@fern-api/fern-definition-schema': specifier: workspace:* version: link:../../packages/cli/fern-definition/schema @@ -209,8 +209,8 @@ importers: specifier: workspace:* version: link:../../../packages/commons/logging-execa '@fern-fern/ir-sdk': - specifier: ^61.7.0 - version: 61.7.0 + specifier: ^62.3.0 + version: 62.3.0 '@types/lodash-es': specifier: ^4.17.12 version: 4.17.12 @@ -239,11 +239,11 @@ importers: specifier: workspace:* version: link:../../../packages/commons/core-utils '@fern-api/dynamic-ir-sdk': - specifier: ^61.7.0 - version: 61.7.0 + specifier: ^62.3.0 + version: 62.3.0 '@fern-fern/ir-sdk': - specifier: ^61.7.0 - version: 61.7.0 + specifier: ^62.3.0 + version: 62.3.0 zod: specifier: ^3.22.3 version: 3.25.76 @@ -279,8 +279,8 @@ importers: specifier: workspace:* version: link:../codegen '@fern-api/dynamic-ir-sdk': - specifier: 61.4.0 - version: 61.4.0 + specifier: ^62.3.0 + version: 62.3.0 '@fern-api/path-utils': specifier: workspace:* version: link:../../../packages/commons/path-utils @@ -355,8 +355,8 @@ importers: specifier: workspace:* version: link:../../../packages/commons/fs-utils '@fern-fern/ir-sdk': - specifier: ^61.7.0 - version: 61.7.0 + specifier: ^62.3.0 + version: 62.3.0 '@types/node': specifier: 18.15.3 version: 18.15.3 @@ -406,8 +406,8 @@ importers: specifier: ^0.0.1167 version: 0.0.1167 '@fern-fern/ir-sdk': - specifier: ^61.7.0 - version: 61.7.0 + specifier: ^62.3.0 + version: 62.3.0 '@types/node': specifier: 18.15.3 version: 18.15.3 @@ -9319,15 +9319,15 @@ packages: '@fern-api/dynamic-ir-sdk@59.6.1': resolution: {integrity: sha512-GNo6cLnpaJjwY041x2clzRaUqsPKcQkpW9TsLYVptj2UniSFS7dtMcoPpRb/zFMmBO/SjFnOs/2OQ8AHYp58NA==} - '@fern-api/dynamic-ir-sdk@61.4.0': - resolution: {integrity: sha512-pJ8oswOmhVBi7I7OnFXndb2TqXMKweQby1/YmbJi8Usp/c+mFwEvnCYfvTvj6an34mG1k7HiS4ACZHtDp/SMJw==} - '@fern-api/dynamic-ir-sdk@61.7.0': resolution: {integrity: sha512-3BcgNnTgr3qucG2AYr1hCOjVMjM5VLZZIkSTgOlVjwASTvH5eqxApfYasUNLAAA9xXU7anfCqtnd7IOE2G2jTw==} '@fern-api/dynamic-ir-sdk@62.1.0': resolution: {integrity: sha512-3GrWtmxRpgStXUyejFxa+yA5kIm5NS5FsxmKT3sLQN52OKGd9phskyPk8GiiPmIeKdeGIHT8HIwKz003ksxZSg==} + '@fern-api/dynamic-ir-sdk@62.3.0': + resolution: {integrity: sha512-DMmK1xgOuLsrRk9MTvFuMU7dQVb2MsRAPN/cV88YK1yaDqb5xszOKRINB7FhZEjaCarWEclwmdGWzL7S32JlGA==} + '@fern-api/fai-sdk@0.0.6-2ee1b7e28': resolution: {integrity: sha512-3qhAAuc4ZJWLaFtyZzaYXfF9OQ5iviNrvLDXtjKScKUNS134fR3v3c3xedCidTq5KedapuBECziUaOmmd6KXVA==} engines: {node: '>=18.0.0'} @@ -9391,6 +9391,9 @@ packages: '@fern-fern/ir-sdk@62.1.0': resolution: {integrity: sha512-0FVJFtG1TvxAvPe6NlxOUSOYgs4dVTbYB1Faj/Fj+/ounMyucifW3QVy/DJeYjgdyn3DUzochSwIMvPrf1atqQ==} + '@fern-fern/ir-sdk@62.3.0': + resolution: {integrity: sha512-yrOgFcuSaUTYQoADidk+0oUZAx620w9ufJ04qwKe6e98vzLAkpcjHN+030hxBE+nIiAN4KAo49jUDjcqJ/ns0w==} + '@fern-fern/ir-v1-model@0.0.2': resolution: {integrity: sha512-Rho6qXYfRoB1sAISFS4V7vttVFN0ypoaztmbfKKNFmSTNVOyLN++e2xNZ+Aw9ckE5ZZmfMXK9v4+dnFReWVzvA==} @@ -16943,12 +16946,12 @@ snapshots: '@fern-api/dynamic-ir-sdk@59.6.1': {} - '@fern-api/dynamic-ir-sdk@61.4.0': {} - '@fern-api/dynamic-ir-sdk@61.7.0': {} '@fern-api/dynamic-ir-sdk@62.1.0': {} + '@fern-api/dynamic-ir-sdk@62.3.0': {} + '@fern-api/fai-sdk@0.0.6-2ee1b7e28': {} '@fern-api/fdr-sdk@0.142.4-4cf986c21(typescript@5.9.3)': @@ -17104,6 +17107,16 @@ snapshots: transitivePeerDependencies: - encoding + '@fern-fern/ir-sdk@62.3.0': + dependencies: + form-data: 4.0.5 + formdata-node: 6.0.3 + node-fetch: 2.7.0 + qs: 6.13.0 + readable-stream: 4.7.0 + transitivePeerDependencies: + - encoding + '@fern-fern/ir-v1-model@0.0.2': {} '@fern-fern/ir-v10-model@0.0.1': {} diff --git a/seed/csharp-model/inferred-auth-implicit-api-key/.editorconfig b/seed/csharp-model/inferred-auth-implicit-api-key/.editorconfig new file mode 100644 index 000000000000..1e7a0adbac80 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-api-key/.editorconfig @@ -0,0 +1,35 @@ +root = true + +[*.cs] +resharper_arrange_object_creation_when_type_evident_highlighting = hint +resharper_auto_property_can_be_made_get_only_global_highlighting = hint +resharper_check_namespace_highlighting = hint +resharper_class_never_instantiated_global_highlighting = hint +resharper_class_never_instantiated_local_highlighting = hint +resharper_collection_never_updated_global_highlighting = hint +resharper_convert_type_check_pattern_to_null_check_highlighting = hint +resharper_inconsistent_naming_highlighting = hint +resharper_member_can_be_private_global_highlighting = hint +resharper_member_hides_static_from_outer_class_highlighting = hint +resharper_not_accessed_field_local_highlighting = hint +resharper_nullable_warning_suppression_is_used_highlighting = suggestion +resharper_partial_type_with_single_part_highlighting = hint +resharper_prefer_concrete_value_over_default_highlighting = none +resharper_private_field_can_be_converted_to_local_variable_highlighting = hint +resharper_property_can_be_made_init_only_global_highlighting = hint +resharper_property_can_be_made_init_only_local_highlighting = hint +resharper_redundant_name_qualifier_highlighting = none +resharper_redundant_using_directive_highlighting = hint +resharper_replace_slice_with_range_indexer_highlighting = none +resharper_unused_auto_property_accessor_global_highlighting = hint +resharper_unused_auto_property_accessor_local_highlighting = hint +resharper_unused_member_global_highlighting = hint +resharper_unused_type_global_highlighting = hint +resharper_use_string_interpolation_highlighting = hint +dotnet_diagnostic.CS1591.severity = suggestion + +[src/**/Types/*.cs] +resharper_check_namespace_highlighting = none + +[src/**/Core/Public/*.cs] +resharper_check_namespace_highlighting = none \ No newline at end of file diff --git a/seed/csharp-model/inferred-auth-implicit-api-key/.fern/metadata.json b/seed/csharp-model/inferred-auth-implicit-api-key/.fern/metadata.json new file mode 100644 index 000000000000..1e1156144085 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-api-key/.fern/metadata.json @@ -0,0 +1,5 @@ +{ + "cliVersion": "DUMMY", + "generatorName": "fernapi/fern-csharp-model", + "generatorVersion": "latest" +} \ No newline at end of file diff --git a/seed/csharp-model/inferred-auth-implicit-api-key/.github/workflows/ci.yml b/seed/csharp-model/inferred-auth-implicit-api-key/.github/workflows/ci.yml new file mode 100644 index 000000000000..1ff248b5263a --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-api-key/.github/workflows/ci.yml @@ -0,0 +1,97 @@ +name: ci + +on: [push] + +env: + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true + DOTNET_NOLOGO: true + +jobs: + compile: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v5 + + - uses: actions/checkout@master + + - name: Setup .NET + uses: actions/setup-dotnet@v5 + with: + dotnet-version: 10.x + + - name: Print .NET info + run: dotnet --info + + - name: Install tools + run: dotnet tool restore + + - name: Restore dependencies + run: dotnet restore src/SeedInferredAuthImplicitApiKey/SeedInferredAuthImplicitApiKey.csproj + + - name: Build Release + run: dotnet build src/SeedInferredAuthImplicitApiKey/SeedInferredAuthImplicitApiKey.csproj -c Release --no-restore + + unit-tests: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v5 + + - name: Setup .NET + uses: actions/setup-dotnet@v5 + with: + dotnet-version: 10.x + + - name: Print .NET info + run: dotnet --info + + - name: Install tools + run: | + dotnet tool restore + + - name: Restore dependencies + run: dotnet restore src/SeedInferredAuthImplicitApiKey.Test/SeedInferredAuthImplicitApiKey.Test.csproj + + - name: Build Release + run: dotnet build src/SeedInferredAuthImplicitApiKey.Test/SeedInferredAuthImplicitApiKey.Test.csproj -c Release --no-restore + + - name: Run Tests + run: dotnet test src/SeedInferredAuthImplicitApiKey.Test/SeedInferredAuthImplicitApiKey.Test.csproj -c Release --no-build --no-restore + + + publish: + needs: [compile] + if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v5 + + - name: Setup .NET + uses: actions/setup-dotnet@v5 + with: + dotnet-version: 10.x + + - name: Print .NET info + run: dotnet --info + + - name: Install tools + run: dotnet tool restore + + - name: Restore dependencies + run: dotnet restore src/SeedInferredAuthImplicitApiKey/SeedInferredAuthImplicitApiKey.csproj + + - name: Build Release + run: dotnet build src/SeedInferredAuthImplicitApiKey/SeedInferredAuthImplicitApiKey.csproj -c Release --no-restore + + - name: Publish + env: + NUGET_API_KEY: ${{ secrets.NUGET_API_TOKEN }} + run: | + dotnet pack src/SeedInferredAuthImplicitApiKey/SeedInferredAuthImplicitApiKey.csproj -c Release --no-build --no-restore + dotnet nuget push src/SeedInferredAuthImplicitApiKey/bin/Release/*.nupkg --api-key $NUGET_API_KEY --source "nuget.org" + diff --git a/seed/csharp-model/inferred-auth-implicit-api-key/.gitignore b/seed/csharp-model/inferred-auth-implicit-api-key/.gitignore new file mode 100644 index 000000000000..11014f2b33d7 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-api-key/.gitignore @@ -0,0 +1,484 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +## This is based on `dotnet new gitignore` and customized by Fern + +# dotenv files +.env + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +# [Rr]elease/ (Ignored by Fern) +# [Rr]eleases/ (Ignored by Fern) +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +# [Ll]og/ (Ignored by Fern) +# [Ll]ogs/ (Ignored by Fern) + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET +project.lock.json +project.fragment.lock.json +artifacts/ + +# Tye +.tye/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml +.idea + +## +## Visual studio for Mac +## + + +# globs +Makefile.in +*.userprefs +*.usertasks +config.make +config.status +aclocal.m4 +install-sh +autom4te.cache/ +*.tar.gz +tarballs/ +test-results/ + +# Mac bundle stuff +*.dmg +*.app + +# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# Vim temporary swap files +*.swp diff --git a/seed/csharp-model/inferred-auth-implicit-api-key/SeedInferredAuthImplicitApiKey.slnx b/seed/csharp-model/inferred-auth-implicit-api-key/SeedInferredAuthImplicitApiKey.slnx new file mode 100644 index 000000000000..c990fc94b736 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-api-key/SeedInferredAuthImplicitApiKey.slnx @@ -0,0 +1,4 @@ + + + + diff --git a/seed/csharp-model/inferred-auth-implicit-api-key/snippet.json b/seed/csharp-model/inferred-auth-implicit-api-key/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/Json/AdditionalPropertiesTests.cs b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/Json/AdditionalPropertiesTests.cs new file mode 100644 index 000000000000..7770db8c3c63 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/Json/AdditionalPropertiesTests.cs @@ -0,0 +1,365 @@ +using global::System.Text.Json; +using global::System.Text.Json.Serialization; +using NUnit.Framework; +using SeedInferredAuthImplicitApiKey.Core; + +namespace SeedInferredAuthImplicitApiKey.Test.Core.Json; + +[TestFixture] +public class AdditionalPropertiesTests +{ + [Test] + public void Record_OnDeserialized_ShouldPopulateAdditionalProperties() + { + // Arrange + const string json = """ + { + "id": "1", + "category": "fiction", + "title": "The Hobbit" + } + """; + + // Act + var record = JsonUtils.Deserialize(json); + + // Assert + Assert.That(record, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(record.Id, Is.EqualTo("1")); + Assert.That(record.AdditionalProperties["category"].GetString(), Is.EqualTo("fiction")); + Assert.That(record.AdditionalProperties["title"].GetString(), Is.EqualTo("The Hobbit")); + }); + } + + [Test] + public void RecordWithWriteableAdditionalProperties_OnSerialization_ShouldIncludeAdditionalProperties() + { + // Arrange + var record = new WriteableRecord + { + Id = "1", + AdditionalProperties = { ["category"] = "fiction", ["title"] = "The Hobbit" }, + }; + + // Act + var json = JsonUtils.Serialize(record); + var deserializedRecord = JsonUtils.Deserialize(json); + + // Assert + Assert.That(deserializedRecord, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(deserializedRecord.Id, Is.EqualTo("1")); + Assert.That( + deserializedRecord.AdditionalProperties["category"], + Is.InstanceOf() + ); + Assert.That( + ((JsonElement)deserializedRecord.AdditionalProperties["category"]!).GetString(), + Is.EqualTo("fiction") + ); + Assert.That( + deserializedRecord.AdditionalProperties["title"], + Is.InstanceOf() + ); + Assert.That( + ((JsonElement)deserializedRecord.AdditionalProperties["title"]!).GetString(), + Is.EqualTo("The Hobbit") + ); + }); + } + + [Test] + public void ReadOnlyAdditionalProperties_ShouldRetrieveValuesCorrectly() + { + // Arrange + var extensionData = new Dictionary + { + ["key1"] = JsonUtils.SerializeToElement("value1"), + ["key2"] = JsonUtils.SerializeToElement(123), + }; + var readOnlyProps = new ReadOnlyAdditionalProperties(); + readOnlyProps.CopyFromExtensionData(extensionData); + + // Act & Assert + Assert.That(readOnlyProps["key1"].GetString(), Is.EqualTo("value1")); + Assert.That(readOnlyProps["key2"].GetInt32(), Is.EqualTo(123)); + } + + [Test] + public void AdditionalProperties_ShouldBehaveAsDictionary() + { + // Arrange + var additionalProps = new AdditionalProperties { ["key1"] = "value1", ["key2"] = 123 }; + + // Act + additionalProps["key3"] = true; + + // Assert + Assert.Multiple(() => + { + Assert.That(additionalProps["key1"], Is.EqualTo("value1")); + Assert.That(additionalProps["key2"], Is.EqualTo(123)); + Assert.That((bool)additionalProps["key3"]!, Is.True); + Assert.That(additionalProps.Count, Is.EqualTo(3)); + }); + } + + [Test] + public void AdditionalProperties_ToJsonObject_ShouldSerializeCorrectly() + { + // Arrange + var additionalProps = new AdditionalProperties { ["key1"] = "value1", ["key2"] = 123 }; + + // Act + var jsonObject = additionalProps.ToJsonObject(); + + Assert.Multiple(() => + { + // Assert + Assert.That(jsonObject["key1"]!.GetValue(), Is.EqualTo("value1")); + Assert.That(jsonObject["key2"]!.GetValue(), Is.EqualTo(123)); + }); + } + + [Test] + public void AdditionalProperties_MixReadAndWrite_ShouldOverwriteDeserializedProperty() + { + // Arrange + const string json = """ + { + "id": "1", + "category": "fiction", + "title": "The Hobbit" + } + """; + var record = JsonUtils.Deserialize(json); + + // Act + record.AdditionalProperties["category"] = "non-fiction"; + + // Assert + Assert.Multiple(() => + { + Assert.That(record, Is.Not.Null); + Assert.That(record.Id, Is.EqualTo("1")); + Assert.That(record.AdditionalProperties["category"], Is.EqualTo("non-fiction")); + Assert.That(record.AdditionalProperties["title"], Is.InstanceOf()); + Assert.That( + ((JsonElement)record.AdditionalProperties["title"]!).GetString(), + Is.EqualTo("The Hobbit") + ); + }); + } + + [Test] + public void RecordWithReadonlyAdditionalPropertiesInts_OnDeserialized_ShouldPopulateAdditionalProperties() + { + // Arrange + const string json = """ + { + "extra1": 42, + "extra2": 99 + } + """; + + // Act + var record = JsonUtils.Deserialize(json); + + // Assert + Assert.That(record, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(record.AdditionalProperties["extra1"], Is.EqualTo(42)); + Assert.That(record.AdditionalProperties["extra2"], Is.EqualTo(99)); + }); + } + + [Test] + public void RecordWithAdditionalPropertiesInts_OnSerialization_ShouldIncludeAdditionalProperties() + { + // Arrange + var record = new WriteableRecordWithInts + { + AdditionalProperties = { ["extra1"] = 42, ["extra2"] = 99 }, + }; + + // Act + var json = JsonUtils.Serialize(record); + var deserializedRecord = JsonUtils.Deserialize(json); + + // Assert + Assert.That(deserializedRecord, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(deserializedRecord.AdditionalProperties["extra1"], Is.EqualTo(42)); + Assert.That(deserializedRecord.AdditionalProperties["extra2"], Is.EqualTo(99)); + }); + } + + [Test] + public void RecordWithReadonlyAdditionalPropertiesDictionaries_OnDeserialized_ShouldPopulateAdditionalProperties() + { + // Arrange + const string json = """ + { + "extra1": { "key1": true, "key2": false }, + "extra2": { "key3": true } + } + """; + + // Act + var record = JsonUtils.Deserialize(json); + + // Assert + Assert.That(record, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(record.AdditionalProperties["extra1"]["key1"], Is.True); + Assert.That(record.AdditionalProperties["extra1"]["key2"], Is.False); + Assert.That(record.AdditionalProperties["extra2"]["key3"], Is.True); + }); + } + + [Test] + public void RecordWithAdditionalPropertiesDictionaries_OnSerialization_ShouldIncludeAdditionalProperties() + { + // Arrange + var record = new WriteableRecordWithDictionaries + { + AdditionalProperties = + { + ["extra1"] = new Dictionary { { "key1", true }, { "key2", false } }, + ["extra2"] = new Dictionary { { "key3", true } }, + }, + }; + + // Act + var json = JsonUtils.Serialize(record); + var deserializedRecord = JsonUtils.Deserialize(json); + + // Assert + Assert.That(deserializedRecord, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(deserializedRecord.AdditionalProperties["extra1"]["key1"], Is.True); + Assert.That(deserializedRecord.AdditionalProperties["extra1"]["key2"], Is.False); + Assert.That(deserializedRecord.AdditionalProperties["extra2"]["key3"], Is.True); + }); + } + + private record Record : IJsonOnDeserialized + { + [JsonPropertyName("id")] + public required string Id { get; set; } + + [JsonExtensionData] + private readonly IDictionary _extensionData = + new Dictionary(); + + [JsonIgnore] + public ReadOnlyAdditionalProperties AdditionalProperties { get; } = new(); + + void IJsonOnDeserialized.OnDeserialized() + { + AdditionalProperties.CopyFromExtensionData(_extensionData); + } + } + + private record WriteableRecord : IJsonOnDeserialized, IJsonOnSerializing + { + [JsonPropertyName("id")] + public required string Id { get; set; } + + [JsonExtensionData] + private readonly IDictionary _extensionData = + new Dictionary(); + + [JsonIgnore] + public AdditionalProperties AdditionalProperties { get; set; } = new(); + + void IJsonOnDeserialized.OnDeserialized() + { + AdditionalProperties.CopyFromExtensionData(_extensionData); + } + + void IJsonOnSerializing.OnSerializing() + { + AdditionalProperties.CopyToExtensionData(_extensionData); + } + } + + private record RecordWithInts : IJsonOnDeserialized + { + [JsonExtensionData] + private readonly IDictionary _extensionData = + new Dictionary(); + + [JsonIgnore] + public ReadOnlyAdditionalProperties AdditionalProperties { get; } = new(); + + void IJsonOnDeserialized.OnDeserialized() + { + AdditionalProperties.CopyFromExtensionData(_extensionData); + } + } + + private record WriteableRecordWithInts : IJsonOnDeserialized, IJsonOnSerializing + { + [JsonExtensionData] + private readonly IDictionary _extensionData = + new Dictionary(); + + [JsonIgnore] + public AdditionalProperties AdditionalProperties { get; } = new(); + + void IJsonOnDeserialized.OnDeserialized() + { + AdditionalProperties.CopyFromExtensionData(_extensionData); + } + + void IJsonOnSerializing.OnSerializing() + { + AdditionalProperties.CopyToExtensionData(_extensionData); + } + } + + private record RecordWithDictionaries : IJsonOnDeserialized + { + [JsonExtensionData] + private readonly IDictionary _extensionData = + new Dictionary(); + + [JsonIgnore] + public ReadOnlyAdditionalProperties< + Dictionary + > AdditionalProperties { get; } = new(); + + void IJsonOnDeserialized.OnDeserialized() + { + AdditionalProperties.CopyFromExtensionData(_extensionData); + } + } + + private record WriteableRecordWithDictionaries : IJsonOnDeserialized, IJsonOnSerializing + { + [JsonExtensionData] + private readonly IDictionary _extensionData = + new Dictionary(); + + [JsonIgnore] + public AdditionalProperties> AdditionalProperties { get; } = new(); + + void IJsonOnDeserialized.OnDeserialized() + { + AdditionalProperties.CopyFromExtensionData(_extensionData); + } + + void IJsonOnSerializing.OnSerializing() + { + AdditionalProperties.CopyToExtensionData(_extensionData); + } + } +} diff --git a/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/Json/DateOnlyJsonTests.cs b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/Json/DateOnlyJsonTests.cs new file mode 100644 index 000000000000..2e0120b089d7 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/Json/DateOnlyJsonTests.cs @@ -0,0 +1,76 @@ +using NUnit.Framework; +using SeedInferredAuthImplicitApiKey.Core; + +namespace SeedInferredAuthImplicitApiKey.Test.Core.Json; + +[TestFixture] +public class DateOnlyJsonTests +{ + [Test] + public void SerializeDateOnly_ShouldMatchExpectedFormat() + { + (DateOnly dateOnly, string expected)[] testCases = + [ + (new DateOnly(2023, 10, 5), "\"2023-10-05\""), + (new DateOnly(2023, 1, 1), "\"2023-01-01\""), + (new DateOnly(2023, 12, 31), "\"2023-12-31\""), + (new DateOnly(2023, 6, 15), "\"2023-06-15\""), + (new DateOnly(2023, 3, 10), "\"2023-03-10\""), + ]; + foreach (var (dateOnly, expected) in testCases) + { + var json = JsonUtils.Serialize(dateOnly); + Assert.That(json, Is.EqualTo(expected)); + } + } + + [Test] + public void DeserializeDateOnly_ShouldMatchExpectedDateOnly() + { + (DateOnly expected, string json)[] testCases = + [ + (new DateOnly(2023, 10, 5), "\"2023-10-05\""), + (new DateOnly(2023, 1, 1), "\"2023-01-01\""), + (new DateOnly(2023, 12, 31), "\"2023-12-31\""), + (new DateOnly(2023, 6, 15), "\"2023-06-15\""), + (new DateOnly(2023, 3, 10), "\"2023-03-10\""), + ]; + + foreach (var (expected, json) in testCases) + { + var dateOnly = JsonUtils.Deserialize(json); + Assert.That(dateOnly, Is.EqualTo(expected)); + } + } + + [Test] + public void SerializeNullableDateOnly_ShouldMatchExpectedFormat() + { + (DateOnly? dateOnly, string expected)[] testCases = + [ + (new DateOnly(2023, 10, 5), "\"2023-10-05\""), + (null, "null"), + ]; + foreach (var (dateOnly, expected) in testCases) + { + var json = JsonUtils.Serialize(dateOnly); + Assert.That(json, Is.EqualTo(expected)); + } + } + + [Test] + public void DeserializeNullableDateOnly_ShouldMatchExpectedDateOnly() + { + (DateOnly? expected, string json)[] testCases = + [ + (new DateOnly(2023, 10, 5), "\"2023-10-05\""), + (null, "null"), + ]; + + foreach (var (expected, json) in testCases) + { + var dateOnly = JsonUtils.Deserialize(json); + Assert.That(dateOnly, Is.EqualTo(expected)); + } + } +} diff --git a/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/Json/DateTimeJsonTests.cs b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/Json/DateTimeJsonTests.cs new file mode 100644 index 000000000000..3c8de3eb25a7 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/Json/DateTimeJsonTests.cs @@ -0,0 +1,110 @@ +using NUnit.Framework; +using SeedInferredAuthImplicitApiKey.Core; + +namespace SeedInferredAuthImplicitApiKey.Test.Core.Json; + +[TestFixture] +public class DateTimeJsonTests +{ + [Test] + public void SerializeDateTime_ShouldMatchExpectedFormat() + { + (DateTime dateTime, string expected)[] testCases = + [ + ( + new DateTime(2023, 10, 5, 14, 30, 0, DateTimeKind.Utc), + "\"2023-10-05T14:30:00.000Z\"" + ), + (new DateTime(2023, 1, 1, 0, 0, 0, DateTimeKind.Utc), "\"2023-01-01T00:00:00.000Z\""), + ( + new DateTime(2023, 12, 31, 23, 59, 59, DateTimeKind.Utc), + "\"2023-12-31T23:59:59.000Z\"" + ), + (new DateTime(2023, 6, 15, 12, 0, 0, DateTimeKind.Utc), "\"2023-06-15T12:00:00.000Z\""), + ( + new DateTime(2023, 3, 10, 8, 45, 30, DateTimeKind.Utc), + "\"2023-03-10T08:45:30.000Z\"" + ), + ( + new DateTime(2023, 3, 10, 8, 45, 30, 123, DateTimeKind.Utc), + "\"2023-03-10T08:45:30.123Z\"" + ), + ]; + foreach (var (dateTime, expected) in testCases) + { + var json = JsonUtils.Serialize(dateTime); + Assert.That(json, Is.EqualTo(expected)); + } + } + + [Test] + public void DeserializeDateTime_ShouldMatchExpectedDateTime() + { + (DateTime expected, string json)[] testCases = + [ + ( + new DateTime(2023, 10, 5, 14, 30, 0, DateTimeKind.Utc), + "\"2023-10-05T14:30:00.000Z\"" + ), + (new DateTime(2023, 1, 1, 0, 0, 0, DateTimeKind.Utc), "\"2023-01-01T00:00:00.000Z\""), + ( + new DateTime(2023, 12, 31, 23, 59, 59, DateTimeKind.Utc), + "\"2023-12-31T23:59:59.000Z\"" + ), + (new DateTime(2023, 6, 15, 12, 0, 0, DateTimeKind.Utc), "\"2023-06-15T12:00:00.000Z\""), + ( + new DateTime(2023, 3, 10, 8, 45, 30, DateTimeKind.Utc), + "\"2023-03-10T08:45:30.000Z\"" + ), + (new DateTime(2023, 3, 10, 8, 45, 30, DateTimeKind.Utc), "\"2023-03-10T08:45:30Z\""), + ( + new DateTime(2023, 3, 10, 8, 45, 30, 123, DateTimeKind.Utc), + "\"2023-03-10T08:45:30.123Z\"" + ), + ]; + + foreach (var (expected, json) in testCases) + { + var dateTime = JsonUtils.Deserialize(json); + Assert.That(dateTime, Is.EqualTo(expected)); + } + } + + [Test] + public void SerializeNullableDateTime_ShouldMatchExpectedFormat() + { + (DateTime? expected, string json)[] testCases = + [ + ( + new DateTime(2023, 10, 5, 14, 30, 0, DateTimeKind.Utc), + "\"2023-10-05T14:30:00.000Z\"" + ), + (null, "null"), + ]; + + foreach (var (expected, json) in testCases) + { + var dateTime = JsonUtils.Deserialize(json); + Assert.That(dateTime, Is.EqualTo(expected)); + } + } + + [Test] + public void DeserializeNullableDateTime_ShouldMatchExpectedDateTime() + { + (DateTime? expected, string json)[] testCases = + [ + ( + new DateTime(2023, 10, 5, 14, 30, 0, DateTimeKind.Utc), + "\"2023-10-05T14:30:00.000Z\"" + ), + (null, "null"), + ]; + + foreach (var (expected, json) in testCases) + { + var dateTime = JsonUtils.Deserialize(json); + Assert.That(dateTime, Is.EqualTo(expected)); + } + } +} diff --git a/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/Json/JsonAccessAttributeTests.cs b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/Json/JsonAccessAttributeTests.cs new file mode 100644 index 000000000000..0e8ca22b9358 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/Json/JsonAccessAttributeTests.cs @@ -0,0 +1,160 @@ +using global::System.Text.Json.Serialization; +using NUnit.Framework; +using SeedInferredAuthImplicitApiKey.Core; + +namespace SeedInferredAuthImplicitApiKey.Test.Core.Json; + +[TestFixture] +public class JsonAccessAttributeTests +{ + private class MyClass + { + [JsonPropertyName("read_only_prop")] + [JsonAccess(JsonAccessType.ReadOnly)] + public string? ReadOnlyProp { get; set; } + + [JsonPropertyName("write_only_prop")] + [JsonAccess(JsonAccessType.WriteOnly)] + public string? WriteOnlyProp { get; set; } + + [JsonPropertyName("normal_prop")] + public string? NormalProp { get; set; } + + [JsonPropertyName("read_only_nullable_list")] + [JsonAccess(JsonAccessType.ReadOnly)] + public IEnumerable? ReadOnlyNullableList { get; set; } + + [JsonPropertyName("read_only_list")] + [JsonAccess(JsonAccessType.ReadOnly)] + public IEnumerable ReadOnlyList { get; set; } = []; + + [JsonPropertyName("write_only_nullable_list")] + [JsonAccess(JsonAccessType.WriteOnly)] + public IEnumerable? WriteOnlyNullableList { get; set; } + + [JsonPropertyName("write_only_list")] + [JsonAccess(JsonAccessType.WriteOnly)] + public IEnumerable WriteOnlyList { get; set; } = []; + + [JsonPropertyName("normal_list")] + public IEnumerable NormalList { get; set; } = []; + + [JsonPropertyName("normal_nullable_list")] + public IEnumerable? NullableNormalList { get; set; } + } + + [Test] + public void JsonAccessAttribute_ShouldWorkAsExpected() + { + const string json = """ + { + "read_only_prop": "read", + "write_only_prop": "write", + "normal_prop": "normal_prop", + "read_only_nullable_list": ["item1", "item2"], + "read_only_list": ["item3", "item4"], + "write_only_nullable_list": ["item5", "item6"], + "write_only_list": ["item7", "item8"], + "normal_list": ["normal1", "normal2"], + "normal_nullable_list": ["normal1", "normal2"] + } + """; + var obj = JsonUtils.Deserialize(json); + + Assert.Multiple(() => + { + // String properties + Assert.That(obj.ReadOnlyProp, Is.EqualTo("read")); + Assert.That(obj.WriteOnlyProp, Is.Null); + Assert.That(obj.NormalProp, Is.EqualTo("normal_prop")); + + // List properties - read only + var nullableReadOnlyList = obj.ReadOnlyNullableList?.ToArray(); + Assert.That(nullableReadOnlyList, Is.Not.Null); + Assert.That(nullableReadOnlyList, Has.Length.EqualTo(2)); + Assert.That(nullableReadOnlyList![0], Is.EqualTo("item1")); + Assert.That(nullableReadOnlyList![1], Is.EqualTo("item2")); + + var readOnlyList = obj.ReadOnlyList.ToArray(); + Assert.That(readOnlyList, Is.Not.Null); + Assert.That(readOnlyList, Has.Length.EqualTo(2)); + Assert.That(readOnlyList[0], Is.EqualTo("item3")); + Assert.That(readOnlyList[1], Is.EqualTo("item4")); + + // List properties - write only + Assert.That(obj.WriteOnlyNullableList, Is.Null); + Assert.That(obj.WriteOnlyList, Is.Not.Null); + Assert.That(obj.WriteOnlyList, Is.Empty); + + // Normal list property + var normalList = obj.NormalList.ToArray(); + Assert.That(normalList, Is.Not.Null); + Assert.That(normalList, Has.Length.EqualTo(2)); + Assert.That(normalList[0], Is.EqualTo("normal1")); + Assert.That(normalList[1], Is.EqualTo("normal2")); + }); + + // Set up values for serialization + obj.WriteOnlyProp = "write"; + obj.NormalProp = "new_value"; + obj.WriteOnlyNullableList = new List { "write1", "write2" }; + obj.WriteOnlyList = new List { "write3", "write4" }; + obj.NormalList = new List { "new_normal" }; + obj.NullableNormalList = new List { "new_normal" }; + + var serializedJson = JsonUtils.Serialize(obj); + const string expectedJson = """ + { + "write_only_prop": "write", + "normal_prop": "new_value", + "write_only_nullable_list": [ + "write1", + "write2" + ], + "write_only_list": [ + "write3", + "write4" + ], + "normal_list": [ + "new_normal" + ], + "normal_nullable_list": [ + "new_normal" + ] + } + """; + Assert.That(serializedJson, Is.EqualTo(expectedJson).IgnoreWhiteSpace); + } + + [Test] + public void JsonAccessAttribute_WithNullListsInJson_ShouldWorkAsExpected() + { + const string json = """ + { + "read_only_prop": "read", + "normal_prop": "normal_prop", + "read_only_nullable_list": null, + "read_only_list": [] + } + """; + var obj = JsonUtils.Deserialize(json); + + Assert.Multiple(() => + { + // Read-only nullable list should be null when JSON contains null + var nullableReadOnlyList = obj.ReadOnlyNullableList?.ToArray(); + Assert.That(nullableReadOnlyList, Is.Null); + + // Read-only non-nullable list should never be null, but empty when JSON contains null + var readOnlyList = obj.ReadOnlyList.ToArray(); // This should be initialized to an empty list by default + Assert.That(readOnlyList, Is.Not.Null); + Assert.That(readOnlyList, Is.Empty); + }); + + // Serialize and verify read-only lists are not included + var serializedJson = JsonUtils.Serialize(obj); + Assert.That(serializedJson, Does.Not.Contain("read_only_prop")); + Assert.That(serializedJson, Does.Not.Contain("read_only_nullable_list")); + Assert.That(serializedJson, Does.Not.Contain("read_only_list")); + } +} diff --git a/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/Json/OneOfSerializerTests.cs b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/Json/OneOfSerializerTests.cs new file mode 100644 index 000000000000..f8dcd3076895 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/Json/OneOfSerializerTests.cs @@ -0,0 +1,314 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using NUnit.Framework; +using OneOf; +using SeedInferredAuthImplicitApiKey.Core; + +namespace SeedInferredAuthImplicitApiKey.Test.Core.Json; + +[TestFixture] +[Parallelizable(ParallelScope.All)] +public class OneOfSerializerTests +{ + private class Foo + { + [JsonPropertyName("string_prop")] + public required string StringProp { get; set; } + } + + private class Bar + { + [JsonPropertyName("int_prop")] + public required int IntProp { get; set; } + } + + private static readonly OneOf OneOf1 = OneOf< + string, + int, + object, + Foo, + Bar + >.FromT2(new { }); + private const string OneOf1String = "{}"; + + private static readonly OneOf OneOf2 = OneOf< + string, + int, + object, + Foo, + Bar + >.FromT0("test"); + private const string OneOf2String = "\"test\""; + + private static readonly OneOf OneOf3 = OneOf< + string, + int, + object, + Foo, + Bar + >.FromT1(123); + private const string OneOf3String = "123"; + + private static readonly OneOf OneOf4 = OneOf< + string, + int, + object, + Foo, + Bar + >.FromT3(new Foo { StringProp = "test" }); + private const string OneOf4String = "{\"string_prop\": \"test\"}"; + + private static readonly OneOf OneOf5 = OneOf< + string, + int, + object, + Foo, + Bar + >.FromT4(new Bar { IntProp = 5 }); + private const string OneOf5String = "{\"int_prop\": 5}"; + + [Test] + public void Serialize_OneOfs_Should_Return_Expected_String() + { + (OneOf, string)[] testData = + [ + (OneOf1, OneOf1String), + (OneOf2, OneOf2String), + (OneOf3, OneOf3String), + (OneOf4, OneOf4String), + (OneOf5, OneOf5String), + ]; + Assert.Multiple(() => + { + foreach (var (oneOf, expected) in testData) + { + var result = JsonUtils.Serialize(oneOf); + Assert.That(result, Is.EqualTo(expected).IgnoreWhiteSpace); + } + }); + } + + [Test] + public void OneOfs_Should_Deserialize_From_String() + { + (OneOf, string)[] testData = + [ + (OneOf1, OneOf1String), + (OneOf2, OneOf2String), + (OneOf3, OneOf3String), + (OneOf4, OneOf4String), + (OneOf5, OneOf5String), + ]; + Assert.Multiple(() => + { + foreach (var (oneOf, json) in testData) + { + var result = JsonUtils.Deserialize>(json); + Assert.That(result.Index, Is.EqualTo(oneOf.Index)); + Assert.That(json, Is.EqualTo(JsonUtils.Serialize(result.Value)).IgnoreWhiteSpace); + } + }); + } + + private static readonly OneOf? NullableOneOf1 = null; + private const string NullableOneOf1String = "null"; + + private static readonly OneOf? NullableOneOf2 = OneOf< + string, + int, + object, + Foo, + Bar + >.FromT4(new Bar { IntProp = 5 }); + private const string NullableOneOf2String = "{\"int_prop\": 5}"; + + [Test] + public void Serialize_NullableOneOfs_Should_Return_Expected_String() + { + (OneOf?, string)[] testData = + [ + (NullableOneOf1, NullableOneOf1String), + (NullableOneOf2, NullableOneOf2String), + ]; + Assert.Multiple(() => + { + foreach (var (oneOf, expected) in testData) + { + var result = JsonUtils.Serialize(oneOf); + Assert.That(result, Is.EqualTo(expected).IgnoreWhiteSpace); + } + }); + } + + [Test] + public void NullableOneOfs_Should_Deserialize_From_String() + { + (OneOf?, string)[] testData = + [ + (NullableOneOf1, NullableOneOf1String), + (NullableOneOf2, NullableOneOf2String), + ]; + Assert.Multiple(() => + { + foreach (var (oneOf, json) in testData) + { + var result = JsonUtils.Deserialize?>(json); + Assert.That(result?.Index, Is.EqualTo(oneOf?.Index)); + Assert.That(json, Is.EqualTo(JsonUtils.Serialize(result?.Value)).IgnoreWhiteSpace); + } + }); + } + + private static readonly OneOf OneOfWithNullable1 = OneOf< + string, + int, + Foo? + >.FromT2(null); + private const string OneOfWithNullable1String = "null"; + + private static readonly OneOf OneOfWithNullable2 = OneOf< + string, + int, + Foo? + >.FromT2(new Foo { StringProp = "test" }); + private const string OneOfWithNullable2String = "{\"string_prop\": \"test\"}"; + + private static readonly OneOf OneOfWithNullable3 = OneOf< + string, + int, + Foo? + >.FromT0("test"); + private const string OneOfWithNullable3String = "\"test\""; + + [Test] + public void Serialize_OneOfWithNullables_Should_Return_Expected_String() + { + (OneOf, string)[] testData = + [ + (OneOfWithNullable1, OneOfWithNullable1String), + (OneOfWithNullable2, OneOfWithNullable2String), + (OneOfWithNullable3, OneOfWithNullable3String), + ]; + Assert.Multiple(() => + { + foreach (var (oneOf, expected) in testData) + { + var result = JsonUtils.Serialize(oneOf); + Assert.That(result, Is.EqualTo(expected).IgnoreWhiteSpace); + } + }); + } + + [Test] + public void OneOfWithNullables_Should_Deserialize_From_String() + { + (OneOf, string)[] testData = + [ + // (OneOfWithNullable1, OneOfWithNullable1String), // not possible with .NET's JSON serializer + (OneOfWithNullable2, OneOfWithNullable2String), + (OneOfWithNullable3, OneOfWithNullable3String), + ]; + Assert.Multiple(() => + { + foreach (var (oneOf, json) in testData) + { + var result = JsonUtils.Deserialize>(json); + Assert.That(result.Index, Is.EqualTo(oneOf.Index)); + Assert.That(json, Is.EqualTo(JsonUtils.Serialize(result.Value)).IgnoreWhiteSpace); + } + }); + } + + [Test] + public void Serialize_OneOfWithObjectLast_Should_Return_Expected_String() + { + var oneOfWithObjectLast = OneOf.FromT4( + new { random = "data" } + ); + const string oneOfWithObjectLastString = "{\"random\": \"data\"}"; + + var result = JsonUtils.Serialize(oneOfWithObjectLast); + Assert.That(result, Is.EqualTo(oneOfWithObjectLastString).IgnoreWhiteSpace); + } + + [Test] + public void OneOfWithObjectLast_Should_Deserialize_From_String() + { + const string oneOfWithObjectLastString = "{\"random\": \"data\"}"; + var result = JsonUtils.Deserialize>( + oneOfWithObjectLastString + ); + Assert.Multiple(() => + { + Assert.That(result.Index, Is.EqualTo(4)); + Assert.That(result.Value, Is.InstanceOf()); + Assert.That( + JsonUtils.Serialize(result.Value), + Is.EqualTo(oneOfWithObjectLastString).IgnoreWhiteSpace + ); + }); + } + + [Test] + public void Serialize_OneOfWithObjectNotLast_Should_Return_Expected_String() + { + var oneOfWithObjectNotLast = OneOf.FromT1( + new { random = "data" } + ); + const string oneOfWithObjectNotLastString = "{\"random\": \"data\"}"; + + var result = JsonUtils.Serialize(oneOfWithObjectNotLast); + Assert.That(result, Is.EqualTo(oneOfWithObjectNotLastString).IgnoreWhiteSpace); + } + + [Test] + public void OneOfWithObjectNotLast_Should_Deserialize_From_String() + { + const string oneOfWithObjectNotLastString = "{\"random\": \"data\"}"; + var result = JsonUtils.Deserialize>( + oneOfWithObjectNotLastString + ); + Assert.Multiple(() => + { + Assert.That(result.Index, Is.EqualTo(1)); + Assert.That(result.Value, Is.InstanceOf()); + Assert.That( + JsonUtils.Serialize(result.Value), + Is.EqualTo(oneOfWithObjectNotLastString).IgnoreWhiteSpace + ); + }); + } + + [Test] + public void Serialize_OneOfSingleType_Should_Return_Expected_String() + { + var oneOfSingle = OneOf.FromT0("single"); + const string oneOfSingleString = "\"single\""; + + var result = JsonUtils.Serialize(oneOfSingle); + Assert.That(result, Is.EqualTo(oneOfSingleString)); + } + + [Test] + public void OneOfSingleType_Should_Deserialize_From_String() + { + const string oneOfSingleString = "\"single\""; + var result = JsonUtils.Deserialize>(oneOfSingleString); + Assert.Multiple(() => + { + Assert.That(result.Index, Is.EqualTo(0)); + Assert.That(result.Value, Is.EqualTo("single")); + }); + } + + [Test] + public void Deserialize_InvalidData_Should_Throw_Exception() + { + const string invalidJson = "{\"invalid\": \"data\"}"; + + Assert.Throws(() => + { + JsonUtils.Deserialize>(invalidJson); + }); + } +} diff --git a/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/Json/StringEnumSerializerTests.cs b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/Json/StringEnumSerializerTests.cs new file mode 100644 index 000000000000..52f0b414861d --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/Json/StringEnumSerializerTests.cs @@ -0,0 +1,138 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using NUnit.Framework; +using SeedInferredAuthImplicitApiKey.Core; + +namespace SeedInferredAuthImplicitApiKey.Test.Core.Json; + +[TestFixture] +[Parallelizable(ParallelScope.All)] +public class StringEnumSerializerTests +{ + private static readonly JsonSerializerOptions JsonOptions = new() { WriteIndented = true }; + + private static readonly DummyEnum KnownEnumValue2 = DummyEnum.KnownValue2; + private static readonly DummyEnum UnknownEnumValue = DummyEnum.FromCustom("unknown_value"); + + private static readonly string JsonWithKnownEnum2 = $$""" + { + "enum_property": "{{KnownEnumValue2}}" + } + """; + + private static readonly string JsonWithUnknownEnum = $$""" + { + "enum_property": "{{UnknownEnumValue}}" + } + """; + + [Test] + public void ShouldParseKnownEnumValue2() + { + var obj = JsonSerializer.Deserialize(JsonWithKnownEnum2, JsonOptions); + Assert.That(obj, Is.Not.Null); + Assert.That(obj.EnumProperty, Is.EqualTo(KnownEnumValue2)); + } + + [Test] + public void ShouldParseUnknownEnum() + { + var obj = JsonSerializer.Deserialize(JsonWithUnknownEnum, JsonOptions); + Assert.That(obj, Is.Not.Null); + Assert.That(obj.EnumProperty, Is.EqualTo(UnknownEnumValue)); + } + + [Test] + public void ShouldSerializeKnownEnumValue2() + { + var json = JsonSerializer.SerializeToElement( + new DummyObject { EnumProperty = KnownEnumValue2 }, + JsonOptions + ); + TestContext.Out.WriteLine("Serialized JSON: \n" + json); + var enumString = json.GetProperty("enum_property").GetString(); + Assert.That(enumString, Is.Not.Null); + Assert.That(enumString, Is.EqualTo(KnownEnumValue2)); + } + + [Test] + public void ShouldSerializeUnknownEnum() + { + var json = JsonSerializer.SerializeToElement( + new DummyObject { EnumProperty = UnknownEnumValue }, + JsonOptions + ); + TestContext.Out.WriteLine("Serialized JSON: \n" + json); + var enumString = json.GetProperty("enum_property").GetString(); + Assert.That(enumString, Is.Not.Null); + Assert.That(enumString, Is.EqualTo(UnknownEnumValue)); + } +} + +public class DummyObject +{ + [JsonPropertyName("enum_property")] + public DummyEnum EnumProperty { get; set; } +} + +[JsonConverter(typeof(StringEnumSerializer))] +public readonly record struct DummyEnum : IStringEnum +{ + public DummyEnum(string value) + { + Value = value; + } + + /// + /// The string value of the enum. + /// + public string Value { get; } + + public static readonly DummyEnum KnownValue1 = FromCustom(Values.KnownValue1); + + public static readonly DummyEnum KnownValue2 = FromCustom(Values.KnownValue2); + + /// + /// Constant strings for enum values + /// + public static class Values + { + public const string KnownValue1 = "known_value1"; + + public const string KnownValue2 = "known_value2"; + } + + /// + /// Create a string enum with the given value. + /// + public static DummyEnum FromCustom(string value) + { + return new DummyEnum(value); + } + + /// + /// Returns the string value of the enum. + /// + public override string ToString() + { + return Value; + } + + public bool Equals(string? other) + { + return Value.Equals(other); + } + + public override int GetHashCode() + { + return Value.GetHashCode(); + } + + public static explicit operator string(DummyEnum value) => value.Value; + + public static explicit operator DummyEnum(string value) => new(value); + + public static bool operator ==(DummyEnum value1, string value2) => value1.Value.Equals(value2); + + public static bool operator !=(DummyEnum value1, string value2) => !value1.Value.Equals(value2); +} diff --git a/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/SeedInferredAuthImplicitApiKey.Test.Custom.props b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/SeedInferredAuthImplicitApiKey.Test.Custom.props new file mode 100644 index 000000000000..aac9b5020d80 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/SeedInferredAuthImplicitApiKey.Test.Custom.props @@ -0,0 +1,6 @@ + + diff --git a/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/SeedInferredAuthImplicitApiKey.Test.csproj b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/SeedInferredAuthImplicitApiKey.Test.csproj new file mode 100644 index 000000000000..ef25e188c123 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/SeedInferredAuthImplicitApiKey.Test.csproj @@ -0,0 +1,39 @@ + + + net8.0 + 12 + enable + enable + false + true + true + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + diff --git a/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Utils/JsonElementComparer.cs b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Utils/JsonElementComparer.cs new file mode 100644 index 000000000000..1704c99af443 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Utils/JsonElementComparer.cs @@ -0,0 +1,236 @@ +using System.Text.Json; +using NUnit.Framework.Constraints; + +namespace NUnit.Framework; + +/// +/// Extensions for EqualConstraint to handle JsonElement objects. +/// +public static class JsonElementComparerExtensions +{ + /// + /// Extension method for comparing JsonElement objects in NUnit tests. + /// Property order doesn't matter, but array order does matter. + /// Includes special handling for DateTime string formats. + /// + /// The Is.EqualTo() constraint instance. + /// A constraint that can compare JsonElements with detailed diffs. + public static EqualConstraint UsingJsonElementComparer(this EqualConstraint constraint) + { + return constraint.Using(new JsonElementComparer()); + } +} + +/// +/// Equality comparer for JsonElement with detailed reporting. +/// Property order doesn't matter, but array order does matter. +/// Now includes special handling for DateTime string formats with improved null handling. +/// +public class JsonElementComparer : IEqualityComparer +{ + private string _failurePath = string.Empty; + + /// + public bool Equals(JsonElement x, JsonElement y) + { + _failurePath = string.Empty; + return CompareJsonElements(x, y, string.Empty); + } + + /// + public int GetHashCode(JsonElement obj) + { + return JsonSerializer.Serialize(obj).GetHashCode(); + } + + private bool CompareJsonElements(JsonElement x, JsonElement y, string path) + { + // If value kinds don't match, they're not equivalent + if (x.ValueKind != y.ValueKind) + { + _failurePath = $"{path}: Expected {x.ValueKind} but got {y.ValueKind}"; + return false; + } + + switch (x.ValueKind) + { + case JsonValueKind.Object: + return CompareJsonObjects(x, y, path); + + case JsonValueKind.Array: + return CompareJsonArraysInOrder(x, y, path); + + case JsonValueKind.String: + string? xStr = x.GetString(); + string? yStr = y.GetString(); + + // Handle null strings + if (xStr is null && yStr is null) + return true; + + if (xStr is null || yStr is null) + { + _failurePath = + $"{path}: Expected {(xStr is null ? "null" : $"\"{xStr}\"")} but got {(yStr is null ? "null" : $"\"{yStr}\"")}"; + return false; + } + + // Check if they are identical strings + if (xStr == yStr) + return true; + + // Try to handle DateTime strings + if (IsLikelyDateTimeString(xStr) && IsLikelyDateTimeString(yStr)) + { + if (AreEquivalentDateTimeStrings(xStr, yStr)) + return true; + } + + _failurePath = $"{path}: Expected \"{xStr}\" but got \"{yStr}\""; + return false; + + case JsonValueKind.Number: + if (x.GetDecimal() != y.GetDecimal()) + { + _failurePath = $"{path}: Expected {x.GetDecimal()} but got {y.GetDecimal()}"; + return false; + } + + return true; + + case JsonValueKind.True: + case JsonValueKind.False: + if (x.GetBoolean() != y.GetBoolean()) + { + _failurePath = $"{path}: Expected {x.GetBoolean()} but got {y.GetBoolean()}"; + return false; + } + + return true; + + case JsonValueKind.Null: + return true; + + default: + _failurePath = $"{path}: Unsupported JsonValueKind {x.ValueKind}"; + return false; + } + } + + private bool IsLikelyDateTimeString(string? str) + { + // Simple heuristic to identify likely ISO date time strings + return str != null + && (str.Contains("T") && (str.EndsWith("Z") || str.Contains("+") || str.Contains("-"))); + } + + private bool AreEquivalentDateTimeStrings(string str1, string str2) + { + // Try to parse both as DateTime + if (DateTime.TryParse(str1, out DateTime dt1) && DateTime.TryParse(str2, out DateTime dt2)) + { + return dt1 == dt2; + } + + return false; + } + + private bool CompareJsonObjects(JsonElement x, JsonElement y, string path) + { + // Create dictionaries for both JSON objects + var xProps = new Dictionary(); + var yProps = new Dictionary(); + + foreach (var prop in x.EnumerateObject()) + xProps[prop.Name] = prop.Value; + + foreach (var prop in y.EnumerateObject()) + yProps[prop.Name] = prop.Value; + + // Check if all properties in x exist in y + foreach (var key in xProps.Keys) + { + if (!yProps.ContainsKey(key)) + { + _failurePath = $"{path}: Missing property '{key}'"; + return false; + } + } + + // Check if y has extra properties + foreach (var key in yProps.Keys) + { + if (!xProps.ContainsKey(key)) + { + _failurePath = $"{path}: Unexpected property '{key}'"; + return false; + } + } + + // Compare each property value + foreach (var key in xProps.Keys) + { + var propPath = string.IsNullOrEmpty(path) ? key : $"{path}.{key}"; + if (!CompareJsonElements(xProps[key], yProps[key], propPath)) + { + return false; + } + } + + return true; + } + + private bool CompareJsonArraysInOrder(JsonElement x, JsonElement y, string path) + { + var xArray = x.EnumerateArray(); + var yArray = y.EnumerateArray(); + + // Count x elements + var xCount = 0; + var xElements = new List(); + foreach (var item in xArray) + { + xElements.Add(item); + xCount++; + } + + // Count y elements + var yCount = 0; + var yElements = new List(); + foreach (var item in yArray) + { + yElements.Add(item); + yCount++; + } + + // Check if counts match + if (xCount != yCount) + { + _failurePath = $"{path}: Expected {xCount} items but found {yCount}"; + return false; + } + + // Compare elements in order + for (var i = 0; i < xCount; i++) + { + var itemPath = $"{path}[{i}]"; + if (!CompareJsonElements(xElements[i], yElements[i], itemPath)) + { + return false; + } + } + + return true; + } + + /// + public override string ToString() + { + if (!string.IsNullOrEmpty(_failurePath)) + { + return $"JSON comparison failed at {_failurePath}"; + } + + return "JsonElementEqualityComparer"; + } +} diff --git a/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Utils/NUnitExtensions.cs b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Utils/NUnitExtensions.cs new file mode 100644 index 000000000000..426df1245388 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Utils/NUnitExtensions.cs @@ -0,0 +1,28 @@ +using NUnit.Framework.Constraints; + +namespace NUnit.Framework; + +/// +/// Extensions for NUnit constraints. +/// +public static class NUnitExtensions +{ + /// + /// Modifies the EqualConstraint to use our own set of default comparers. + /// + /// + /// + public static EqualConstraint UsingDefaults(this EqualConstraint constraint) => + constraint + .UsingPropertiesComparer() + .UsingReadOnlyMemoryComparer() + .UsingReadOnlyMemoryComparer() + .UsingReadOnlyMemoryComparer() + .UsingReadOnlyMemoryComparer() + .UsingReadOnlyMemoryComparer() + .UsingReadOnlyMemoryComparer() + .UsingReadOnlyMemoryComparer() + .UsingReadOnlyMemoryComparer() + .UsingOneOfComparer() + .UsingJsonElementComparer(); +} diff --git a/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Utils/OneOfComparer.cs b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Utils/OneOfComparer.cs new file mode 100644 index 000000000000..0c975b471ff3 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Utils/OneOfComparer.cs @@ -0,0 +1,43 @@ +using NUnit.Framework.Constraints; +using OneOf; + +namespace NUnit.Framework; + +/// +/// Extensions for EqualConstraint to handle OneOf values. +/// +public static class EqualConstraintExtensions +{ + /// + /// Modifies the EqualConstraint to handle OneOf instances by comparing their inner values. + /// This works alongside other comparison modifiers like UsingPropertiesComparer. + /// + /// The EqualConstraint to modify. + /// The same constraint instance for method chaining. + public static EqualConstraint UsingOneOfComparer(this EqualConstraint constraint) + { + // Register a comparer factory for IOneOf types + constraint.Using( + (x, y) => + { + // ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract + if (x.Value is null && y.Value is null) + { + return true; + } + + if (x.Value is null) + { + return false; + } + + var propertiesComparer = new NUnitEqualityComparer(); + var tolerance = Tolerance.Default; + propertiesComparer.CompareProperties = true; + return propertiesComparer.AreEqual(x.Value, y.Value, ref tolerance); + } + ); + + return constraint; + } +} diff --git a/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Utils/ReadOnlyMemoryComparer.cs b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Utils/ReadOnlyMemoryComparer.cs new file mode 100644 index 000000000000..fc0b595a5e54 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Utils/ReadOnlyMemoryComparer.cs @@ -0,0 +1,87 @@ +using NUnit.Framework.Constraints; + +namespace NUnit.Framework; + +/// +/// Extensions for NUnit constraints. +/// +public static class ReadOnlyMemoryComparerExtensions +{ + /// + /// Extension method for comparing ReadOnlyMemory<T> in NUnit tests. + /// + /// The type of elements in the ReadOnlyMemory. + /// The Is.EqualTo() constraint instance. + /// A constraint that can compare ReadOnlyMemory<T>. + public static EqualConstraint UsingReadOnlyMemoryComparer(this EqualConstraint constraint) + where T : IComparable + { + return constraint.Using(new ReadOnlyMemoryComparer()); + } +} + +/// +/// Comparer for ReadOnlyMemory<T>. Compares sequences by value. +/// +/// +/// The type of elements in the ReadOnlyMemory. +/// +public class ReadOnlyMemoryComparer : IComparer> + where T : IComparable +{ + /// + public int Compare(ReadOnlyMemory x, ReadOnlyMemory y) + { + // Check if sequences are equal + var xSpan = x.Span; + var ySpan = y.Span; + + // Optimized case for IEquatable implementations + if (typeof(IEquatable).IsAssignableFrom(typeof(T))) + { + var areEqual = xSpan.SequenceEqual(ySpan); + if (areEqual) + { + return 0; // Sequences are equal + } + } + else + { + // Manual equality check for non-IEquatable types + if (xSpan.Length == ySpan.Length) + { + var areEqual = true; + for (var i = 0; i < xSpan.Length; i++) + { + if (!EqualityComparer.Default.Equals(xSpan[i], ySpan[i])) + { + areEqual = false; + break; + } + } + + if (areEqual) + { + return 0; // Sequences are equal + } + } + } + + // For non-equal sequences, we need to return a consistent ordering + // First compare lengths + if (x.Length != y.Length) + return x.Length.CompareTo(y.Length); + + // Same length but different content - compare first differing element + for (var i = 0; i < x.Length; i++) + { + if (!EqualityComparer.Default.Equals(xSpan[i], ySpan[i])) + { + return xSpan[i].CompareTo(ySpan[i]); + } + } + + // Should never reach here if not equal + return 0; + } +} diff --git a/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Auth/TokenResponse.cs b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Auth/TokenResponse.cs new file mode 100644 index 000000000000..369a4e8a5cc4 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Auth/TokenResponse.cs @@ -0,0 +1,40 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using SeedInferredAuthImplicitApiKey.Core; + +namespace SeedInferredAuthImplicitApiKey; + +/// +/// An auth token response. +/// +[Serializable] +public record TokenResponse : IJsonOnDeserialized +{ + [JsonExtensionData] + private readonly IDictionary _extensionData = + new Dictionary(); + + [JsonPropertyName("access_token")] + public required string AccessToken { get; set; } + + [JsonPropertyName("token_type")] + public required string TokenType { get; set; } + + [JsonPropertyName("expires_in")] + public required int ExpiresIn { get; set; } + + [JsonPropertyName("scope")] + public string? Scope { get; set; } + + [JsonIgnore] + public ReadOnlyAdditionalProperties AdditionalProperties { get; private set; } = new(); + + void IJsonOnDeserialized.OnDeserialized() => + AdditionalProperties.CopyFromExtensionData(_extensionData); + + /// + public override string ToString() + { + return JsonUtils.Serialize(this); + } +} diff --git a/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/CollectionItemSerializer.cs b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/CollectionItemSerializer.cs new file mode 100644 index 000000000000..eb701f5d1910 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/CollectionItemSerializer.cs @@ -0,0 +1,89 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace SeedInferredAuthImplicitApiKey.Core; + +/// +/// Json collection converter. +/// +/// Type of item to convert. +/// Converter to use for individual items. +internal class CollectionItemSerializer + : JsonConverter> + where TConverterType : JsonConverter +{ + /// + /// Reads a json string and deserializes it into an object. + /// + /// Json reader. + /// Type to convert. + /// Serializer options. + /// Created object. + public override IEnumerable? Read( + ref Utf8JsonReader reader, + global::System.Type typeToConvert, + JsonSerializerOptions options + ) + { + if (reader.TokenType == JsonTokenType.Null) + { + return default; + } + + var jsonSerializerOptions = new JsonSerializerOptions(options); + jsonSerializerOptions.Converters.Clear(); + jsonSerializerOptions.Converters.Add(Activator.CreateInstance()); + + var returnValue = new List(); + + while (reader.TokenType != JsonTokenType.EndArray) + { + if (reader.TokenType != JsonTokenType.StartArray) + { + var item = (TDatatype)( + JsonSerializer.Deserialize(ref reader, typeof(TDatatype), jsonSerializerOptions) + ?? throw new global::System.Exception( + $"Failed to deserialize collection item of type {typeof(TDatatype)}" + ) + ); + returnValue.Add(item); + } + + reader.Read(); + } + + return returnValue; + } + + /// + /// Writes a json string. + /// + /// Json writer. + /// Value to write. + /// Serializer options. + public override void Write( + Utf8JsonWriter writer, + IEnumerable? value, + JsonSerializerOptions options + ) + { + if (value == null) + { + writer.WriteNullValue(); + return; + } + + var jsonSerializerOptions = new JsonSerializerOptions(options); + jsonSerializerOptions.Converters.Clear(); + jsonSerializerOptions.Converters.Add(Activator.CreateInstance()); + + writer.WriteStartArray(); + + foreach (var data in value) + { + JsonSerializer.Serialize(writer, data, jsonSerializerOptions); + } + + writer.WriteEndArray(); + } +} diff --git a/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/Constants.cs b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/Constants.cs new file mode 100644 index 000000000000..46133ca0c122 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/Constants.cs @@ -0,0 +1,7 @@ +namespace SeedInferredAuthImplicitApiKey.Core; + +internal static class Constants +{ + public const string DateTimeFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.fffK"; + public const string DateFormat = "yyyy-MM-dd"; +} diff --git a/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/DateOnlyConverter.cs b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/DateOnlyConverter.cs new file mode 100644 index 000000000000..6944ed44d437 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/DateOnlyConverter.cs @@ -0,0 +1,747 @@ +// ReSharper disable All +#pragma warning disable + +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using global::System.Diagnostics; +using global::System.Diagnostics.CodeAnalysis; +using global::System.Globalization; +using global::System.Runtime.CompilerServices; +using global::System.Runtime.InteropServices; +using global::System.Text.Json; +using global::System.Text.Json.Serialization; + +// ReSharper disable SuggestVarOrType_SimpleTypes +// ReSharper disable SuggestVarOrType_BuiltInTypes + +namespace SeedInferredAuthImplicitApiKey.Core +{ + /// + /// Custom converter for handling the data type with the System.Text.Json library. + /// + /// + /// This class backported from: + /// + /// System.Text.Json.Serialization.Converters.DateOnlyConverter + /// + public sealed class DateOnlyConverter : JsonConverter + { + private const int FormatLength = 10; // YYYY-MM-DD + + private const int MaxEscapedFormatLength = + FormatLength * JsonConstants.MaxExpansionFactorWhileEscaping; + + /// + public override DateOnly Read( + ref Utf8JsonReader reader, + global::System.Type typeToConvert, + JsonSerializerOptions options + ) + { + if (reader.TokenType != JsonTokenType.String) + { + ThrowHelper.ThrowInvalidOperationException_ExpectedString(reader.TokenType); + } + + return ReadCore(ref reader); + } + + /// + public override DateOnly ReadAsPropertyName( + ref Utf8JsonReader reader, + global::System.Type typeToConvert, + JsonSerializerOptions options + ) + { + Debug.Assert(reader.TokenType == JsonTokenType.PropertyName); + return ReadCore(ref reader); + } + + private static DateOnly ReadCore(ref Utf8JsonReader reader) + { + if ( + !JsonHelpers.IsInRangeInclusive( + reader.ValueLength(), + FormatLength, + MaxEscapedFormatLength + ) + ) + { + ThrowHelper.ThrowFormatException(DataType.DateOnly); + } + + scoped ReadOnlySpan source; + if (!reader.HasValueSequence && !reader.ValueIsEscaped) + { + source = reader.ValueSpan; + } + else + { + Span stackSpan = stackalloc byte[MaxEscapedFormatLength]; + int bytesWritten = reader.CopyString(stackSpan); + source = stackSpan.Slice(0, bytesWritten); + } + + if (!JsonHelpers.TryParseAsIso(source, out DateOnly value)) + { + ThrowHelper.ThrowFormatException(DataType.DateOnly); + } + + return value; + } + + /// + public override void Write( + Utf8JsonWriter writer, + DateOnly value, + JsonSerializerOptions options + ) + { +#if NET8_0_OR_GREATER + Span buffer = stackalloc byte[FormatLength]; +#else + Span buffer = stackalloc char[FormatLength]; +#endif + // ReSharper disable once RedundantAssignment + bool formattedSuccessfully = value.TryFormat( + buffer, + out int charsWritten, + "O".AsSpan(), + CultureInfo.InvariantCulture + ); + Debug.Assert(formattedSuccessfully && charsWritten == FormatLength); + writer.WriteStringValue(buffer); + } + + /// + public override void WriteAsPropertyName( + Utf8JsonWriter writer, + DateOnly value, + JsonSerializerOptions options + ) + { +#if NET8_0_OR_GREATER + Span buffer = stackalloc byte[FormatLength]; +#else + Span buffer = stackalloc char[FormatLength]; +#endif + // ReSharper disable once RedundantAssignment + bool formattedSuccessfully = value.TryFormat( + buffer, + out int charsWritten, + "O".AsSpan(), + CultureInfo.InvariantCulture + ); + Debug.Assert(formattedSuccessfully && charsWritten == FormatLength); + writer.WritePropertyName(buffer); + } + } + + internal static class JsonConstants + { + // The maximum number of fraction digits the Json DateTime parser allows + public const int DateTimeParseNumFractionDigits = 16; + + // In the worst case, an ASCII character represented as a single utf-8 byte could expand 6x when escaped. + public const int MaxExpansionFactorWhileEscaping = 6; + + // The largest fraction expressible by TimeSpan and DateTime formats + public const int MaxDateTimeFraction = 9_999_999; + + // TimeSpan and DateTime formats allow exactly up to many digits for specifying the fraction after the seconds. + public const int DateTimeNumFractionDigits = 7; + + public const byte UtcOffsetToken = (byte)'Z'; + + public const byte TimePrefix = (byte)'T'; + + public const byte Period = (byte)'.'; + + public const byte Hyphen = (byte)'-'; + + public const byte Colon = (byte)':'; + + public const byte Plus = (byte)'+'; + } + + // ReSharper disable SuggestVarOrType_Elsewhere + // ReSharper disable SuggestVarOrType_SimpleTypes + // ReSharper disable SuggestVarOrType_BuiltInTypes + + internal static class JsonHelpers + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsInRangeInclusive(int value, int lowerBound, int upperBound) => + (uint)(value - lowerBound) <= (uint)(upperBound - lowerBound); + + public static bool IsDigit(byte value) => (uint)(value - '0') <= '9' - '0'; + + [StructLayout(LayoutKind.Auto)] + private struct DateTimeParseData + { + public int Year; + public int Month; + public int Day; + public bool IsCalendarDateOnly; + public int Hour; + public int Minute; + public int Second; + public int Fraction; // This value should never be greater than 9_999_999. + public int OffsetHours; + public int OffsetMinutes; + + // ReSharper disable once NotAccessedField.Local + public byte OffsetToken; + } + + public static bool TryParseAsIso(ReadOnlySpan source, out DateOnly value) + { + if ( + TryParseDateTimeOffset(source, out DateTimeParseData parseData) + && parseData.IsCalendarDateOnly + && TryCreateDateTime(parseData, DateTimeKind.Unspecified, out DateTime dateTime) + ) + { + value = DateOnly.FromDateTime(dateTime); + return true; + } + + value = default; + return false; + } + + /// + /// ISO 8601 date time parser (ISO 8601-1:2019). + /// + /// The date/time to parse in UTF-8 format. + /// The parsed for the given . + /// + /// Supports extended calendar date (5.2.2.1) and complete (5.4.2.1) calendar date/time of day + /// representations with optional specification of seconds and fractional seconds. + /// + /// Times can be explicitly specified as UTC ("Z" - 5.3.3) or offsets from UTC ("+/-hh:mm" 5.3.4.2). + /// If unspecified they are considered to be local per spec. + /// + /// Examples: (TZD is either "Z" or hh:mm offset from UTC) + /// + /// YYYY-MM-DD (e.g. 1997-07-16) + /// YYYY-MM-DDThh:mm (e.g. 1997-07-16T19:20) + /// YYYY-MM-DDThh:mm:ss (e.g. 1997-07-16T19:20:30) + /// YYYY-MM-DDThh:mm:ss.s (e.g. 1997-07-16T19:20:30.45) + /// YYYY-MM-DDThh:mmTZD (e.g. 1997-07-16T19:20+01:00) + /// YYYY-MM-DDThh:mm:ssTZD (e.g. 1997-07-16T19:20:3001:00) + /// YYYY-MM-DDThh:mm:ss.sTZD (e.g. 1997-07-16T19:20:30.45Z) + /// + /// Generally speaking we always require the "extended" option when one exists (3.1.3.5). + /// The extended variants have separator characters between components ('-', ':', '.', etc.). + /// Spaces are not permitted. + /// + /// "true" if successfully parsed. + private static bool TryParseDateTimeOffset( + ReadOnlySpan source, + out DateTimeParseData parseData + ) + { + parseData = default; + + // too short datetime + Debug.Assert(source.Length >= 10); + + // Parse the calendar date + // ----------------------- + // ISO 8601-1:2019 5.2.2.1b "Calendar date complete extended format" + // [dateX] = [year]["-"][month]["-"][day] + // [year] = [YYYY] [0000 - 9999] (4.3.2) + // [month] = [MM] [01 - 12] (4.3.3) + // [day] = [DD] [01 - 28, 29, 30, 31] (4.3.4) + // + // Note: 5.2.2.2 "Representations with reduced precision" allows for + // just [year]["-"][month] (a) and just [year] (b), but we currently + // don't permit it. + + { + uint digit1 = source[0] - (uint)'0'; + uint digit2 = source[1] - (uint)'0'; + uint digit3 = source[2] - (uint)'0'; + uint digit4 = source[3] - (uint)'0'; + + if (digit1 > 9 || digit2 > 9 || digit3 > 9 || digit4 > 9) + { + return false; + } + + parseData.Year = (int)(digit1 * 1000 + digit2 * 100 + digit3 * 10 + digit4); + } + + if ( + source[4] != JsonConstants.Hyphen + || !TryGetNextTwoDigits(source.Slice(start: 5, length: 2), ref parseData.Month) + || source[7] != JsonConstants.Hyphen + || !TryGetNextTwoDigits(source.Slice(start: 8, length: 2), ref parseData.Day) + ) + { + return false; + } + + // We now have YYYY-MM-DD [dateX] + // ReSharper disable once ConvertIfStatementToSwitchStatement + if (source.Length == 10) + { + parseData.IsCalendarDateOnly = true; + return true; + } + + // Parse the time of day + // --------------------- + // + // ISO 8601-1:2019 5.3.1.2b "Local time of day complete extended format" + // [timeX] = ["T"][hour][":"][min][":"][sec] + // [hour] = [hh] [00 - 23] (4.3.8a) + // [minute] = [mm] [00 - 59] (4.3.9a) + // [sec] = [ss] [00 - 59, 60 with a leap second] (4.3.10a) + // + // ISO 8601-1:2019 5.3.3 "UTC of day" + // [timeX]["Z"] + // + // ISO 8601-1:2019 5.3.4.2 "Local time of day with the time shift between + // local timescale and UTC" (Extended format) + // + // [shiftX] = ["+"|"-"][hour][":"][min] + // + // Notes: + // + // "T" is optional per spec, but _only_ when times are used alone. In our + // case, we're reading out a complete date & time and as such require "T". + // (5.4.2.1b). + // + // For [timeX] We allow seconds to be omitted per 5.3.1.3a "Representations + // with reduced precision". 5.3.1.3b allows just specifying the hour, but + // we currently don't permit this. + // + // Decimal fractions are allowed for hours, minutes and seconds (5.3.14). + // We only allow fractions for seconds currently. Lower order components + // can't follow, i.e. you can have T23.3, but not T23.3:04. There must be + // one digit, but the max number of digits is implementation defined. We + // currently allow up to 16 digits of fractional seconds only. While we + // support 16 fractional digits we only parse the first seven, anything + // past that is considered a zero. This is to stay compatible with the + // DateTime implementation which is limited to this resolution. + + if (source.Length < 16) + { + // Source does not have enough characters for YYYY-MM-DDThh:mm + return false; + } + + // Parse THH:MM (e.g. "T10:32") + if ( + source[10] != JsonConstants.TimePrefix + || source[13] != JsonConstants.Colon + || !TryGetNextTwoDigits(source.Slice(start: 11, length: 2), ref parseData.Hour) + || !TryGetNextTwoDigits(source.Slice(start: 14, length: 2), ref parseData.Minute) + ) + { + return false; + } + + // We now have YYYY-MM-DDThh:mm + Debug.Assert(source.Length >= 16); + if (source.Length == 16) + { + return true; + } + + byte curByte = source[16]; + int sourceIndex = 17; + + // Either a TZD ['Z'|'+'|'-'] or a seconds separator [':'] is valid at this point + switch (curByte) + { + case JsonConstants.UtcOffsetToken: + parseData.OffsetToken = JsonConstants.UtcOffsetToken; + return sourceIndex == source.Length; + case JsonConstants.Plus: + case JsonConstants.Hyphen: + parseData.OffsetToken = curByte; + return ParseOffset(ref parseData, source.Slice(sourceIndex)); + case JsonConstants.Colon: + break; + default: + return false; + } + + // Try reading the seconds + if ( + source.Length < 19 + || !TryGetNextTwoDigits(source.Slice(start: 17, length: 2), ref parseData.Second) + ) + { + return false; + } + + // We now have YYYY-MM-DDThh:mm:ss + Debug.Assert(source.Length >= 19); + if (source.Length == 19) + { + return true; + } + + curByte = source[19]; + sourceIndex = 20; + + // Either a TZD ['Z'|'+'|'-'] or a seconds decimal fraction separator ['.'] is valid at this point + switch (curByte) + { + case JsonConstants.UtcOffsetToken: + parseData.OffsetToken = JsonConstants.UtcOffsetToken; + return sourceIndex == source.Length; + case JsonConstants.Plus: + case JsonConstants.Hyphen: + parseData.OffsetToken = curByte; + return ParseOffset(ref parseData, source.Slice(sourceIndex)); + case JsonConstants.Period: + break; + default: + return false; + } + + // Source does not have enough characters for second fractions (i.e. ".s") + // YYYY-MM-DDThh:mm:ss.s + if (source.Length < 21) + { + return false; + } + + // Parse fraction. This value should never be greater than 9_999_999 + int numDigitsRead = 0; + int fractionEnd = Math.Min( + sourceIndex + JsonConstants.DateTimeParseNumFractionDigits, + source.Length + ); + + while (sourceIndex < fractionEnd && IsDigit(curByte = source[sourceIndex])) + { + if (numDigitsRead < JsonConstants.DateTimeNumFractionDigits) + { + parseData.Fraction = parseData.Fraction * 10 + (int)(curByte - (uint)'0'); + numDigitsRead++; + } + + sourceIndex++; + } + + if (parseData.Fraction != 0) + { + while (numDigitsRead < JsonConstants.DateTimeNumFractionDigits) + { + parseData.Fraction *= 10; + numDigitsRead++; + } + } + + // We now have YYYY-MM-DDThh:mm:ss.s + Debug.Assert(sourceIndex <= source.Length); + if (sourceIndex == source.Length) + { + return true; + } + + curByte = source[sourceIndex++]; + + // TZD ['Z'|'+'|'-'] is valid at this point + switch (curByte) + { + case JsonConstants.UtcOffsetToken: + parseData.OffsetToken = JsonConstants.UtcOffsetToken; + return sourceIndex == source.Length; + case JsonConstants.Plus: + case JsonConstants.Hyphen: + parseData.OffsetToken = curByte; + return ParseOffset(ref parseData, source.Slice(sourceIndex)); + default: + return false; + } + + static bool ParseOffset(ref DateTimeParseData parseData, ReadOnlySpan offsetData) + { + // Parse the hours for the offset + if ( + offsetData.Length < 2 + || !TryGetNextTwoDigits(offsetData.Slice(0, 2), ref parseData.OffsetHours) + ) + { + return false; + } + + // We now have YYYY-MM-DDThh:mm:ss.s+|-hh + + if (offsetData.Length == 2) + { + // Just hours offset specified + return true; + } + + // Ensure we have enough for ":mm" + return offsetData.Length == 5 + && offsetData[2] == JsonConstants.Colon + && TryGetNextTwoDigits(offsetData.Slice(3), ref parseData.OffsetMinutes); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + // ReSharper disable once RedundantAssignment + private static bool TryGetNextTwoDigits(ReadOnlySpan source, ref int value) + { + Debug.Assert(source.Length == 2); + + uint digit1 = source[0] - (uint)'0'; + uint digit2 = source[1] - (uint)'0'; + + if (digit1 > 9 || digit2 > 9) + { + value = 0; + return false; + } + + value = (int)(digit1 * 10 + digit2); + return true; + } + + // The following methods are borrowed verbatim from src/Common/src/CoreLib/System/Buffers/Text/Utf8Parser/Utf8Parser.Date.Helpers.cs + + /// + /// Overflow-safe DateTime factory. + /// + private static bool TryCreateDateTime( + DateTimeParseData parseData, + DateTimeKind kind, + out DateTime value + ) + { + if (parseData.Year == 0) + { + value = default; + return false; + } + + Debug.Assert(parseData.Year <= 9999); // All of our callers to date parse the year from fixed 4-digit fields so this value is trusted. + + if ((uint)parseData.Month - 1 >= 12) + { + value = default; + return false; + } + + uint dayMinusOne = (uint)parseData.Day - 1; + if ( + dayMinusOne >= 28 + && dayMinusOne >= DateTime.DaysInMonth(parseData.Year, parseData.Month) + ) + { + value = default; + return false; + } + + if ((uint)parseData.Hour > 23) + { + value = default; + return false; + } + + if ((uint)parseData.Minute > 59) + { + value = default; + return false; + } + + // This needs to allow leap seconds when appropriate. + // See https://github.com/dotnet/runtime/issues/30135. + if ((uint)parseData.Second > 59) + { + value = default; + return false; + } + + Debug.Assert(parseData.Fraction is >= 0 and <= JsonConstants.MaxDateTimeFraction); // All of our callers to date parse the fraction from fixed 7-digit fields so this value is trusted. + + ReadOnlySpan days = DateTime.IsLeapYear(parseData.Year) + ? DaysToMonth366 + : DaysToMonth365; + int yearMinusOne = parseData.Year - 1; + int totalDays = + yearMinusOne * 365 + + yearMinusOne / 4 + - yearMinusOne / 100 + + yearMinusOne / 400 + + days[parseData.Month - 1] + + parseData.Day + - 1; + long ticks = totalDays * TimeSpan.TicksPerDay; + int totalSeconds = parseData.Hour * 3600 + parseData.Minute * 60 + parseData.Second; + ticks += totalSeconds * TimeSpan.TicksPerSecond; + ticks += parseData.Fraction; + value = new DateTime(ticks: ticks, kind: kind); + return true; + } + + private static ReadOnlySpan DaysToMonth365 => + [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365]; + private static ReadOnlySpan DaysToMonth366 => + [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366]; + } + + internal static class ThrowHelper + { + private const string ExceptionSourceValueToRethrowAsJsonException = + "System.Text.Json.Rethrowable"; + + [DoesNotReturn] + public static void ThrowInvalidOperationException_ExpectedString(JsonTokenType tokenType) + { + throw GetInvalidOperationException("string", tokenType); + } + + public static void ThrowFormatException(DataType dataType) + { + throw new FormatException(SR.Format(SR.UnsupportedFormat, dataType)) + { + Source = ExceptionSourceValueToRethrowAsJsonException, + }; + } + + private static global::System.Exception GetInvalidOperationException( + string message, + JsonTokenType tokenType + ) + { + return GetInvalidOperationException(SR.Format(SR.InvalidCast, tokenType, message)); + } + + private static InvalidOperationException GetInvalidOperationException(string message) + { + return new InvalidOperationException(message) + { + Source = ExceptionSourceValueToRethrowAsJsonException, + }; + } + } + + internal static class Utf8JsonReaderExtensions + { + internal static int ValueLength(this Utf8JsonReader reader) => + reader.HasValueSequence + ? checked((int)reader.ValueSequence.Length) + : reader.ValueSpan.Length; + } + + internal enum DataType + { + TimeOnly, + DateOnly, + } + + [SuppressMessage("ReSharper", "InconsistentNaming")] + internal static class SR + { + private static readonly bool s_usingResourceKeys = + AppContext.TryGetSwitch( + "System.Resources.UseSystemResourceKeys", + out bool usingResourceKeys + ) && usingResourceKeys; + + public static string UnsupportedFormat => Strings.UnsupportedFormat; + + public static string InvalidCast => Strings.InvalidCast; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static string Format(string resourceFormat, object? p1) => + s_usingResourceKeys + ? string.Join(", ", resourceFormat, p1) + : string.Format(resourceFormat, p1); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static string Format(string resourceFormat, object? p1, object? p2) => + s_usingResourceKeys + ? string.Join(", ", resourceFormat, p1, p2) + : string.Format(resourceFormat, p1, p2); + } + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute( + "System.Resources.Tools.StronglyTypedResourceBuilder", + "17.0.0.0" + )] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Strings + { + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute( + "Microsoft.Performance", + "CA1811:AvoidUncalledPrivateCode" + )] + internal Strings() { } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute( + global::System.ComponentModel.EditorBrowsableState.Advanced + )] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if (object.ReferenceEquals(resourceMan, null)) + { + global::System.Resources.ResourceManager temp = + new global::System.Resources.ResourceManager( + "System.Text.Json.Resources.Strings", + typeof(Strings).Assembly + ); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute( + global::System.ComponentModel.EditorBrowsableState.Advanced + )] + internal static global::System.Globalization.CultureInfo Culture + { + get { return resourceCulture; } + set { resourceCulture = value; } + } + + /// + /// Looks up a localized string similar to Cannot get the value of a token type '{0}' as a {1}.. + /// + internal static string InvalidCast + { + get { return ResourceManager.GetString("InvalidCast", resourceCulture); } + } + + /// + /// Looks up a localized string similar to The JSON value is not in a supported {0} format.. + /// + internal static string UnsupportedFormat + { + get { return ResourceManager.GetString("UnsupportedFormat", resourceCulture); } + } + } +} diff --git a/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/DateTimeSerializer.cs b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/DateTimeSerializer.cs new file mode 100644 index 000000000000..da2b193d24af --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/DateTimeSerializer.cs @@ -0,0 +1,22 @@ +using System.Globalization; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace SeedInferredAuthImplicitApiKey.Core; + +internal class DateTimeSerializer : JsonConverter +{ + public override DateTime Read( + ref Utf8JsonReader reader, + global::System.Type typeToConvert, + JsonSerializerOptions options + ) + { + return DateTime.Parse(reader.GetString()!, null, DateTimeStyles.RoundtripKind); + } + + public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.ToString(Constants.DateTimeFormat)); + } +} diff --git a/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/JsonAccessAttribute.cs b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/JsonAccessAttribute.cs new file mode 100644 index 000000000000..5b368983daa3 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/JsonAccessAttribute.cs @@ -0,0 +1,15 @@ +namespace SeedInferredAuthImplicitApiKey.Core; + +[global::System.AttributeUsage( + global::System.AttributeTargets.Property | global::System.AttributeTargets.Field +)] +internal class JsonAccessAttribute(JsonAccessType accessType) : global::System.Attribute +{ + internal JsonAccessType AccessType { get; init; } = accessType; +} + +internal enum JsonAccessType +{ + ReadOnly, + WriteOnly, +} diff --git a/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/JsonConfiguration.cs b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/JsonConfiguration.cs new file mode 100644 index 000000000000..8a773147e702 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/JsonConfiguration.cs @@ -0,0 +1,180 @@ +using global::System.Reflection; +using global::System.Text.Json; +using global::System.Text.Json.Nodes; +using global::System.Text.Json.Serialization; +using global::System.Text.Json.Serialization.Metadata; + +namespace SeedInferredAuthImplicitApiKey.Core; + +internal static partial class JsonOptions +{ + internal static readonly JsonSerializerOptions JsonSerializerOptions; + + static JsonOptions() + { + var options = new JsonSerializerOptions + { + Converters = { new DateTimeSerializer(), +#if USE_PORTABLE_DATE_ONLY + new DateOnlyConverter(), +#endif + new OneOfSerializer() }, +#if DEBUG + WriteIndented = true, +#endif + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + TypeInfoResolver = new DefaultJsonTypeInfoResolver + { + Modifiers = + { + static typeInfo => + { + if (typeInfo.Kind != JsonTypeInfoKind.Object) + return; + + foreach (var propertyInfo in typeInfo.Properties) + { + var jsonAccessAttribute = propertyInfo + .AttributeProvider?.GetCustomAttributes( + typeof(JsonAccessAttribute), + true + ) + .OfType() + .FirstOrDefault(); + + if (jsonAccessAttribute != null) + { + propertyInfo.IsRequired = false; + switch (jsonAccessAttribute.AccessType) + { + case JsonAccessType.ReadOnly: + propertyInfo.ShouldSerialize = (_, _) => false; + break; + case JsonAccessType.WriteOnly: + propertyInfo.Set = null; + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + + var jsonIgnoreAttribute = propertyInfo + .AttributeProvider?.GetCustomAttributes( + typeof(JsonIgnoreAttribute), + true + ) + .OfType() + .FirstOrDefault(); + + if (jsonIgnoreAttribute is not null) + { + propertyInfo.IsRequired = false; + } + } + + if ( + typeInfo.Kind == JsonTypeInfoKind.Object + && typeInfo.Properties.All(prop => !prop.IsExtensionData) + ) + { + var extensionProp = typeInfo + .Type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic) + .FirstOrDefault(prop => + prop.GetCustomAttribute() != null + ); + + if (extensionProp is not null) + { + var jsonPropertyInfo = typeInfo.CreateJsonPropertyInfo( + extensionProp.FieldType, + extensionProp.Name + ); + jsonPropertyInfo.Get = extensionProp.GetValue; + jsonPropertyInfo.Set = extensionProp.SetValue; + jsonPropertyInfo.IsExtensionData = true; + typeInfo.Properties.Add(jsonPropertyInfo); + } + } + }, + }, + }, + }; + ConfigureJsonSerializerOptions(options); + JsonSerializerOptions = options; + } + + static partial void ConfigureJsonSerializerOptions(JsonSerializerOptions defaultOptions); +} + +internal static class JsonUtils +{ + internal static string Serialize(T obj) => + JsonSerializer.Serialize(obj, JsonOptions.JsonSerializerOptions); + + internal static JsonElement SerializeToElement(T obj) => + JsonSerializer.SerializeToElement(obj, JsonOptions.JsonSerializerOptions); + + internal static JsonDocument SerializeToDocument(T obj) => + JsonSerializer.SerializeToDocument(obj, JsonOptions.JsonSerializerOptions); + + internal static JsonNode? SerializeToNode(T obj) => + JsonSerializer.SerializeToNode(obj, JsonOptions.JsonSerializerOptions); + + internal static byte[] SerializeToUtf8Bytes(T obj) => + JsonSerializer.SerializeToUtf8Bytes(obj, JsonOptions.JsonSerializerOptions); + + internal static string SerializeWithAdditionalProperties( + T obj, + object? additionalProperties = null + ) + { + if (additionalProperties == null) + { + return Serialize(obj); + } + var additionalPropertiesJsonNode = SerializeToNode(additionalProperties); + if (additionalPropertiesJsonNode is not JsonObject additionalPropertiesJsonObject) + { + throw new InvalidOperationException( + "The additional properties must serialize to a JSON object." + ); + } + var jsonNode = SerializeToNode(obj); + if (jsonNode is not JsonObject jsonObject) + { + throw new InvalidOperationException( + "The serialized object must be a JSON object to add properties." + ); + } + MergeJsonObjects(jsonObject, additionalPropertiesJsonObject); + return jsonObject.ToJsonString(JsonOptions.JsonSerializerOptions); + } + + private static void MergeJsonObjects(JsonObject baseObject, JsonObject overrideObject) + { + foreach (var property in overrideObject) + { + if (!baseObject.TryGetPropertyValue(property.Key, out JsonNode? existingValue)) + { + baseObject[property.Key] = + property.Value != null ? JsonNode.Parse(property.Value.ToJsonString()) : null; + continue; + } + if ( + existingValue is JsonObject nestedBaseObject + && property.Value is JsonObject nestedOverrideObject + ) + { + // If both values are objects, recursively merge them. + MergeJsonObjects(nestedBaseObject, nestedOverrideObject); + continue; + } + // Otherwise, the overrideObject takes precedence. + baseObject[property.Key] = + property.Value != null ? JsonNode.Parse(property.Value.ToJsonString()) : null; + } + } + + internal static T Deserialize(string json) => + JsonSerializer.Deserialize(json, JsonOptions.JsonSerializerOptions)!; +} diff --git a/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/OneOfSerializer.cs b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/OneOfSerializer.cs new file mode 100644 index 000000000000..e96243b11e3e --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/OneOfSerializer.cs @@ -0,0 +1,91 @@ +using System.Reflection; +using System.Text.Json; +using System.Text.Json.Serialization; +using OneOf; + +namespace SeedInferredAuthImplicitApiKey.Core; + +internal class OneOfSerializer : JsonConverter +{ + public override IOneOf? Read( + ref Utf8JsonReader reader, + global::System.Type typeToConvert, + JsonSerializerOptions options + ) + { + if (reader.TokenType is JsonTokenType.Null) + return default; + + foreach (var (type, cast) in GetOneOfTypes(typeToConvert)) + { + try + { + var readerCopy = reader; + var result = JsonSerializer.Deserialize(ref readerCopy, type, options); + reader.Skip(); + return (IOneOf)cast.Invoke(null, [result])!; + } + catch (JsonException) { } + } + + throw new JsonException( + $"Cannot deserialize into one of the supported types for {typeToConvert}" + ); + } + + public override void Write(Utf8JsonWriter writer, IOneOf value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.Value, options); + } + + private static (global::System.Type type, MethodInfo cast)[] GetOneOfTypes( + global::System.Type typeToConvert + ) + { + var type = typeToConvert; + if (Nullable.GetUnderlyingType(type) is { } underlyingType) + { + type = underlyingType; + } + + var casts = type.GetRuntimeMethods() + .Where(m => m.IsSpecialName && m.Name == "op_Implicit") + .ToArray(); + while (type != null) + { + if ( + type.IsGenericType + && (type.Name.StartsWith("OneOf`") || type.Name.StartsWith("OneOfBase`")) + ) + { + var genericArguments = type.GetGenericArguments(); + if (genericArguments.Length == 1) + { + return [(genericArguments[0], casts[0])]; + } + + // if object type is present, make sure it is last + var indexOfObjectType = Array.IndexOf(genericArguments, typeof(object)); + if (indexOfObjectType != -1 && genericArguments.Length - 1 != indexOfObjectType) + { + genericArguments = genericArguments + .OrderBy(t => t == typeof(object) ? 1 : 0) + .ToArray(); + } + + return genericArguments + .Select(t => (t, casts.First(c => c.GetParameters()[0].ParameterType == t))) + .ToArray(); + } + + type = type.BaseType; + } + + throw new InvalidOperationException($"{type} isn't OneOf or OneOfBase"); + } + + public override bool CanConvert(global::System.Type typeToConvert) + { + return typeof(IOneOf).IsAssignableFrom(typeToConvert); + } +} diff --git a/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/Public/AdditionalProperties.cs b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/Public/AdditionalProperties.cs new file mode 100644 index 000000000000..af33ded80d3a --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/Public/AdditionalProperties.cs @@ -0,0 +1,353 @@ +using global::System.Collections; +using global::System.Collections.ObjectModel; +using global::System.Text.Json; +using global::System.Text.Json.Nodes; +using SeedInferredAuthImplicitApiKey.Core; + +namespace SeedInferredAuthImplicitApiKey; + +public record ReadOnlyAdditionalProperties : ReadOnlyAdditionalProperties +{ + internal ReadOnlyAdditionalProperties() { } + + internal ReadOnlyAdditionalProperties(IDictionary properties) + : base(properties) { } +} + +public record ReadOnlyAdditionalProperties : IReadOnlyDictionary +{ + private readonly Dictionary _extensionData = new(); + private readonly Dictionary _convertedCache = new(); + + internal ReadOnlyAdditionalProperties() + { + _extensionData = new Dictionary(); + _convertedCache = new Dictionary(); + } + + internal ReadOnlyAdditionalProperties(IDictionary properties) + { + _extensionData = new Dictionary(properties.Count); + _convertedCache = new Dictionary(properties.Count); + foreach (var kvp in properties) + { + if (kvp.Value is JsonElement element) + { + _extensionData.Add(kvp.Key, element); + } + else + { + _extensionData[kvp.Key] = JsonUtils.SerializeToElement(kvp.Value); + } + + _convertedCache[kvp.Key] = kvp.Value; + } + } + + private static T ConvertToT(JsonElement value) + { + if (typeof(T) == typeof(JsonElement)) + { + return (T)(object)value; + } + + return value.Deserialize(JsonOptions.JsonSerializerOptions)!; + } + + internal void CopyFromExtensionData(IDictionary extensionData) + { + _extensionData.Clear(); + _convertedCache.Clear(); + foreach (var kvp in extensionData) + { + _extensionData[kvp.Key] = kvp.Value; + if (kvp.Value is T value) + { + _convertedCache[kvp.Key] = value; + } + } + } + + private T GetCached(string key) + { + if (_convertedCache.TryGetValue(key, out var cached)) + { + return cached; + } + + var value = ConvertToT(_extensionData[key]); + _convertedCache[key] = value; + return value; + } + + public IEnumerator> GetEnumerator() + { + return _extensionData + .Select(kvp => new KeyValuePair(kvp.Key, GetCached(kvp.Key))) + .GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public int Count => _extensionData.Count; + + public bool ContainsKey(string key) => _extensionData.ContainsKey(key); + + public bool TryGetValue(string key, out T value) + { + if (_convertedCache.TryGetValue(key, out value!)) + { + return true; + } + + if (_extensionData.TryGetValue(key, out var element)) + { + value = ConvertToT(element); + _convertedCache[key] = value; + return true; + } + + return false; + } + + public T this[string key] => GetCached(key); + + public IEnumerable Keys => _extensionData.Keys; + + public IEnumerable Values => Keys.Select(GetCached); +} + +public record AdditionalProperties : AdditionalProperties +{ + public AdditionalProperties() { } + + public AdditionalProperties(IDictionary properties) + : base(properties) { } +} + +public record AdditionalProperties : IDictionary +{ + private readonly Dictionary _extensionData; + private readonly Dictionary _convertedCache; + + public AdditionalProperties() + { + _extensionData = new Dictionary(); + _convertedCache = new Dictionary(); + } + + public AdditionalProperties(IDictionary properties) + { + _extensionData = new Dictionary(properties.Count); + _convertedCache = new Dictionary(properties.Count); + foreach (var kvp in properties) + { + _extensionData[kvp.Key] = kvp.Value; + _convertedCache[kvp.Key] = kvp.Value; + } + } + + private static T ConvertToT(object? extensionDataValue) + { + return extensionDataValue switch + { + T value => value, + JsonElement jsonElement => jsonElement.Deserialize( + JsonOptions.JsonSerializerOptions + )!, + JsonNode jsonNode => jsonNode.Deserialize(JsonOptions.JsonSerializerOptions)!, + _ => JsonUtils + .SerializeToElement(extensionDataValue) + .Deserialize(JsonOptions.JsonSerializerOptions)!, + }; + } + + internal void CopyFromExtensionData(IDictionary extensionData) + { + _extensionData.Clear(); + _convertedCache.Clear(); + foreach (var kvp in extensionData) + { + _extensionData[kvp.Key] = kvp.Value; + if (kvp.Value is T value) + { + _convertedCache[kvp.Key] = value; + } + } + } + + internal void CopyToExtensionData(IDictionary extensionData) + { + extensionData.Clear(); + foreach (var kvp in _extensionData) + { + extensionData[kvp.Key] = kvp.Value; + } + } + + public JsonObject ToJsonObject() => + ( + JsonUtils.SerializeToNode(_extensionData) + ?? throw new InvalidOperationException( + "Failed to serialize AdditionalProperties to JSON Node." + ) + ).AsObject(); + + public JsonNode ToJsonNode() => + JsonUtils.SerializeToNode(_extensionData) + ?? throw new InvalidOperationException( + "Failed to serialize AdditionalProperties to JSON Node." + ); + + public JsonElement ToJsonElement() => JsonUtils.SerializeToElement(_extensionData); + + public JsonDocument ToJsonDocument() => JsonUtils.SerializeToDocument(_extensionData); + + public IReadOnlyDictionary ToJsonElementDictionary() + { + return new ReadOnlyDictionary( + _extensionData.ToDictionary( + kvp => kvp.Key, + kvp => + { + if (kvp.Value is JsonElement jsonElement) + { + return jsonElement; + } + + return JsonUtils.SerializeToElement(kvp.Value); + } + ) + ); + } + + public ICollection Keys => _extensionData.Keys; + + public ICollection Values + { + get + { + var values = new T[_extensionData.Count]; + var i = 0; + foreach (var key in Keys) + { + values[i++] = GetCached(key); + } + + return values; + } + } + + private T GetCached(string key) + { + if (_convertedCache.TryGetValue(key, out var value)) + { + return value; + } + + value = ConvertToT(_extensionData[key]); + _convertedCache.Add(key, value); + return value; + } + + private void SetCached(string key, T value) + { + _extensionData[key] = value; + _convertedCache[key] = value; + } + + private void AddCached(string key, T value) + { + _extensionData.Add(key, value); + _convertedCache.Add(key, value); + } + + private bool RemoveCached(string key) + { + var isRemoved = _extensionData.Remove(key); + _convertedCache.Remove(key); + return isRemoved; + } + + public int Count => _extensionData.Count; + public bool IsReadOnly => false; + + public T this[string key] + { + get => GetCached(key); + set => SetCached(key, value); + } + + public void Add(string key, T value) => AddCached(key, value); + + public void Add(KeyValuePair item) => AddCached(item.Key, item.Value); + + public bool Remove(string key) => RemoveCached(key); + + public bool Remove(KeyValuePair item) => RemoveCached(item.Key); + + public bool ContainsKey(string key) => _extensionData.ContainsKey(key); + + public bool Contains(KeyValuePair item) + { + return _extensionData.ContainsKey(item.Key) + && EqualityComparer.Default.Equals(GetCached(item.Key), item.Value); + } + + public bool TryGetValue(string key, out T value) + { + if (_convertedCache.TryGetValue(key, out value!)) + { + return true; + } + + if (_extensionData.TryGetValue(key, out var extensionDataValue)) + { + value = ConvertToT(extensionDataValue); + _convertedCache[key] = value; + return true; + } + + return false; + } + + public void Clear() + { + _extensionData.Clear(); + _convertedCache.Clear(); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + if (array is null) + { + throw new ArgumentNullException(nameof(array)); + } + + if (arrayIndex < 0 || arrayIndex > array.Length) + { + throw new ArgumentOutOfRangeException(nameof(arrayIndex)); + } + + if (array.Length - arrayIndex < _extensionData.Count) + { + throw new ArgumentException( + "The array does not have enough space to copy the elements." + ); + } + + foreach (var kvp in _extensionData) + { + array[arrayIndex++] = new KeyValuePair(kvp.Key, GetCached(kvp.Key)); + } + } + + public IEnumerator> GetEnumerator() + { + return _extensionData + .Select(kvp => new KeyValuePair(kvp.Key, GetCached(kvp.Key))) + .GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); +} diff --git a/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/Public/FileParameter.cs b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/Public/FileParameter.cs new file mode 100644 index 000000000000..5bceeb9e95a6 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/Public/FileParameter.cs @@ -0,0 +1,63 @@ +namespace SeedInferredAuthImplicitApiKey; + +/// +/// File parameter for uploading files. +/// +public record FileParameter : IDisposable +#if NET6_0_OR_GREATER + , IAsyncDisposable +#endif +{ + private bool _disposed; + + /// + /// The name of the file to be uploaded. + /// + public string? FileName { get; set; } + + /// + /// The content type of the file to be uploaded. + /// + public string? ContentType { get; set; } + + /// + /// The content of the file to be uploaded. + /// + public required Stream Stream { get; set; } + + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + protected virtual void Dispose(bool disposing) + { + if (_disposed) + return; + if (disposing) + { + Stream.Dispose(); + } + + _disposed = true; + } + +#if NET6_0_OR_GREATER + /// + public async ValueTask DisposeAsync() + { + if (!_disposed) + { + await Stream.DisposeAsync().ConfigureAwait(false); + _disposed = true; + } + + GC.SuppressFinalize(this); + } +#endif + + public static implicit operator FileParameter(Stream stream) => new() { Stream = stream }; +} diff --git a/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/Public/Version.cs b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/Public/Version.cs new file mode 100644 index 000000000000..67937e4e7c97 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/Public/Version.cs @@ -0,0 +1,7 @@ +namespace SeedInferredAuthImplicitApiKey; + +[Serializable] +internal class Version +{ + public const string Current = "0.0.1"; +} diff --git a/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/StringEnum.cs b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/StringEnum.cs new file mode 100644 index 000000000000..5aedd1ab0281 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/StringEnum.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace SeedInferredAuthImplicitApiKey.Core; + +public interface IStringEnum : IEquatable +{ + public string Value { get; } +} diff --git a/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/StringEnumExtensions.cs b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/StringEnumExtensions.cs new file mode 100644 index 000000000000..761c43c6a797 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/StringEnumExtensions.cs @@ -0,0 +1,6 @@ +namespace SeedInferredAuthImplicitApiKey.Core; + +internal static class StringEnumExtensions +{ + public static string Stringify(this IStringEnum stringEnum) => stringEnum.Value; +} diff --git a/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/StringEnumSerializer.cs b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/StringEnumSerializer.cs new file mode 100644 index 000000000000..87e14d75a623 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/StringEnumSerializer.cs @@ -0,0 +1,25 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace SeedInferredAuthImplicitApiKey.Core; + +internal class StringEnumSerializer : JsonConverter + where T : IStringEnum +{ + public override T? Read( + ref Utf8JsonReader reader, + global::System.Type typeToConvert, + JsonSerializerOptions options + ) + { + var stringValue = + reader.GetString() + ?? throw new global::System.Exception("The JSON value could not be read as a string."); + return (T?)Activator.CreateInstance(typeToConvert, stringValue); + } + + public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.Value); + } +} diff --git a/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/SeedInferredAuthImplicitApiKey.Custom.props b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/SeedInferredAuthImplicitApiKey.Custom.props new file mode 100644 index 000000000000..17a84cada530 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/SeedInferredAuthImplicitApiKey.Custom.props @@ -0,0 +1,20 @@ + + + + diff --git a/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/SeedInferredAuthImplicitApiKey.csproj b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/SeedInferredAuthImplicitApiKey.csproj new file mode 100644 index 000000000000..0a96b5dc6c42 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/SeedInferredAuthImplicitApiKey.csproj @@ -0,0 +1,61 @@ + + + net462;net8.0;net7.0;net6.0;netstandard2.0 + enable + 12 + enable + 0.0.1 + $(Version) + $(Version) + README.md + https://github.com/inferred-auth-implicit-api-key/fern + true + + + + false + + + $(DefineConstants);USE_PORTABLE_DATE_ONLY + true + + + + + + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + + + + + <_Parameter1>SeedInferredAuthImplicitApiKey.Test + + + + + diff --git a/seed/csharp-model/inferred-auth-implicit-reference/.editorconfig b/seed/csharp-model/inferred-auth-implicit-reference/.editorconfig new file mode 100644 index 000000000000..1e7a0adbac80 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-reference/.editorconfig @@ -0,0 +1,35 @@ +root = true + +[*.cs] +resharper_arrange_object_creation_when_type_evident_highlighting = hint +resharper_auto_property_can_be_made_get_only_global_highlighting = hint +resharper_check_namespace_highlighting = hint +resharper_class_never_instantiated_global_highlighting = hint +resharper_class_never_instantiated_local_highlighting = hint +resharper_collection_never_updated_global_highlighting = hint +resharper_convert_type_check_pattern_to_null_check_highlighting = hint +resharper_inconsistent_naming_highlighting = hint +resharper_member_can_be_private_global_highlighting = hint +resharper_member_hides_static_from_outer_class_highlighting = hint +resharper_not_accessed_field_local_highlighting = hint +resharper_nullable_warning_suppression_is_used_highlighting = suggestion +resharper_partial_type_with_single_part_highlighting = hint +resharper_prefer_concrete_value_over_default_highlighting = none +resharper_private_field_can_be_converted_to_local_variable_highlighting = hint +resharper_property_can_be_made_init_only_global_highlighting = hint +resharper_property_can_be_made_init_only_local_highlighting = hint +resharper_redundant_name_qualifier_highlighting = none +resharper_redundant_using_directive_highlighting = hint +resharper_replace_slice_with_range_indexer_highlighting = none +resharper_unused_auto_property_accessor_global_highlighting = hint +resharper_unused_auto_property_accessor_local_highlighting = hint +resharper_unused_member_global_highlighting = hint +resharper_unused_type_global_highlighting = hint +resharper_use_string_interpolation_highlighting = hint +dotnet_diagnostic.CS1591.severity = suggestion + +[src/**/Types/*.cs] +resharper_check_namespace_highlighting = none + +[src/**/Core/Public/*.cs] +resharper_check_namespace_highlighting = none \ No newline at end of file diff --git a/seed/csharp-model/inferred-auth-implicit-reference/.fern/metadata.json b/seed/csharp-model/inferred-auth-implicit-reference/.fern/metadata.json new file mode 100644 index 000000000000..1e1156144085 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-reference/.fern/metadata.json @@ -0,0 +1,5 @@ +{ + "cliVersion": "DUMMY", + "generatorName": "fernapi/fern-csharp-model", + "generatorVersion": "latest" +} \ No newline at end of file diff --git a/seed/csharp-model/inferred-auth-implicit-reference/.github/workflows/ci.yml b/seed/csharp-model/inferred-auth-implicit-reference/.github/workflows/ci.yml new file mode 100644 index 000000000000..7b850e54db3c --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-reference/.github/workflows/ci.yml @@ -0,0 +1,97 @@ +name: ci + +on: [push] + +env: + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true + DOTNET_NOLOGO: true + +jobs: + compile: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v5 + + - uses: actions/checkout@master + + - name: Setup .NET + uses: actions/setup-dotnet@v5 + with: + dotnet-version: 10.x + + - name: Print .NET info + run: dotnet --info + + - name: Install tools + run: dotnet tool restore + + - name: Restore dependencies + run: dotnet restore src/SeedInferredAuthImplicit/SeedInferredAuthImplicit.csproj + + - name: Build Release + run: dotnet build src/SeedInferredAuthImplicit/SeedInferredAuthImplicit.csproj -c Release --no-restore + + unit-tests: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v5 + + - name: Setup .NET + uses: actions/setup-dotnet@v5 + with: + dotnet-version: 10.x + + - name: Print .NET info + run: dotnet --info + + - name: Install tools + run: | + dotnet tool restore + + - name: Restore dependencies + run: dotnet restore src/SeedInferredAuthImplicit.Test/SeedInferredAuthImplicit.Test.csproj + + - name: Build Release + run: dotnet build src/SeedInferredAuthImplicit.Test/SeedInferredAuthImplicit.Test.csproj -c Release --no-restore + + - name: Run Tests + run: dotnet test src/SeedInferredAuthImplicit.Test/SeedInferredAuthImplicit.Test.csproj -c Release --no-build --no-restore + + + publish: + needs: [compile] + if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v5 + + - name: Setup .NET + uses: actions/setup-dotnet@v5 + with: + dotnet-version: 10.x + + - name: Print .NET info + run: dotnet --info + + - name: Install tools + run: dotnet tool restore + + - name: Restore dependencies + run: dotnet restore src/SeedInferredAuthImplicit/SeedInferredAuthImplicit.csproj + + - name: Build Release + run: dotnet build src/SeedInferredAuthImplicit/SeedInferredAuthImplicit.csproj -c Release --no-restore + + - name: Publish + env: + NUGET_API_KEY: ${{ secrets.NUGET_API_TOKEN }} + run: | + dotnet pack src/SeedInferredAuthImplicit/SeedInferredAuthImplicit.csproj -c Release --no-build --no-restore + dotnet nuget push src/SeedInferredAuthImplicit/bin/Release/*.nupkg --api-key $NUGET_API_KEY --source "nuget.org" + diff --git a/seed/csharp-model/inferred-auth-implicit-reference/.gitignore b/seed/csharp-model/inferred-auth-implicit-reference/.gitignore new file mode 100644 index 000000000000..11014f2b33d7 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-reference/.gitignore @@ -0,0 +1,484 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +## This is based on `dotnet new gitignore` and customized by Fern + +# dotenv files +.env + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +# [Rr]elease/ (Ignored by Fern) +# [Rr]eleases/ (Ignored by Fern) +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +# [Ll]og/ (Ignored by Fern) +# [Ll]ogs/ (Ignored by Fern) + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET +project.lock.json +project.fragment.lock.json +artifacts/ + +# Tye +.tye/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml +.idea + +## +## Visual studio for Mac +## + + +# globs +Makefile.in +*.userprefs +*.usertasks +config.make +config.status +aclocal.m4 +install-sh +autom4te.cache/ +*.tar.gz +tarballs/ +test-results/ + +# Mac bundle stuff +*.dmg +*.app + +# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# Vim temporary swap files +*.swp diff --git a/seed/csharp-model/inferred-auth-implicit-reference/SeedInferredAuthImplicit.slnx b/seed/csharp-model/inferred-auth-implicit-reference/SeedInferredAuthImplicit.slnx new file mode 100644 index 000000000000..a036be008daa --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-reference/SeedInferredAuthImplicit.slnx @@ -0,0 +1,4 @@ + + + + diff --git a/seed/csharp-model/inferred-auth-implicit-reference/snippet.json b/seed/csharp-model/inferred-auth-implicit-reference/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/Json/AdditionalPropertiesTests.cs b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/Json/AdditionalPropertiesTests.cs new file mode 100644 index 000000000000..c0a032843861 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/Json/AdditionalPropertiesTests.cs @@ -0,0 +1,365 @@ +using global::System.Text.Json; +using global::System.Text.Json.Serialization; +using NUnit.Framework; +using SeedInferredAuthImplicit.Core; + +namespace SeedInferredAuthImplicit.Test.Core.Json; + +[TestFixture] +public class AdditionalPropertiesTests +{ + [Test] + public void Record_OnDeserialized_ShouldPopulateAdditionalProperties() + { + // Arrange + const string json = """ + { + "id": "1", + "category": "fiction", + "title": "The Hobbit" + } + """; + + // Act + var record = JsonUtils.Deserialize(json); + + // Assert + Assert.That(record, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(record.Id, Is.EqualTo("1")); + Assert.That(record.AdditionalProperties["category"].GetString(), Is.EqualTo("fiction")); + Assert.That(record.AdditionalProperties["title"].GetString(), Is.EqualTo("The Hobbit")); + }); + } + + [Test] + public void RecordWithWriteableAdditionalProperties_OnSerialization_ShouldIncludeAdditionalProperties() + { + // Arrange + var record = new WriteableRecord + { + Id = "1", + AdditionalProperties = { ["category"] = "fiction", ["title"] = "The Hobbit" }, + }; + + // Act + var json = JsonUtils.Serialize(record); + var deserializedRecord = JsonUtils.Deserialize(json); + + // Assert + Assert.That(deserializedRecord, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(deserializedRecord.Id, Is.EqualTo("1")); + Assert.That( + deserializedRecord.AdditionalProperties["category"], + Is.InstanceOf() + ); + Assert.That( + ((JsonElement)deserializedRecord.AdditionalProperties["category"]!).GetString(), + Is.EqualTo("fiction") + ); + Assert.That( + deserializedRecord.AdditionalProperties["title"], + Is.InstanceOf() + ); + Assert.That( + ((JsonElement)deserializedRecord.AdditionalProperties["title"]!).GetString(), + Is.EqualTo("The Hobbit") + ); + }); + } + + [Test] + public void ReadOnlyAdditionalProperties_ShouldRetrieveValuesCorrectly() + { + // Arrange + var extensionData = new Dictionary + { + ["key1"] = JsonUtils.SerializeToElement("value1"), + ["key2"] = JsonUtils.SerializeToElement(123), + }; + var readOnlyProps = new ReadOnlyAdditionalProperties(); + readOnlyProps.CopyFromExtensionData(extensionData); + + // Act & Assert + Assert.That(readOnlyProps["key1"].GetString(), Is.EqualTo("value1")); + Assert.That(readOnlyProps["key2"].GetInt32(), Is.EqualTo(123)); + } + + [Test] + public void AdditionalProperties_ShouldBehaveAsDictionary() + { + // Arrange + var additionalProps = new AdditionalProperties { ["key1"] = "value1", ["key2"] = 123 }; + + // Act + additionalProps["key3"] = true; + + // Assert + Assert.Multiple(() => + { + Assert.That(additionalProps["key1"], Is.EqualTo("value1")); + Assert.That(additionalProps["key2"], Is.EqualTo(123)); + Assert.That((bool)additionalProps["key3"]!, Is.True); + Assert.That(additionalProps.Count, Is.EqualTo(3)); + }); + } + + [Test] + public void AdditionalProperties_ToJsonObject_ShouldSerializeCorrectly() + { + // Arrange + var additionalProps = new AdditionalProperties { ["key1"] = "value1", ["key2"] = 123 }; + + // Act + var jsonObject = additionalProps.ToJsonObject(); + + Assert.Multiple(() => + { + // Assert + Assert.That(jsonObject["key1"]!.GetValue(), Is.EqualTo("value1")); + Assert.That(jsonObject["key2"]!.GetValue(), Is.EqualTo(123)); + }); + } + + [Test] + public void AdditionalProperties_MixReadAndWrite_ShouldOverwriteDeserializedProperty() + { + // Arrange + const string json = """ + { + "id": "1", + "category": "fiction", + "title": "The Hobbit" + } + """; + var record = JsonUtils.Deserialize(json); + + // Act + record.AdditionalProperties["category"] = "non-fiction"; + + // Assert + Assert.Multiple(() => + { + Assert.That(record, Is.Not.Null); + Assert.That(record.Id, Is.EqualTo("1")); + Assert.That(record.AdditionalProperties["category"], Is.EqualTo("non-fiction")); + Assert.That(record.AdditionalProperties["title"], Is.InstanceOf()); + Assert.That( + ((JsonElement)record.AdditionalProperties["title"]!).GetString(), + Is.EqualTo("The Hobbit") + ); + }); + } + + [Test] + public void RecordWithReadonlyAdditionalPropertiesInts_OnDeserialized_ShouldPopulateAdditionalProperties() + { + // Arrange + const string json = """ + { + "extra1": 42, + "extra2": 99 + } + """; + + // Act + var record = JsonUtils.Deserialize(json); + + // Assert + Assert.That(record, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(record.AdditionalProperties["extra1"], Is.EqualTo(42)); + Assert.That(record.AdditionalProperties["extra2"], Is.EqualTo(99)); + }); + } + + [Test] + public void RecordWithAdditionalPropertiesInts_OnSerialization_ShouldIncludeAdditionalProperties() + { + // Arrange + var record = new WriteableRecordWithInts + { + AdditionalProperties = { ["extra1"] = 42, ["extra2"] = 99 }, + }; + + // Act + var json = JsonUtils.Serialize(record); + var deserializedRecord = JsonUtils.Deserialize(json); + + // Assert + Assert.That(deserializedRecord, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(deserializedRecord.AdditionalProperties["extra1"], Is.EqualTo(42)); + Assert.That(deserializedRecord.AdditionalProperties["extra2"], Is.EqualTo(99)); + }); + } + + [Test] + public void RecordWithReadonlyAdditionalPropertiesDictionaries_OnDeserialized_ShouldPopulateAdditionalProperties() + { + // Arrange + const string json = """ + { + "extra1": { "key1": true, "key2": false }, + "extra2": { "key3": true } + } + """; + + // Act + var record = JsonUtils.Deserialize(json); + + // Assert + Assert.That(record, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(record.AdditionalProperties["extra1"]["key1"], Is.True); + Assert.That(record.AdditionalProperties["extra1"]["key2"], Is.False); + Assert.That(record.AdditionalProperties["extra2"]["key3"], Is.True); + }); + } + + [Test] + public void RecordWithAdditionalPropertiesDictionaries_OnSerialization_ShouldIncludeAdditionalProperties() + { + // Arrange + var record = new WriteableRecordWithDictionaries + { + AdditionalProperties = + { + ["extra1"] = new Dictionary { { "key1", true }, { "key2", false } }, + ["extra2"] = new Dictionary { { "key3", true } }, + }, + }; + + // Act + var json = JsonUtils.Serialize(record); + var deserializedRecord = JsonUtils.Deserialize(json); + + // Assert + Assert.That(deserializedRecord, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(deserializedRecord.AdditionalProperties["extra1"]["key1"], Is.True); + Assert.That(deserializedRecord.AdditionalProperties["extra1"]["key2"], Is.False); + Assert.That(deserializedRecord.AdditionalProperties["extra2"]["key3"], Is.True); + }); + } + + private record Record : IJsonOnDeserialized + { + [JsonPropertyName("id")] + public required string Id { get; set; } + + [JsonExtensionData] + private readonly IDictionary _extensionData = + new Dictionary(); + + [JsonIgnore] + public ReadOnlyAdditionalProperties AdditionalProperties { get; } = new(); + + void IJsonOnDeserialized.OnDeserialized() + { + AdditionalProperties.CopyFromExtensionData(_extensionData); + } + } + + private record WriteableRecord : IJsonOnDeserialized, IJsonOnSerializing + { + [JsonPropertyName("id")] + public required string Id { get; set; } + + [JsonExtensionData] + private readonly IDictionary _extensionData = + new Dictionary(); + + [JsonIgnore] + public AdditionalProperties AdditionalProperties { get; set; } = new(); + + void IJsonOnDeserialized.OnDeserialized() + { + AdditionalProperties.CopyFromExtensionData(_extensionData); + } + + void IJsonOnSerializing.OnSerializing() + { + AdditionalProperties.CopyToExtensionData(_extensionData); + } + } + + private record RecordWithInts : IJsonOnDeserialized + { + [JsonExtensionData] + private readonly IDictionary _extensionData = + new Dictionary(); + + [JsonIgnore] + public ReadOnlyAdditionalProperties AdditionalProperties { get; } = new(); + + void IJsonOnDeserialized.OnDeserialized() + { + AdditionalProperties.CopyFromExtensionData(_extensionData); + } + } + + private record WriteableRecordWithInts : IJsonOnDeserialized, IJsonOnSerializing + { + [JsonExtensionData] + private readonly IDictionary _extensionData = + new Dictionary(); + + [JsonIgnore] + public AdditionalProperties AdditionalProperties { get; } = new(); + + void IJsonOnDeserialized.OnDeserialized() + { + AdditionalProperties.CopyFromExtensionData(_extensionData); + } + + void IJsonOnSerializing.OnSerializing() + { + AdditionalProperties.CopyToExtensionData(_extensionData); + } + } + + private record RecordWithDictionaries : IJsonOnDeserialized + { + [JsonExtensionData] + private readonly IDictionary _extensionData = + new Dictionary(); + + [JsonIgnore] + public ReadOnlyAdditionalProperties< + Dictionary + > AdditionalProperties { get; } = new(); + + void IJsonOnDeserialized.OnDeserialized() + { + AdditionalProperties.CopyFromExtensionData(_extensionData); + } + } + + private record WriteableRecordWithDictionaries : IJsonOnDeserialized, IJsonOnSerializing + { + [JsonExtensionData] + private readonly IDictionary _extensionData = + new Dictionary(); + + [JsonIgnore] + public AdditionalProperties> AdditionalProperties { get; } = new(); + + void IJsonOnDeserialized.OnDeserialized() + { + AdditionalProperties.CopyFromExtensionData(_extensionData); + } + + void IJsonOnSerializing.OnSerializing() + { + AdditionalProperties.CopyToExtensionData(_extensionData); + } + } +} diff --git a/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/Json/DateOnlyJsonTests.cs b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/Json/DateOnlyJsonTests.cs new file mode 100644 index 000000000000..16c45d284f09 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/Json/DateOnlyJsonTests.cs @@ -0,0 +1,76 @@ +using NUnit.Framework; +using SeedInferredAuthImplicit.Core; + +namespace SeedInferredAuthImplicit.Test.Core.Json; + +[TestFixture] +public class DateOnlyJsonTests +{ + [Test] + public void SerializeDateOnly_ShouldMatchExpectedFormat() + { + (DateOnly dateOnly, string expected)[] testCases = + [ + (new DateOnly(2023, 10, 5), "\"2023-10-05\""), + (new DateOnly(2023, 1, 1), "\"2023-01-01\""), + (new DateOnly(2023, 12, 31), "\"2023-12-31\""), + (new DateOnly(2023, 6, 15), "\"2023-06-15\""), + (new DateOnly(2023, 3, 10), "\"2023-03-10\""), + ]; + foreach (var (dateOnly, expected) in testCases) + { + var json = JsonUtils.Serialize(dateOnly); + Assert.That(json, Is.EqualTo(expected)); + } + } + + [Test] + public void DeserializeDateOnly_ShouldMatchExpectedDateOnly() + { + (DateOnly expected, string json)[] testCases = + [ + (new DateOnly(2023, 10, 5), "\"2023-10-05\""), + (new DateOnly(2023, 1, 1), "\"2023-01-01\""), + (new DateOnly(2023, 12, 31), "\"2023-12-31\""), + (new DateOnly(2023, 6, 15), "\"2023-06-15\""), + (new DateOnly(2023, 3, 10), "\"2023-03-10\""), + ]; + + foreach (var (expected, json) in testCases) + { + var dateOnly = JsonUtils.Deserialize(json); + Assert.That(dateOnly, Is.EqualTo(expected)); + } + } + + [Test] + public void SerializeNullableDateOnly_ShouldMatchExpectedFormat() + { + (DateOnly? dateOnly, string expected)[] testCases = + [ + (new DateOnly(2023, 10, 5), "\"2023-10-05\""), + (null, "null"), + ]; + foreach (var (dateOnly, expected) in testCases) + { + var json = JsonUtils.Serialize(dateOnly); + Assert.That(json, Is.EqualTo(expected)); + } + } + + [Test] + public void DeserializeNullableDateOnly_ShouldMatchExpectedDateOnly() + { + (DateOnly? expected, string json)[] testCases = + [ + (new DateOnly(2023, 10, 5), "\"2023-10-05\""), + (null, "null"), + ]; + + foreach (var (expected, json) in testCases) + { + var dateOnly = JsonUtils.Deserialize(json); + Assert.That(dateOnly, Is.EqualTo(expected)); + } + } +} diff --git a/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/Json/DateTimeJsonTests.cs b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/Json/DateTimeJsonTests.cs new file mode 100644 index 000000000000..8bab90119d14 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/Json/DateTimeJsonTests.cs @@ -0,0 +1,110 @@ +using NUnit.Framework; +using SeedInferredAuthImplicit.Core; + +namespace SeedInferredAuthImplicit.Test.Core.Json; + +[TestFixture] +public class DateTimeJsonTests +{ + [Test] + public void SerializeDateTime_ShouldMatchExpectedFormat() + { + (DateTime dateTime, string expected)[] testCases = + [ + ( + new DateTime(2023, 10, 5, 14, 30, 0, DateTimeKind.Utc), + "\"2023-10-05T14:30:00.000Z\"" + ), + (new DateTime(2023, 1, 1, 0, 0, 0, DateTimeKind.Utc), "\"2023-01-01T00:00:00.000Z\""), + ( + new DateTime(2023, 12, 31, 23, 59, 59, DateTimeKind.Utc), + "\"2023-12-31T23:59:59.000Z\"" + ), + (new DateTime(2023, 6, 15, 12, 0, 0, DateTimeKind.Utc), "\"2023-06-15T12:00:00.000Z\""), + ( + new DateTime(2023, 3, 10, 8, 45, 30, DateTimeKind.Utc), + "\"2023-03-10T08:45:30.000Z\"" + ), + ( + new DateTime(2023, 3, 10, 8, 45, 30, 123, DateTimeKind.Utc), + "\"2023-03-10T08:45:30.123Z\"" + ), + ]; + foreach (var (dateTime, expected) in testCases) + { + var json = JsonUtils.Serialize(dateTime); + Assert.That(json, Is.EqualTo(expected)); + } + } + + [Test] + public void DeserializeDateTime_ShouldMatchExpectedDateTime() + { + (DateTime expected, string json)[] testCases = + [ + ( + new DateTime(2023, 10, 5, 14, 30, 0, DateTimeKind.Utc), + "\"2023-10-05T14:30:00.000Z\"" + ), + (new DateTime(2023, 1, 1, 0, 0, 0, DateTimeKind.Utc), "\"2023-01-01T00:00:00.000Z\""), + ( + new DateTime(2023, 12, 31, 23, 59, 59, DateTimeKind.Utc), + "\"2023-12-31T23:59:59.000Z\"" + ), + (new DateTime(2023, 6, 15, 12, 0, 0, DateTimeKind.Utc), "\"2023-06-15T12:00:00.000Z\""), + ( + new DateTime(2023, 3, 10, 8, 45, 30, DateTimeKind.Utc), + "\"2023-03-10T08:45:30.000Z\"" + ), + (new DateTime(2023, 3, 10, 8, 45, 30, DateTimeKind.Utc), "\"2023-03-10T08:45:30Z\""), + ( + new DateTime(2023, 3, 10, 8, 45, 30, 123, DateTimeKind.Utc), + "\"2023-03-10T08:45:30.123Z\"" + ), + ]; + + foreach (var (expected, json) in testCases) + { + var dateTime = JsonUtils.Deserialize(json); + Assert.That(dateTime, Is.EqualTo(expected)); + } + } + + [Test] + public void SerializeNullableDateTime_ShouldMatchExpectedFormat() + { + (DateTime? expected, string json)[] testCases = + [ + ( + new DateTime(2023, 10, 5, 14, 30, 0, DateTimeKind.Utc), + "\"2023-10-05T14:30:00.000Z\"" + ), + (null, "null"), + ]; + + foreach (var (expected, json) in testCases) + { + var dateTime = JsonUtils.Deserialize(json); + Assert.That(dateTime, Is.EqualTo(expected)); + } + } + + [Test] + public void DeserializeNullableDateTime_ShouldMatchExpectedDateTime() + { + (DateTime? expected, string json)[] testCases = + [ + ( + new DateTime(2023, 10, 5, 14, 30, 0, DateTimeKind.Utc), + "\"2023-10-05T14:30:00.000Z\"" + ), + (null, "null"), + ]; + + foreach (var (expected, json) in testCases) + { + var dateTime = JsonUtils.Deserialize(json); + Assert.That(dateTime, Is.EqualTo(expected)); + } + } +} diff --git a/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/Json/JsonAccessAttributeTests.cs b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/Json/JsonAccessAttributeTests.cs new file mode 100644 index 000000000000..6eef3d3e1bc0 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/Json/JsonAccessAttributeTests.cs @@ -0,0 +1,160 @@ +using global::System.Text.Json.Serialization; +using NUnit.Framework; +using SeedInferredAuthImplicit.Core; + +namespace SeedInferredAuthImplicit.Test.Core.Json; + +[TestFixture] +public class JsonAccessAttributeTests +{ + private class MyClass + { + [JsonPropertyName("read_only_prop")] + [JsonAccess(JsonAccessType.ReadOnly)] + public string? ReadOnlyProp { get; set; } + + [JsonPropertyName("write_only_prop")] + [JsonAccess(JsonAccessType.WriteOnly)] + public string? WriteOnlyProp { get; set; } + + [JsonPropertyName("normal_prop")] + public string? NormalProp { get; set; } + + [JsonPropertyName("read_only_nullable_list")] + [JsonAccess(JsonAccessType.ReadOnly)] + public IEnumerable? ReadOnlyNullableList { get; set; } + + [JsonPropertyName("read_only_list")] + [JsonAccess(JsonAccessType.ReadOnly)] + public IEnumerable ReadOnlyList { get; set; } = []; + + [JsonPropertyName("write_only_nullable_list")] + [JsonAccess(JsonAccessType.WriteOnly)] + public IEnumerable? WriteOnlyNullableList { get; set; } + + [JsonPropertyName("write_only_list")] + [JsonAccess(JsonAccessType.WriteOnly)] + public IEnumerable WriteOnlyList { get; set; } = []; + + [JsonPropertyName("normal_list")] + public IEnumerable NormalList { get; set; } = []; + + [JsonPropertyName("normal_nullable_list")] + public IEnumerable? NullableNormalList { get; set; } + } + + [Test] + public void JsonAccessAttribute_ShouldWorkAsExpected() + { + const string json = """ + { + "read_only_prop": "read", + "write_only_prop": "write", + "normal_prop": "normal_prop", + "read_only_nullable_list": ["item1", "item2"], + "read_only_list": ["item3", "item4"], + "write_only_nullable_list": ["item5", "item6"], + "write_only_list": ["item7", "item8"], + "normal_list": ["normal1", "normal2"], + "normal_nullable_list": ["normal1", "normal2"] + } + """; + var obj = JsonUtils.Deserialize(json); + + Assert.Multiple(() => + { + // String properties + Assert.That(obj.ReadOnlyProp, Is.EqualTo("read")); + Assert.That(obj.WriteOnlyProp, Is.Null); + Assert.That(obj.NormalProp, Is.EqualTo("normal_prop")); + + // List properties - read only + var nullableReadOnlyList = obj.ReadOnlyNullableList?.ToArray(); + Assert.That(nullableReadOnlyList, Is.Not.Null); + Assert.That(nullableReadOnlyList, Has.Length.EqualTo(2)); + Assert.That(nullableReadOnlyList![0], Is.EqualTo("item1")); + Assert.That(nullableReadOnlyList![1], Is.EqualTo("item2")); + + var readOnlyList = obj.ReadOnlyList.ToArray(); + Assert.That(readOnlyList, Is.Not.Null); + Assert.That(readOnlyList, Has.Length.EqualTo(2)); + Assert.That(readOnlyList[0], Is.EqualTo("item3")); + Assert.That(readOnlyList[1], Is.EqualTo("item4")); + + // List properties - write only + Assert.That(obj.WriteOnlyNullableList, Is.Null); + Assert.That(obj.WriteOnlyList, Is.Not.Null); + Assert.That(obj.WriteOnlyList, Is.Empty); + + // Normal list property + var normalList = obj.NormalList.ToArray(); + Assert.That(normalList, Is.Not.Null); + Assert.That(normalList, Has.Length.EqualTo(2)); + Assert.That(normalList[0], Is.EqualTo("normal1")); + Assert.That(normalList[1], Is.EqualTo("normal2")); + }); + + // Set up values for serialization + obj.WriteOnlyProp = "write"; + obj.NormalProp = "new_value"; + obj.WriteOnlyNullableList = new List { "write1", "write2" }; + obj.WriteOnlyList = new List { "write3", "write4" }; + obj.NormalList = new List { "new_normal" }; + obj.NullableNormalList = new List { "new_normal" }; + + var serializedJson = JsonUtils.Serialize(obj); + const string expectedJson = """ + { + "write_only_prop": "write", + "normal_prop": "new_value", + "write_only_nullable_list": [ + "write1", + "write2" + ], + "write_only_list": [ + "write3", + "write4" + ], + "normal_list": [ + "new_normal" + ], + "normal_nullable_list": [ + "new_normal" + ] + } + """; + Assert.That(serializedJson, Is.EqualTo(expectedJson).IgnoreWhiteSpace); + } + + [Test] + public void JsonAccessAttribute_WithNullListsInJson_ShouldWorkAsExpected() + { + const string json = """ + { + "read_only_prop": "read", + "normal_prop": "normal_prop", + "read_only_nullable_list": null, + "read_only_list": [] + } + """; + var obj = JsonUtils.Deserialize(json); + + Assert.Multiple(() => + { + // Read-only nullable list should be null when JSON contains null + var nullableReadOnlyList = obj.ReadOnlyNullableList?.ToArray(); + Assert.That(nullableReadOnlyList, Is.Null); + + // Read-only non-nullable list should never be null, but empty when JSON contains null + var readOnlyList = obj.ReadOnlyList.ToArray(); // This should be initialized to an empty list by default + Assert.That(readOnlyList, Is.Not.Null); + Assert.That(readOnlyList, Is.Empty); + }); + + // Serialize and verify read-only lists are not included + var serializedJson = JsonUtils.Serialize(obj); + Assert.That(serializedJson, Does.Not.Contain("read_only_prop")); + Assert.That(serializedJson, Does.Not.Contain("read_only_nullable_list")); + Assert.That(serializedJson, Does.Not.Contain("read_only_list")); + } +} diff --git a/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/Json/OneOfSerializerTests.cs b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/Json/OneOfSerializerTests.cs new file mode 100644 index 000000000000..2d420f8b76df --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/Json/OneOfSerializerTests.cs @@ -0,0 +1,314 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using NUnit.Framework; +using OneOf; +using SeedInferredAuthImplicit.Core; + +namespace SeedInferredAuthImplicit.Test.Core.Json; + +[TestFixture] +[Parallelizable(ParallelScope.All)] +public class OneOfSerializerTests +{ + private class Foo + { + [JsonPropertyName("string_prop")] + public required string StringProp { get; set; } + } + + private class Bar + { + [JsonPropertyName("int_prop")] + public required int IntProp { get; set; } + } + + private static readonly OneOf OneOf1 = OneOf< + string, + int, + object, + Foo, + Bar + >.FromT2(new { }); + private const string OneOf1String = "{}"; + + private static readonly OneOf OneOf2 = OneOf< + string, + int, + object, + Foo, + Bar + >.FromT0("test"); + private const string OneOf2String = "\"test\""; + + private static readonly OneOf OneOf3 = OneOf< + string, + int, + object, + Foo, + Bar + >.FromT1(123); + private const string OneOf3String = "123"; + + private static readonly OneOf OneOf4 = OneOf< + string, + int, + object, + Foo, + Bar + >.FromT3(new Foo { StringProp = "test" }); + private const string OneOf4String = "{\"string_prop\": \"test\"}"; + + private static readonly OneOf OneOf5 = OneOf< + string, + int, + object, + Foo, + Bar + >.FromT4(new Bar { IntProp = 5 }); + private const string OneOf5String = "{\"int_prop\": 5}"; + + [Test] + public void Serialize_OneOfs_Should_Return_Expected_String() + { + (OneOf, string)[] testData = + [ + (OneOf1, OneOf1String), + (OneOf2, OneOf2String), + (OneOf3, OneOf3String), + (OneOf4, OneOf4String), + (OneOf5, OneOf5String), + ]; + Assert.Multiple(() => + { + foreach (var (oneOf, expected) in testData) + { + var result = JsonUtils.Serialize(oneOf); + Assert.That(result, Is.EqualTo(expected).IgnoreWhiteSpace); + } + }); + } + + [Test] + public void OneOfs_Should_Deserialize_From_String() + { + (OneOf, string)[] testData = + [ + (OneOf1, OneOf1String), + (OneOf2, OneOf2String), + (OneOf3, OneOf3String), + (OneOf4, OneOf4String), + (OneOf5, OneOf5String), + ]; + Assert.Multiple(() => + { + foreach (var (oneOf, json) in testData) + { + var result = JsonUtils.Deserialize>(json); + Assert.That(result.Index, Is.EqualTo(oneOf.Index)); + Assert.That(json, Is.EqualTo(JsonUtils.Serialize(result.Value)).IgnoreWhiteSpace); + } + }); + } + + private static readonly OneOf? NullableOneOf1 = null; + private const string NullableOneOf1String = "null"; + + private static readonly OneOf? NullableOneOf2 = OneOf< + string, + int, + object, + Foo, + Bar + >.FromT4(new Bar { IntProp = 5 }); + private const string NullableOneOf2String = "{\"int_prop\": 5}"; + + [Test] + public void Serialize_NullableOneOfs_Should_Return_Expected_String() + { + (OneOf?, string)[] testData = + [ + (NullableOneOf1, NullableOneOf1String), + (NullableOneOf2, NullableOneOf2String), + ]; + Assert.Multiple(() => + { + foreach (var (oneOf, expected) in testData) + { + var result = JsonUtils.Serialize(oneOf); + Assert.That(result, Is.EqualTo(expected).IgnoreWhiteSpace); + } + }); + } + + [Test] + public void NullableOneOfs_Should_Deserialize_From_String() + { + (OneOf?, string)[] testData = + [ + (NullableOneOf1, NullableOneOf1String), + (NullableOneOf2, NullableOneOf2String), + ]; + Assert.Multiple(() => + { + foreach (var (oneOf, json) in testData) + { + var result = JsonUtils.Deserialize?>(json); + Assert.That(result?.Index, Is.EqualTo(oneOf?.Index)); + Assert.That(json, Is.EqualTo(JsonUtils.Serialize(result?.Value)).IgnoreWhiteSpace); + } + }); + } + + private static readonly OneOf OneOfWithNullable1 = OneOf< + string, + int, + Foo? + >.FromT2(null); + private const string OneOfWithNullable1String = "null"; + + private static readonly OneOf OneOfWithNullable2 = OneOf< + string, + int, + Foo? + >.FromT2(new Foo { StringProp = "test" }); + private const string OneOfWithNullable2String = "{\"string_prop\": \"test\"}"; + + private static readonly OneOf OneOfWithNullable3 = OneOf< + string, + int, + Foo? + >.FromT0("test"); + private const string OneOfWithNullable3String = "\"test\""; + + [Test] + public void Serialize_OneOfWithNullables_Should_Return_Expected_String() + { + (OneOf, string)[] testData = + [ + (OneOfWithNullable1, OneOfWithNullable1String), + (OneOfWithNullable2, OneOfWithNullable2String), + (OneOfWithNullable3, OneOfWithNullable3String), + ]; + Assert.Multiple(() => + { + foreach (var (oneOf, expected) in testData) + { + var result = JsonUtils.Serialize(oneOf); + Assert.That(result, Is.EqualTo(expected).IgnoreWhiteSpace); + } + }); + } + + [Test] + public void OneOfWithNullables_Should_Deserialize_From_String() + { + (OneOf, string)[] testData = + [ + // (OneOfWithNullable1, OneOfWithNullable1String), // not possible with .NET's JSON serializer + (OneOfWithNullable2, OneOfWithNullable2String), + (OneOfWithNullable3, OneOfWithNullable3String), + ]; + Assert.Multiple(() => + { + foreach (var (oneOf, json) in testData) + { + var result = JsonUtils.Deserialize>(json); + Assert.That(result.Index, Is.EqualTo(oneOf.Index)); + Assert.That(json, Is.EqualTo(JsonUtils.Serialize(result.Value)).IgnoreWhiteSpace); + } + }); + } + + [Test] + public void Serialize_OneOfWithObjectLast_Should_Return_Expected_String() + { + var oneOfWithObjectLast = OneOf.FromT4( + new { random = "data" } + ); + const string oneOfWithObjectLastString = "{\"random\": \"data\"}"; + + var result = JsonUtils.Serialize(oneOfWithObjectLast); + Assert.That(result, Is.EqualTo(oneOfWithObjectLastString).IgnoreWhiteSpace); + } + + [Test] + public void OneOfWithObjectLast_Should_Deserialize_From_String() + { + const string oneOfWithObjectLastString = "{\"random\": \"data\"}"; + var result = JsonUtils.Deserialize>( + oneOfWithObjectLastString + ); + Assert.Multiple(() => + { + Assert.That(result.Index, Is.EqualTo(4)); + Assert.That(result.Value, Is.InstanceOf()); + Assert.That( + JsonUtils.Serialize(result.Value), + Is.EqualTo(oneOfWithObjectLastString).IgnoreWhiteSpace + ); + }); + } + + [Test] + public void Serialize_OneOfWithObjectNotLast_Should_Return_Expected_String() + { + var oneOfWithObjectNotLast = OneOf.FromT1( + new { random = "data" } + ); + const string oneOfWithObjectNotLastString = "{\"random\": \"data\"}"; + + var result = JsonUtils.Serialize(oneOfWithObjectNotLast); + Assert.That(result, Is.EqualTo(oneOfWithObjectNotLastString).IgnoreWhiteSpace); + } + + [Test] + public void OneOfWithObjectNotLast_Should_Deserialize_From_String() + { + const string oneOfWithObjectNotLastString = "{\"random\": \"data\"}"; + var result = JsonUtils.Deserialize>( + oneOfWithObjectNotLastString + ); + Assert.Multiple(() => + { + Assert.That(result.Index, Is.EqualTo(1)); + Assert.That(result.Value, Is.InstanceOf()); + Assert.That( + JsonUtils.Serialize(result.Value), + Is.EqualTo(oneOfWithObjectNotLastString).IgnoreWhiteSpace + ); + }); + } + + [Test] + public void Serialize_OneOfSingleType_Should_Return_Expected_String() + { + var oneOfSingle = OneOf.FromT0("single"); + const string oneOfSingleString = "\"single\""; + + var result = JsonUtils.Serialize(oneOfSingle); + Assert.That(result, Is.EqualTo(oneOfSingleString)); + } + + [Test] + public void OneOfSingleType_Should_Deserialize_From_String() + { + const string oneOfSingleString = "\"single\""; + var result = JsonUtils.Deserialize>(oneOfSingleString); + Assert.Multiple(() => + { + Assert.That(result.Index, Is.EqualTo(0)); + Assert.That(result.Value, Is.EqualTo("single")); + }); + } + + [Test] + public void Deserialize_InvalidData_Should_Throw_Exception() + { + const string invalidJson = "{\"invalid\": \"data\"}"; + + Assert.Throws(() => + { + JsonUtils.Deserialize>(invalidJson); + }); + } +} diff --git a/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/Json/StringEnumSerializerTests.cs b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/Json/StringEnumSerializerTests.cs new file mode 100644 index 000000000000..b1afde182649 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/Json/StringEnumSerializerTests.cs @@ -0,0 +1,138 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using NUnit.Framework; +using SeedInferredAuthImplicit.Core; + +namespace SeedInferredAuthImplicit.Test.Core.Json; + +[TestFixture] +[Parallelizable(ParallelScope.All)] +public class StringEnumSerializerTests +{ + private static readonly JsonSerializerOptions JsonOptions = new() { WriteIndented = true }; + + private static readonly DummyEnum KnownEnumValue2 = DummyEnum.KnownValue2; + private static readonly DummyEnum UnknownEnumValue = DummyEnum.FromCustom("unknown_value"); + + private static readonly string JsonWithKnownEnum2 = $$""" + { + "enum_property": "{{KnownEnumValue2}}" + } + """; + + private static readonly string JsonWithUnknownEnum = $$""" + { + "enum_property": "{{UnknownEnumValue}}" + } + """; + + [Test] + public void ShouldParseKnownEnumValue2() + { + var obj = JsonSerializer.Deserialize(JsonWithKnownEnum2, JsonOptions); + Assert.That(obj, Is.Not.Null); + Assert.That(obj.EnumProperty, Is.EqualTo(KnownEnumValue2)); + } + + [Test] + public void ShouldParseUnknownEnum() + { + var obj = JsonSerializer.Deserialize(JsonWithUnknownEnum, JsonOptions); + Assert.That(obj, Is.Not.Null); + Assert.That(obj.EnumProperty, Is.EqualTo(UnknownEnumValue)); + } + + [Test] + public void ShouldSerializeKnownEnumValue2() + { + var json = JsonSerializer.SerializeToElement( + new DummyObject { EnumProperty = KnownEnumValue2 }, + JsonOptions + ); + TestContext.Out.WriteLine("Serialized JSON: \n" + json); + var enumString = json.GetProperty("enum_property").GetString(); + Assert.That(enumString, Is.Not.Null); + Assert.That(enumString, Is.EqualTo(KnownEnumValue2)); + } + + [Test] + public void ShouldSerializeUnknownEnum() + { + var json = JsonSerializer.SerializeToElement( + new DummyObject { EnumProperty = UnknownEnumValue }, + JsonOptions + ); + TestContext.Out.WriteLine("Serialized JSON: \n" + json); + var enumString = json.GetProperty("enum_property").GetString(); + Assert.That(enumString, Is.Not.Null); + Assert.That(enumString, Is.EqualTo(UnknownEnumValue)); + } +} + +public class DummyObject +{ + [JsonPropertyName("enum_property")] + public DummyEnum EnumProperty { get; set; } +} + +[JsonConverter(typeof(StringEnumSerializer))] +public readonly record struct DummyEnum : IStringEnum +{ + public DummyEnum(string value) + { + Value = value; + } + + /// + /// The string value of the enum. + /// + public string Value { get; } + + public static readonly DummyEnum KnownValue1 = FromCustom(Values.KnownValue1); + + public static readonly DummyEnum KnownValue2 = FromCustom(Values.KnownValue2); + + /// + /// Constant strings for enum values + /// + public static class Values + { + public const string KnownValue1 = "known_value1"; + + public const string KnownValue2 = "known_value2"; + } + + /// + /// Create a string enum with the given value. + /// + public static DummyEnum FromCustom(string value) + { + return new DummyEnum(value); + } + + /// + /// Returns the string value of the enum. + /// + public override string ToString() + { + return Value; + } + + public bool Equals(string? other) + { + return Value.Equals(other); + } + + public override int GetHashCode() + { + return Value.GetHashCode(); + } + + public static explicit operator string(DummyEnum value) => value.Value; + + public static explicit operator DummyEnum(string value) => new(value); + + public static bool operator ==(DummyEnum value1, string value2) => value1.Value.Equals(value2); + + public static bool operator !=(DummyEnum value1, string value2) => !value1.Value.Equals(value2); +} diff --git a/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/SeedInferredAuthImplicit.Test.Custom.props b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/SeedInferredAuthImplicit.Test.Custom.props new file mode 100644 index 000000000000..aac9b5020d80 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/SeedInferredAuthImplicit.Test.Custom.props @@ -0,0 +1,6 @@ + + diff --git a/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/SeedInferredAuthImplicit.Test.csproj b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/SeedInferredAuthImplicit.Test.csproj new file mode 100644 index 000000000000..ed2590573df5 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/SeedInferredAuthImplicit.Test.csproj @@ -0,0 +1,39 @@ + + + net8.0 + 12 + enable + enable + false + true + true + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + diff --git a/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Utils/JsonElementComparer.cs b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Utils/JsonElementComparer.cs new file mode 100644 index 000000000000..1704c99af443 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Utils/JsonElementComparer.cs @@ -0,0 +1,236 @@ +using System.Text.Json; +using NUnit.Framework.Constraints; + +namespace NUnit.Framework; + +/// +/// Extensions for EqualConstraint to handle JsonElement objects. +/// +public static class JsonElementComparerExtensions +{ + /// + /// Extension method for comparing JsonElement objects in NUnit tests. + /// Property order doesn't matter, but array order does matter. + /// Includes special handling for DateTime string formats. + /// + /// The Is.EqualTo() constraint instance. + /// A constraint that can compare JsonElements with detailed diffs. + public static EqualConstraint UsingJsonElementComparer(this EqualConstraint constraint) + { + return constraint.Using(new JsonElementComparer()); + } +} + +/// +/// Equality comparer for JsonElement with detailed reporting. +/// Property order doesn't matter, but array order does matter. +/// Now includes special handling for DateTime string formats with improved null handling. +/// +public class JsonElementComparer : IEqualityComparer +{ + private string _failurePath = string.Empty; + + /// + public bool Equals(JsonElement x, JsonElement y) + { + _failurePath = string.Empty; + return CompareJsonElements(x, y, string.Empty); + } + + /// + public int GetHashCode(JsonElement obj) + { + return JsonSerializer.Serialize(obj).GetHashCode(); + } + + private bool CompareJsonElements(JsonElement x, JsonElement y, string path) + { + // If value kinds don't match, they're not equivalent + if (x.ValueKind != y.ValueKind) + { + _failurePath = $"{path}: Expected {x.ValueKind} but got {y.ValueKind}"; + return false; + } + + switch (x.ValueKind) + { + case JsonValueKind.Object: + return CompareJsonObjects(x, y, path); + + case JsonValueKind.Array: + return CompareJsonArraysInOrder(x, y, path); + + case JsonValueKind.String: + string? xStr = x.GetString(); + string? yStr = y.GetString(); + + // Handle null strings + if (xStr is null && yStr is null) + return true; + + if (xStr is null || yStr is null) + { + _failurePath = + $"{path}: Expected {(xStr is null ? "null" : $"\"{xStr}\"")} but got {(yStr is null ? "null" : $"\"{yStr}\"")}"; + return false; + } + + // Check if they are identical strings + if (xStr == yStr) + return true; + + // Try to handle DateTime strings + if (IsLikelyDateTimeString(xStr) && IsLikelyDateTimeString(yStr)) + { + if (AreEquivalentDateTimeStrings(xStr, yStr)) + return true; + } + + _failurePath = $"{path}: Expected \"{xStr}\" but got \"{yStr}\""; + return false; + + case JsonValueKind.Number: + if (x.GetDecimal() != y.GetDecimal()) + { + _failurePath = $"{path}: Expected {x.GetDecimal()} but got {y.GetDecimal()}"; + return false; + } + + return true; + + case JsonValueKind.True: + case JsonValueKind.False: + if (x.GetBoolean() != y.GetBoolean()) + { + _failurePath = $"{path}: Expected {x.GetBoolean()} but got {y.GetBoolean()}"; + return false; + } + + return true; + + case JsonValueKind.Null: + return true; + + default: + _failurePath = $"{path}: Unsupported JsonValueKind {x.ValueKind}"; + return false; + } + } + + private bool IsLikelyDateTimeString(string? str) + { + // Simple heuristic to identify likely ISO date time strings + return str != null + && (str.Contains("T") && (str.EndsWith("Z") || str.Contains("+") || str.Contains("-"))); + } + + private bool AreEquivalentDateTimeStrings(string str1, string str2) + { + // Try to parse both as DateTime + if (DateTime.TryParse(str1, out DateTime dt1) && DateTime.TryParse(str2, out DateTime dt2)) + { + return dt1 == dt2; + } + + return false; + } + + private bool CompareJsonObjects(JsonElement x, JsonElement y, string path) + { + // Create dictionaries for both JSON objects + var xProps = new Dictionary(); + var yProps = new Dictionary(); + + foreach (var prop in x.EnumerateObject()) + xProps[prop.Name] = prop.Value; + + foreach (var prop in y.EnumerateObject()) + yProps[prop.Name] = prop.Value; + + // Check if all properties in x exist in y + foreach (var key in xProps.Keys) + { + if (!yProps.ContainsKey(key)) + { + _failurePath = $"{path}: Missing property '{key}'"; + return false; + } + } + + // Check if y has extra properties + foreach (var key in yProps.Keys) + { + if (!xProps.ContainsKey(key)) + { + _failurePath = $"{path}: Unexpected property '{key}'"; + return false; + } + } + + // Compare each property value + foreach (var key in xProps.Keys) + { + var propPath = string.IsNullOrEmpty(path) ? key : $"{path}.{key}"; + if (!CompareJsonElements(xProps[key], yProps[key], propPath)) + { + return false; + } + } + + return true; + } + + private bool CompareJsonArraysInOrder(JsonElement x, JsonElement y, string path) + { + var xArray = x.EnumerateArray(); + var yArray = y.EnumerateArray(); + + // Count x elements + var xCount = 0; + var xElements = new List(); + foreach (var item in xArray) + { + xElements.Add(item); + xCount++; + } + + // Count y elements + var yCount = 0; + var yElements = new List(); + foreach (var item in yArray) + { + yElements.Add(item); + yCount++; + } + + // Check if counts match + if (xCount != yCount) + { + _failurePath = $"{path}: Expected {xCount} items but found {yCount}"; + return false; + } + + // Compare elements in order + for (var i = 0; i < xCount; i++) + { + var itemPath = $"{path}[{i}]"; + if (!CompareJsonElements(xElements[i], yElements[i], itemPath)) + { + return false; + } + } + + return true; + } + + /// + public override string ToString() + { + if (!string.IsNullOrEmpty(_failurePath)) + { + return $"JSON comparison failed at {_failurePath}"; + } + + return "JsonElementEqualityComparer"; + } +} diff --git a/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Utils/NUnitExtensions.cs b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Utils/NUnitExtensions.cs new file mode 100644 index 000000000000..426df1245388 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Utils/NUnitExtensions.cs @@ -0,0 +1,28 @@ +using NUnit.Framework.Constraints; + +namespace NUnit.Framework; + +/// +/// Extensions for NUnit constraints. +/// +public static class NUnitExtensions +{ + /// + /// Modifies the EqualConstraint to use our own set of default comparers. + /// + /// + /// + public static EqualConstraint UsingDefaults(this EqualConstraint constraint) => + constraint + .UsingPropertiesComparer() + .UsingReadOnlyMemoryComparer() + .UsingReadOnlyMemoryComparer() + .UsingReadOnlyMemoryComparer() + .UsingReadOnlyMemoryComparer() + .UsingReadOnlyMemoryComparer() + .UsingReadOnlyMemoryComparer() + .UsingReadOnlyMemoryComparer() + .UsingReadOnlyMemoryComparer() + .UsingOneOfComparer() + .UsingJsonElementComparer(); +} diff --git a/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Utils/OneOfComparer.cs b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Utils/OneOfComparer.cs new file mode 100644 index 000000000000..0c975b471ff3 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Utils/OneOfComparer.cs @@ -0,0 +1,43 @@ +using NUnit.Framework.Constraints; +using OneOf; + +namespace NUnit.Framework; + +/// +/// Extensions for EqualConstraint to handle OneOf values. +/// +public static class EqualConstraintExtensions +{ + /// + /// Modifies the EqualConstraint to handle OneOf instances by comparing their inner values. + /// This works alongside other comparison modifiers like UsingPropertiesComparer. + /// + /// The EqualConstraint to modify. + /// The same constraint instance for method chaining. + public static EqualConstraint UsingOneOfComparer(this EqualConstraint constraint) + { + // Register a comparer factory for IOneOf types + constraint.Using( + (x, y) => + { + // ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract + if (x.Value is null && y.Value is null) + { + return true; + } + + if (x.Value is null) + { + return false; + } + + var propertiesComparer = new NUnitEqualityComparer(); + var tolerance = Tolerance.Default; + propertiesComparer.CompareProperties = true; + return propertiesComparer.AreEqual(x.Value, y.Value, ref tolerance); + } + ); + + return constraint; + } +} diff --git a/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Utils/ReadOnlyMemoryComparer.cs b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Utils/ReadOnlyMemoryComparer.cs new file mode 100644 index 000000000000..fc0b595a5e54 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Utils/ReadOnlyMemoryComparer.cs @@ -0,0 +1,87 @@ +using NUnit.Framework.Constraints; + +namespace NUnit.Framework; + +/// +/// Extensions for NUnit constraints. +/// +public static class ReadOnlyMemoryComparerExtensions +{ + /// + /// Extension method for comparing ReadOnlyMemory<T> in NUnit tests. + /// + /// The type of elements in the ReadOnlyMemory. + /// The Is.EqualTo() constraint instance. + /// A constraint that can compare ReadOnlyMemory<T>. + public static EqualConstraint UsingReadOnlyMemoryComparer(this EqualConstraint constraint) + where T : IComparable + { + return constraint.Using(new ReadOnlyMemoryComparer()); + } +} + +/// +/// Comparer for ReadOnlyMemory<T>. Compares sequences by value. +/// +/// +/// The type of elements in the ReadOnlyMemory. +/// +public class ReadOnlyMemoryComparer : IComparer> + where T : IComparable +{ + /// + public int Compare(ReadOnlyMemory x, ReadOnlyMemory y) + { + // Check if sequences are equal + var xSpan = x.Span; + var ySpan = y.Span; + + // Optimized case for IEquatable implementations + if (typeof(IEquatable).IsAssignableFrom(typeof(T))) + { + var areEqual = xSpan.SequenceEqual(ySpan); + if (areEqual) + { + return 0; // Sequences are equal + } + } + else + { + // Manual equality check for non-IEquatable types + if (xSpan.Length == ySpan.Length) + { + var areEqual = true; + for (var i = 0; i < xSpan.Length; i++) + { + if (!EqualityComparer.Default.Equals(xSpan[i], ySpan[i])) + { + areEqual = false; + break; + } + } + + if (areEqual) + { + return 0; // Sequences are equal + } + } + } + + // For non-equal sequences, we need to return a consistent ordering + // First compare lengths + if (x.Length != y.Length) + return x.Length.CompareTo(y.Length); + + // Same length but different content - compare first differing element + for (var i = 0; i < x.Length; i++) + { + if (!EqualityComparer.Default.Equals(xSpan[i], ySpan[i])) + { + return xSpan[i].CompareTo(ySpan[i]); + } + } + + // Should never reach here if not equal + return 0; + } +} diff --git a/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Auth/GetTokenRequest.cs b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Auth/GetTokenRequest.cs new file mode 100644 index 000000000000..a926b4442797 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Auth/GetTokenRequest.cs @@ -0,0 +1,43 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using SeedInferredAuthImplicit.Core; + +namespace SeedInferredAuthImplicit; + +/// +/// A request to obtain an OAuth token. +/// +[Serializable] +public record GetTokenRequest : IJsonOnDeserialized +{ + [JsonExtensionData] + private readonly IDictionary _extensionData = + new Dictionary(); + + [JsonPropertyName("client_id")] + public required string ClientId { get; set; } + + [JsonPropertyName("client_secret")] + public required string ClientSecret { get; set; } + + [JsonPropertyName("audience")] + public string Audience { get; set; } = "https://api.example.com"; + + [JsonPropertyName("grant_type")] + public string GrantType { get; set; } = "client_credentials"; + + [JsonPropertyName("scope")] + public string? Scope { get; set; } + + [JsonIgnore] + public ReadOnlyAdditionalProperties AdditionalProperties { get; private set; } = new(); + + void IJsonOnDeserialized.OnDeserialized() => + AdditionalProperties.CopyFromExtensionData(_extensionData); + + /// + public override string ToString() + { + return JsonUtils.Serialize(this); + } +} diff --git a/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Auth/RefreshTokenRequest.cs b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Auth/RefreshTokenRequest.cs new file mode 100644 index 000000000000..f99d014e4b8e --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Auth/RefreshTokenRequest.cs @@ -0,0 +1,46 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using SeedInferredAuthImplicit.Core; + +namespace SeedInferredAuthImplicit; + +/// +/// A request to refresh an OAuth token. +/// +[Serializable] +public record RefreshTokenRequest : IJsonOnDeserialized +{ + [JsonExtensionData] + private readonly IDictionary _extensionData = + new Dictionary(); + + [JsonPropertyName("client_id")] + public required string ClientId { get; set; } + + [JsonPropertyName("client_secret")] + public required string ClientSecret { get; set; } + + [JsonPropertyName("refresh_token")] + public required string RefreshToken { get; set; } + + [JsonPropertyName("audience")] + public string Audience { get; set; } = "https://api.example.com"; + + [JsonPropertyName("grant_type")] + public string GrantType { get; set; } = "refresh_token"; + + [JsonPropertyName("scope")] + public string? Scope { get; set; } + + [JsonIgnore] + public ReadOnlyAdditionalProperties AdditionalProperties { get; private set; } = new(); + + void IJsonOnDeserialized.OnDeserialized() => + AdditionalProperties.CopyFromExtensionData(_extensionData); + + /// + public override string ToString() + { + return JsonUtils.Serialize(this); + } +} diff --git a/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Auth/TokenResponse.cs b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Auth/TokenResponse.cs new file mode 100644 index 000000000000..8755bd2bb3be --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Auth/TokenResponse.cs @@ -0,0 +1,37 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using SeedInferredAuthImplicit.Core; + +namespace SeedInferredAuthImplicit; + +/// +/// An OAuth token response. +/// +[Serializable] +public record TokenResponse : IJsonOnDeserialized +{ + [JsonExtensionData] + private readonly IDictionary _extensionData = + new Dictionary(); + + [JsonPropertyName("access_token")] + public required string AccessToken { get; set; } + + [JsonPropertyName("expires_in")] + public required int ExpiresIn { get; set; } + + [JsonPropertyName("refresh_token")] + public string? RefreshToken { get; set; } + + [JsonIgnore] + public ReadOnlyAdditionalProperties AdditionalProperties { get; private set; } = new(); + + void IJsonOnDeserialized.OnDeserialized() => + AdditionalProperties.CopyFromExtensionData(_extensionData); + + /// + public override string ToString() + { + return JsonUtils.Serialize(this); + } +} diff --git a/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/CollectionItemSerializer.cs b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/CollectionItemSerializer.cs new file mode 100644 index 000000000000..55e220e5e4e1 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/CollectionItemSerializer.cs @@ -0,0 +1,89 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace SeedInferredAuthImplicit.Core; + +/// +/// Json collection converter. +/// +/// Type of item to convert. +/// Converter to use for individual items. +internal class CollectionItemSerializer + : JsonConverter> + where TConverterType : JsonConverter +{ + /// + /// Reads a json string and deserializes it into an object. + /// + /// Json reader. + /// Type to convert. + /// Serializer options. + /// Created object. + public override IEnumerable? Read( + ref Utf8JsonReader reader, + global::System.Type typeToConvert, + JsonSerializerOptions options + ) + { + if (reader.TokenType == JsonTokenType.Null) + { + return default; + } + + var jsonSerializerOptions = new JsonSerializerOptions(options); + jsonSerializerOptions.Converters.Clear(); + jsonSerializerOptions.Converters.Add(Activator.CreateInstance()); + + var returnValue = new List(); + + while (reader.TokenType != JsonTokenType.EndArray) + { + if (reader.TokenType != JsonTokenType.StartArray) + { + var item = (TDatatype)( + JsonSerializer.Deserialize(ref reader, typeof(TDatatype), jsonSerializerOptions) + ?? throw new global::System.Exception( + $"Failed to deserialize collection item of type {typeof(TDatatype)}" + ) + ); + returnValue.Add(item); + } + + reader.Read(); + } + + return returnValue; + } + + /// + /// Writes a json string. + /// + /// Json writer. + /// Value to write. + /// Serializer options. + public override void Write( + Utf8JsonWriter writer, + IEnumerable? value, + JsonSerializerOptions options + ) + { + if (value == null) + { + writer.WriteNullValue(); + return; + } + + var jsonSerializerOptions = new JsonSerializerOptions(options); + jsonSerializerOptions.Converters.Clear(); + jsonSerializerOptions.Converters.Add(Activator.CreateInstance()); + + writer.WriteStartArray(); + + foreach (var data in value) + { + JsonSerializer.Serialize(writer, data, jsonSerializerOptions); + } + + writer.WriteEndArray(); + } +} diff --git a/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/Constants.cs b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/Constants.cs new file mode 100644 index 000000000000..4319fec065f3 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/Constants.cs @@ -0,0 +1,7 @@ +namespace SeedInferredAuthImplicit.Core; + +internal static class Constants +{ + public const string DateTimeFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.fffK"; + public const string DateFormat = "yyyy-MM-dd"; +} diff --git a/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/DateOnlyConverter.cs b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/DateOnlyConverter.cs new file mode 100644 index 000000000000..3a454faac096 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/DateOnlyConverter.cs @@ -0,0 +1,747 @@ +// ReSharper disable All +#pragma warning disable + +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using global::System.Diagnostics; +using global::System.Diagnostics.CodeAnalysis; +using global::System.Globalization; +using global::System.Runtime.CompilerServices; +using global::System.Runtime.InteropServices; +using global::System.Text.Json; +using global::System.Text.Json.Serialization; + +// ReSharper disable SuggestVarOrType_SimpleTypes +// ReSharper disable SuggestVarOrType_BuiltInTypes + +namespace SeedInferredAuthImplicit.Core +{ + /// + /// Custom converter for handling the data type with the System.Text.Json library. + /// + /// + /// This class backported from: + /// + /// System.Text.Json.Serialization.Converters.DateOnlyConverter + /// + public sealed class DateOnlyConverter : JsonConverter + { + private const int FormatLength = 10; // YYYY-MM-DD + + private const int MaxEscapedFormatLength = + FormatLength * JsonConstants.MaxExpansionFactorWhileEscaping; + + /// + public override DateOnly Read( + ref Utf8JsonReader reader, + global::System.Type typeToConvert, + JsonSerializerOptions options + ) + { + if (reader.TokenType != JsonTokenType.String) + { + ThrowHelper.ThrowInvalidOperationException_ExpectedString(reader.TokenType); + } + + return ReadCore(ref reader); + } + + /// + public override DateOnly ReadAsPropertyName( + ref Utf8JsonReader reader, + global::System.Type typeToConvert, + JsonSerializerOptions options + ) + { + Debug.Assert(reader.TokenType == JsonTokenType.PropertyName); + return ReadCore(ref reader); + } + + private static DateOnly ReadCore(ref Utf8JsonReader reader) + { + if ( + !JsonHelpers.IsInRangeInclusive( + reader.ValueLength(), + FormatLength, + MaxEscapedFormatLength + ) + ) + { + ThrowHelper.ThrowFormatException(DataType.DateOnly); + } + + scoped ReadOnlySpan source; + if (!reader.HasValueSequence && !reader.ValueIsEscaped) + { + source = reader.ValueSpan; + } + else + { + Span stackSpan = stackalloc byte[MaxEscapedFormatLength]; + int bytesWritten = reader.CopyString(stackSpan); + source = stackSpan.Slice(0, bytesWritten); + } + + if (!JsonHelpers.TryParseAsIso(source, out DateOnly value)) + { + ThrowHelper.ThrowFormatException(DataType.DateOnly); + } + + return value; + } + + /// + public override void Write( + Utf8JsonWriter writer, + DateOnly value, + JsonSerializerOptions options + ) + { +#if NET8_0_OR_GREATER + Span buffer = stackalloc byte[FormatLength]; +#else + Span buffer = stackalloc char[FormatLength]; +#endif + // ReSharper disable once RedundantAssignment + bool formattedSuccessfully = value.TryFormat( + buffer, + out int charsWritten, + "O".AsSpan(), + CultureInfo.InvariantCulture + ); + Debug.Assert(formattedSuccessfully && charsWritten == FormatLength); + writer.WriteStringValue(buffer); + } + + /// + public override void WriteAsPropertyName( + Utf8JsonWriter writer, + DateOnly value, + JsonSerializerOptions options + ) + { +#if NET8_0_OR_GREATER + Span buffer = stackalloc byte[FormatLength]; +#else + Span buffer = stackalloc char[FormatLength]; +#endif + // ReSharper disable once RedundantAssignment + bool formattedSuccessfully = value.TryFormat( + buffer, + out int charsWritten, + "O".AsSpan(), + CultureInfo.InvariantCulture + ); + Debug.Assert(formattedSuccessfully && charsWritten == FormatLength); + writer.WritePropertyName(buffer); + } + } + + internal static class JsonConstants + { + // The maximum number of fraction digits the Json DateTime parser allows + public const int DateTimeParseNumFractionDigits = 16; + + // In the worst case, an ASCII character represented as a single utf-8 byte could expand 6x when escaped. + public const int MaxExpansionFactorWhileEscaping = 6; + + // The largest fraction expressible by TimeSpan and DateTime formats + public const int MaxDateTimeFraction = 9_999_999; + + // TimeSpan and DateTime formats allow exactly up to many digits for specifying the fraction after the seconds. + public const int DateTimeNumFractionDigits = 7; + + public const byte UtcOffsetToken = (byte)'Z'; + + public const byte TimePrefix = (byte)'T'; + + public const byte Period = (byte)'.'; + + public const byte Hyphen = (byte)'-'; + + public const byte Colon = (byte)':'; + + public const byte Plus = (byte)'+'; + } + + // ReSharper disable SuggestVarOrType_Elsewhere + // ReSharper disable SuggestVarOrType_SimpleTypes + // ReSharper disable SuggestVarOrType_BuiltInTypes + + internal static class JsonHelpers + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsInRangeInclusive(int value, int lowerBound, int upperBound) => + (uint)(value - lowerBound) <= (uint)(upperBound - lowerBound); + + public static bool IsDigit(byte value) => (uint)(value - '0') <= '9' - '0'; + + [StructLayout(LayoutKind.Auto)] + private struct DateTimeParseData + { + public int Year; + public int Month; + public int Day; + public bool IsCalendarDateOnly; + public int Hour; + public int Minute; + public int Second; + public int Fraction; // This value should never be greater than 9_999_999. + public int OffsetHours; + public int OffsetMinutes; + + // ReSharper disable once NotAccessedField.Local + public byte OffsetToken; + } + + public static bool TryParseAsIso(ReadOnlySpan source, out DateOnly value) + { + if ( + TryParseDateTimeOffset(source, out DateTimeParseData parseData) + && parseData.IsCalendarDateOnly + && TryCreateDateTime(parseData, DateTimeKind.Unspecified, out DateTime dateTime) + ) + { + value = DateOnly.FromDateTime(dateTime); + return true; + } + + value = default; + return false; + } + + /// + /// ISO 8601 date time parser (ISO 8601-1:2019). + /// + /// The date/time to parse in UTF-8 format. + /// The parsed for the given . + /// + /// Supports extended calendar date (5.2.2.1) and complete (5.4.2.1) calendar date/time of day + /// representations with optional specification of seconds and fractional seconds. + /// + /// Times can be explicitly specified as UTC ("Z" - 5.3.3) or offsets from UTC ("+/-hh:mm" 5.3.4.2). + /// If unspecified they are considered to be local per spec. + /// + /// Examples: (TZD is either "Z" or hh:mm offset from UTC) + /// + /// YYYY-MM-DD (e.g. 1997-07-16) + /// YYYY-MM-DDThh:mm (e.g. 1997-07-16T19:20) + /// YYYY-MM-DDThh:mm:ss (e.g. 1997-07-16T19:20:30) + /// YYYY-MM-DDThh:mm:ss.s (e.g. 1997-07-16T19:20:30.45) + /// YYYY-MM-DDThh:mmTZD (e.g. 1997-07-16T19:20+01:00) + /// YYYY-MM-DDThh:mm:ssTZD (e.g. 1997-07-16T19:20:3001:00) + /// YYYY-MM-DDThh:mm:ss.sTZD (e.g. 1997-07-16T19:20:30.45Z) + /// + /// Generally speaking we always require the "extended" option when one exists (3.1.3.5). + /// The extended variants have separator characters between components ('-', ':', '.', etc.). + /// Spaces are not permitted. + /// + /// "true" if successfully parsed. + private static bool TryParseDateTimeOffset( + ReadOnlySpan source, + out DateTimeParseData parseData + ) + { + parseData = default; + + // too short datetime + Debug.Assert(source.Length >= 10); + + // Parse the calendar date + // ----------------------- + // ISO 8601-1:2019 5.2.2.1b "Calendar date complete extended format" + // [dateX] = [year]["-"][month]["-"][day] + // [year] = [YYYY] [0000 - 9999] (4.3.2) + // [month] = [MM] [01 - 12] (4.3.3) + // [day] = [DD] [01 - 28, 29, 30, 31] (4.3.4) + // + // Note: 5.2.2.2 "Representations with reduced precision" allows for + // just [year]["-"][month] (a) and just [year] (b), but we currently + // don't permit it. + + { + uint digit1 = source[0] - (uint)'0'; + uint digit2 = source[1] - (uint)'0'; + uint digit3 = source[2] - (uint)'0'; + uint digit4 = source[3] - (uint)'0'; + + if (digit1 > 9 || digit2 > 9 || digit3 > 9 || digit4 > 9) + { + return false; + } + + parseData.Year = (int)(digit1 * 1000 + digit2 * 100 + digit3 * 10 + digit4); + } + + if ( + source[4] != JsonConstants.Hyphen + || !TryGetNextTwoDigits(source.Slice(start: 5, length: 2), ref parseData.Month) + || source[7] != JsonConstants.Hyphen + || !TryGetNextTwoDigits(source.Slice(start: 8, length: 2), ref parseData.Day) + ) + { + return false; + } + + // We now have YYYY-MM-DD [dateX] + // ReSharper disable once ConvertIfStatementToSwitchStatement + if (source.Length == 10) + { + parseData.IsCalendarDateOnly = true; + return true; + } + + // Parse the time of day + // --------------------- + // + // ISO 8601-1:2019 5.3.1.2b "Local time of day complete extended format" + // [timeX] = ["T"][hour][":"][min][":"][sec] + // [hour] = [hh] [00 - 23] (4.3.8a) + // [minute] = [mm] [00 - 59] (4.3.9a) + // [sec] = [ss] [00 - 59, 60 with a leap second] (4.3.10a) + // + // ISO 8601-1:2019 5.3.3 "UTC of day" + // [timeX]["Z"] + // + // ISO 8601-1:2019 5.3.4.2 "Local time of day with the time shift between + // local timescale and UTC" (Extended format) + // + // [shiftX] = ["+"|"-"][hour][":"][min] + // + // Notes: + // + // "T" is optional per spec, but _only_ when times are used alone. In our + // case, we're reading out a complete date & time and as such require "T". + // (5.4.2.1b). + // + // For [timeX] We allow seconds to be omitted per 5.3.1.3a "Representations + // with reduced precision". 5.3.1.3b allows just specifying the hour, but + // we currently don't permit this. + // + // Decimal fractions are allowed for hours, minutes and seconds (5.3.14). + // We only allow fractions for seconds currently. Lower order components + // can't follow, i.e. you can have T23.3, but not T23.3:04. There must be + // one digit, but the max number of digits is implementation defined. We + // currently allow up to 16 digits of fractional seconds only. While we + // support 16 fractional digits we only parse the first seven, anything + // past that is considered a zero. This is to stay compatible with the + // DateTime implementation which is limited to this resolution. + + if (source.Length < 16) + { + // Source does not have enough characters for YYYY-MM-DDThh:mm + return false; + } + + // Parse THH:MM (e.g. "T10:32") + if ( + source[10] != JsonConstants.TimePrefix + || source[13] != JsonConstants.Colon + || !TryGetNextTwoDigits(source.Slice(start: 11, length: 2), ref parseData.Hour) + || !TryGetNextTwoDigits(source.Slice(start: 14, length: 2), ref parseData.Minute) + ) + { + return false; + } + + // We now have YYYY-MM-DDThh:mm + Debug.Assert(source.Length >= 16); + if (source.Length == 16) + { + return true; + } + + byte curByte = source[16]; + int sourceIndex = 17; + + // Either a TZD ['Z'|'+'|'-'] or a seconds separator [':'] is valid at this point + switch (curByte) + { + case JsonConstants.UtcOffsetToken: + parseData.OffsetToken = JsonConstants.UtcOffsetToken; + return sourceIndex == source.Length; + case JsonConstants.Plus: + case JsonConstants.Hyphen: + parseData.OffsetToken = curByte; + return ParseOffset(ref parseData, source.Slice(sourceIndex)); + case JsonConstants.Colon: + break; + default: + return false; + } + + // Try reading the seconds + if ( + source.Length < 19 + || !TryGetNextTwoDigits(source.Slice(start: 17, length: 2), ref parseData.Second) + ) + { + return false; + } + + // We now have YYYY-MM-DDThh:mm:ss + Debug.Assert(source.Length >= 19); + if (source.Length == 19) + { + return true; + } + + curByte = source[19]; + sourceIndex = 20; + + // Either a TZD ['Z'|'+'|'-'] or a seconds decimal fraction separator ['.'] is valid at this point + switch (curByte) + { + case JsonConstants.UtcOffsetToken: + parseData.OffsetToken = JsonConstants.UtcOffsetToken; + return sourceIndex == source.Length; + case JsonConstants.Plus: + case JsonConstants.Hyphen: + parseData.OffsetToken = curByte; + return ParseOffset(ref parseData, source.Slice(sourceIndex)); + case JsonConstants.Period: + break; + default: + return false; + } + + // Source does not have enough characters for second fractions (i.e. ".s") + // YYYY-MM-DDThh:mm:ss.s + if (source.Length < 21) + { + return false; + } + + // Parse fraction. This value should never be greater than 9_999_999 + int numDigitsRead = 0; + int fractionEnd = Math.Min( + sourceIndex + JsonConstants.DateTimeParseNumFractionDigits, + source.Length + ); + + while (sourceIndex < fractionEnd && IsDigit(curByte = source[sourceIndex])) + { + if (numDigitsRead < JsonConstants.DateTimeNumFractionDigits) + { + parseData.Fraction = parseData.Fraction * 10 + (int)(curByte - (uint)'0'); + numDigitsRead++; + } + + sourceIndex++; + } + + if (parseData.Fraction != 0) + { + while (numDigitsRead < JsonConstants.DateTimeNumFractionDigits) + { + parseData.Fraction *= 10; + numDigitsRead++; + } + } + + // We now have YYYY-MM-DDThh:mm:ss.s + Debug.Assert(sourceIndex <= source.Length); + if (sourceIndex == source.Length) + { + return true; + } + + curByte = source[sourceIndex++]; + + // TZD ['Z'|'+'|'-'] is valid at this point + switch (curByte) + { + case JsonConstants.UtcOffsetToken: + parseData.OffsetToken = JsonConstants.UtcOffsetToken; + return sourceIndex == source.Length; + case JsonConstants.Plus: + case JsonConstants.Hyphen: + parseData.OffsetToken = curByte; + return ParseOffset(ref parseData, source.Slice(sourceIndex)); + default: + return false; + } + + static bool ParseOffset(ref DateTimeParseData parseData, ReadOnlySpan offsetData) + { + // Parse the hours for the offset + if ( + offsetData.Length < 2 + || !TryGetNextTwoDigits(offsetData.Slice(0, 2), ref parseData.OffsetHours) + ) + { + return false; + } + + // We now have YYYY-MM-DDThh:mm:ss.s+|-hh + + if (offsetData.Length == 2) + { + // Just hours offset specified + return true; + } + + // Ensure we have enough for ":mm" + return offsetData.Length == 5 + && offsetData[2] == JsonConstants.Colon + && TryGetNextTwoDigits(offsetData.Slice(3), ref parseData.OffsetMinutes); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + // ReSharper disable once RedundantAssignment + private static bool TryGetNextTwoDigits(ReadOnlySpan source, ref int value) + { + Debug.Assert(source.Length == 2); + + uint digit1 = source[0] - (uint)'0'; + uint digit2 = source[1] - (uint)'0'; + + if (digit1 > 9 || digit2 > 9) + { + value = 0; + return false; + } + + value = (int)(digit1 * 10 + digit2); + return true; + } + + // The following methods are borrowed verbatim from src/Common/src/CoreLib/System/Buffers/Text/Utf8Parser/Utf8Parser.Date.Helpers.cs + + /// + /// Overflow-safe DateTime factory. + /// + private static bool TryCreateDateTime( + DateTimeParseData parseData, + DateTimeKind kind, + out DateTime value + ) + { + if (parseData.Year == 0) + { + value = default; + return false; + } + + Debug.Assert(parseData.Year <= 9999); // All of our callers to date parse the year from fixed 4-digit fields so this value is trusted. + + if ((uint)parseData.Month - 1 >= 12) + { + value = default; + return false; + } + + uint dayMinusOne = (uint)parseData.Day - 1; + if ( + dayMinusOne >= 28 + && dayMinusOne >= DateTime.DaysInMonth(parseData.Year, parseData.Month) + ) + { + value = default; + return false; + } + + if ((uint)parseData.Hour > 23) + { + value = default; + return false; + } + + if ((uint)parseData.Minute > 59) + { + value = default; + return false; + } + + // This needs to allow leap seconds when appropriate. + // See https://github.com/dotnet/runtime/issues/30135. + if ((uint)parseData.Second > 59) + { + value = default; + return false; + } + + Debug.Assert(parseData.Fraction is >= 0 and <= JsonConstants.MaxDateTimeFraction); // All of our callers to date parse the fraction from fixed 7-digit fields so this value is trusted. + + ReadOnlySpan days = DateTime.IsLeapYear(parseData.Year) + ? DaysToMonth366 + : DaysToMonth365; + int yearMinusOne = parseData.Year - 1; + int totalDays = + yearMinusOne * 365 + + yearMinusOne / 4 + - yearMinusOne / 100 + + yearMinusOne / 400 + + days[parseData.Month - 1] + + parseData.Day + - 1; + long ticks = totalDays * TimeSpan.TicksPerDay; + int totalSeconds = parseData.Hour * 3600 + parseData.Minute * 60 + parseData.Second; + ticks += totalSeconds * TimeSpan.TicksPerSecond; + ticks += parseData.Fraction; + value = new DateTime(ticks: ticks, kind: kind); + return true; + } + + private static ReadOnlySpan DaysToMonth365 => + [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365]; + private static ReadOnlySpan DaysToMonth366 => + [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366]; + } + + internal static class ThrowHelper + { + private const string ExceptionSourceValueToRethrowAsJsonException = + "System.Text.Json.Rethrowable"; + + [DoesNotReturn] + public static void ThrowInvalidOperationException_ExpectedString(JsonTokenType tokenType) + { + throw GetInvalidOperationException("string", tokenType); + } + + public static void ThrowFormatException(DataType dataType) + { + throw new FormatException(SR.Format(SR.UnsupportedFormat, dataType)) + { + Source = ExceptionSourceValueToRethrowAsJsonException, + }; + } + + private static global::System.Exception GetInvalidOperationException( + string message, + JsonTokenType tokenType + ) + { + return GetInvalidOperationException(SR.Format(SR.InvalidCast, tokenType, message)); + } + + private static InvalidOperationException GetInvalidOperationException(string message) + { + return new InvalidOperationException(message) + { + Source = ExceptionSourceValueToRethrowAsJsonException, + }; + } + } + + internal static class Utf8JsonReaderExtensions + { + internal static int ValueLength(this Utf8JsonReader reader) => + reader.HasValueSequence + ? checked((int)reader.ValueSequence.Length) + : reader.ValueSpan.Length; + } + + internal enum DataType + { + TimeOnly, + DateOnly, + } + + [SuppressMessage("ReSharper", "InconsistentNaming")] + internal static class SR + { + private static readonly bool s_usingResourceKeys = + AppContext.TryGetSwitch( + "System.Resources.UseSystemResourceKeys", + out bool usingResourceKeys + ) && usingResourceKeys; + + public static string UnsupportedFormat => Strings.UnsupportedFormat; + + public static string InvalidCast => Strings.InvalidCast; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static string Format(string resourceFormat, object? p1) => + s_usingResourceKeys + ? string.Join(", ", resourceFormat, p1) + : string.Format(resourceFormat, p1); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static string Format(string resourceFormat, object? p1, object? p2) => + s_usingResourceKeys + ? string.Join(", ", resourceFormat, p1, p2) + : string.Format(resourceFormat, p1, p2); + } + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute( + "System.Resources.Tools.StronglyTypedResourceBuilder", + "17.0.0.0" + )] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Strings + { + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute( + "Microsoft.Performance", + "CA1811:AvoidUncalledPrivateCode" + )] + internal Strings() { } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute( + global::System.ComponentModel.EditorBrowsableState.Advanced + )] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if (object.ReferenceEquals(resourceMan, null)) + { + global::System.Resources.ResourceManager temp = + new global::System.Resources.ResourceManager( + "System.Text.Json.Resources.Strings", + typeof(Strings).Assembly + ); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute( + global::System.ComponentModel.EditorBrowsableState.Advanced + )] + internal static global::System.Globalization.CultureInfo Culture + { + get { return resourceCulture; } + set { resourceCulture = value; } + } + + /// + /// Looks up a localized string similar to Cannot get the value of a token type '{0}' as a {1}.. + /// + internal static string InvalidCast + { + get { return ResourceManager.GetString("InvalidCast", resourceCulture); } + } + + /// + /// Looks up a localized string similar to The JSON value is not in a supported {0} format.. + /// + internal static string UnsupportedFormat + { + get { return ResourceManager.GetString("UnsupportedFormat", resourceCulture); } + } + } +} diff --git a/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/DateTimeSerializer.cs b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/DateTimeSerializer.cs new file mode 100644 index 000000000000..a0551441c402 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/DateTimeSerializer.cs @@ -0,0 +1,22 @@ +using System.Globalization; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace SeedInferredAuthImplicit.Core; + +internal class DateTimeSerializer : JsonConverter +{ + public override DateTime Read( + ref Utf8JsonReader reader, + global::System.Type typeToConvert, + JsonSerializerOptions options + ) + { + return DateTime.Parse(reader.GetString()!, null, DateTimeStyles.RoundtripKind); + } + + public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.ToString(Constants.DateTimeFormat)); + } +} diff --git a/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/JsonAccessAttribute.cs b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/JsonAccessAttribute.cs new file mode 100644 index 000000000000..a7b16ebab1e8 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/JsonAccessAttribute.cs @@ -0,0 +1,15 @@ +namespace SeedInferredAuthImplicit.Core; + +[global::System.AttributeUsage( + global::System.AttributeTargets.Property | global::System.AttributeTargets.Field +)] +internal class JsonAccessAttribute(JsonAccessType accessType) : global::System.Attribute +{ + internal JsonAccessType AccessType { get; init; } = accessType; +} + +internal enum JsonAccessType +{ + ReadOnly, + WriteOnly, +} diff --git a/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/JsonConfiguration.cs b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/JsonConfiguration.cs new file mode 100644 index 000000000000..deed11ff0ce1 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/JsonConfiguration.cs @@ -0,0 +1,180 @@ +using global::System.Reflection; +using global::System.Text.Json; +using global::System.Text.Json.Nodes; +using global::System.Text.Json.Serialization; +using global::System.Text.Json.Serialization.Metadata; + +namespace SeedInferredAuthImplicit.Core; + +internal static partial class JsonOptions +{ + internal static readonly JsonSerializerOptions JsonSerializerOptions; + + static JsonOptions() + { + var options = new JsonSerializerOptions + { + Converters = { new DateTimeSerializer(), +#if USE_PORTABLE_DATE_ONLY + new DateOnlyConverter(), +#endif + new OneOfSerializer() }, +#if DEBUG + WriteIndented = true, +#endif + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + TypeInfoResolver = new DefaultJsonTypeInfoResolver + { + Modifiers = + { + static typeInfo => + { + if (typeInfo.Kind != JsonTypeInfoKind.Object) + return; + + foreach (var propertyInfo in typeInfo.Properties) + { + var jsonAccessAttribute = propertyInfo + .AttributeProvider?.GetCustomAttributes( + typeof(JsonAccessAttribute), + true + ) + .OfType() + .FirstOrDefault(); + + if (jsonAccessAttribute != null) + { + propertyInfo.IsRequired = false; + switch (jsonAccessAttribute.AccessType) + { + case JsonAccessType.ReadOnly: + propertyInfo.ShouldSerialize = (_, _) => false; + break; + case JsonAccessType.WriteOnly: + propertyInfo.Set = null; + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + + var jsonIgnoreAttribute = propertyInfo + .AttributeProvider?.GetCustomAttributes( + typeof(JsonIgnoreAttribute), + true + ) + .OfType() + .FirstOrDefault(); + + if (jsonIgnoreAttribute is not null) + { + propertyInfo.IsRequired = false; + } + } + + if ( + typeInfo.Kind == JsonTypeInfoKind.Object + && typeInfo.Properties.All(prop => !prop.IsExtensionData) + ) + { + var extensionProp = typeInfo + .Type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic) + .FirstOrDefault(prop => + prop.GetCustomAttribute() != null + ); + + if (extensionProp is not null) + { + var jsonPropertyInfo = typeInfo.CreateJsonPropertyInfo( + extensionProp.FieldType, + extensionProp.Name + ); + jsonPropertyInfo.Get = extensionProp.GetValue; + jsonPropertyInfo.Set = extensionProp.SetValue; + jsonPropertyInfo.IsExtensionData = true; + typeInfo.Properties.Add(jsonPropertyInfo); + } + } + }, + }, + }, + }; + ConfigureJsonSerializerOptions(options); + JsonSerializerOptions = options; + } + + static partial void ConfigureJsonSerializerOptions(JsonSerializerOptions defaultOptions); +} + +internal static class JsonUtils +{ + internal static string Serialize(T obj) => + JsonSerializer.Serialize(obj, JsonOptions.JsonSerializerOptions); + + internal static JsonElement SerializeToElement(T obj) => + JsonSerializer.SerializeToElement(obj, JsonOptions.JsonSerializerOptions); + + internal static JsonDocument SerializeToDocument(T obj) => + JsonSerializer.SerializeToDocument(obj, JsonOptions.JsonSerializerOptions); + + internal static JsonNode? SerializeToNode(T obj) => + JsonSerializer.SerializeToNode(obj, JsonOptions.JsonSerializerOptions); + + internal static byte[] SerializeToUtf8Bytes(T obj) => + JsonSerializer.SerializeToUtf8Bytes(obj, JsonOptions.JsonSerializerOptions); + + internal static string SerializeWithAdditionalProperties( + T obj, + object? additionalProperties = null + ) + { + if (additionalProperties == null) + { + return Serialize(obj); + } + var additionalPropertiesJsonNode = SerializeToNode(additionalProperties); + if (additionalPropertiesJsonNode is not JsonObject additionalPropertiesJsonObject) + { + throw new InvalidOperationException( + "The additional properties must serialize to a JSON object." + ); + } + var jsonNode = SerializeToNode(obj); + if (jsonNode is not JsonObject jsonObject) + { + throw new InvalidOperationException( + "The serialized object must be a JSON object to add properties." + ); + } + MergeJsonObjects(jsonObject, additionalPropertiesJsonObject); + return jsonObject.ToJsonString(JsonOptions.JsonSerializerOptions); + } + + private static void MergeJsonObjects(JsonObject baseObject, JsonObject overrideObject) + { + foreach (var property in overrideObject) + { + if (!baseObject.TryGetPropertyValue(property.Key, out JsonNode? existingValue)) + { + baseObject[property.Key] = + property.Value != null ? JsonNode.Parse(property.Value.ToJsonString()) : null; + continue; + } + if ( + existingValue is JsonObject nestedBaseObject + && property.Value is JsonObject nestedOverrideObject + ) + { + // If both values are objects, recursively merge them. + MergeJsonObjects(nestedBaseObject, nestedOverrideObject); + continue; + } + // Otherwise, the overrideObject takes precedence. + baseObject[property.Key] = + property.Value != null ? JsonNode.Parse(property.Value.ToJsonString()) : null; + } + } + + internal static T Deserialize(string json) => + JsonSerializer.Deserialize(json, JsonOptions.JsonSerializerOptions)!; +} diff --git a/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/OneOfSerializer.cs b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/OneOfSerializer.cs new file mode 100644 index 000000000000..fb1428363660 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/OneOfSerializer.cs @@ -0,0 +1,91 @@ +using System.Reflection; +using System.Text.Json; +using System.Text.Json.Serialization; +using OneOf; + +namespace SeedInferredAuthImplicit.Core; + +internal class OneOfSerializer : JsonConverter +{ + public override IOneOf? Read( + ref Utf8JsonReader reader, + global::System.Type typeToConvert, + JsonSerializerOptions options + ) + { + if (reader.TokenType is JsonTokenType.Null) + return default; + + foreach (var (type, cast) in GetOneOfTypes(typeToConvert)) + { + try + { + var readerCopy = reader; + var result = JsonSerializer.Deserialize(ref readerCopy, type, options); + reader.Skip(); + return (IOneOf)cast.Invoke(null, [result])!; + } + catch (JsonException) { } + } + + throw new JsonException( + $"Cannot deserialize into one of the supported types for {typeToConvert}" + ); + } + + public override void Write(Utf8JsonWriter writer, IOneOf value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.Value, options); + } + + private static (global::System.Type type, MethodInfo cast)[] GetOneOfTypes( + global::System.Type typeToConvert + ) + { + var type = typeToConvert; + if (Nullable.GetUnderlyingType(type) is { } underlyingType) + { + type = underlyingType; + } + + var casts = type.GetRuntimeMethods() + .Where(m => m.IsSpecialName && m.Name == "op_Implicit") + .ToArray(); + while (type != null) + { + if ( + type.IsGenericType + && (type.Name.StartsWith("OneOf`") || type.Name.StartsWith("OneOfBase`")) + ) + { + var genericArguments = type.GetGenericArguments(); + if (genericArguments.Length == 1) + { + return [(genericArguments[0], casts[0])]; + } + + // if object type is present, make sure it is last + var indexOfObjectType = Array.IndexOf(genericArguments, typeof(object)); + if (indexOfObjectType != -1 && genericArguments.Length - 1 != indexOfObjectType) + { + genericArguments = genericArguments + .OrderBy(t => t == typeof(object) ? 1 : 0) + .ToArray(); + } + + return genericArguments + .Select(t => (t, casts.First(c => c.GetParameters()[0].ParameterType == t))) + .ToArray(); + } + + type = type.BaseType; + } + + throw new InvalidOperationException($"{type} isn't OneOf or OneOfBase"); + } + + public override bool CanConvert(global::System.Type typeToConvert) + { + return typeof(IOneOf).IsAssignableFrom(typeToConvert); + } +} diff --git a/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/Public/AdditionalProperties.cs b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/Public/AdditionalProperties.cs new file mode 100644 index 000000000000..4d881bdf77b7 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/Public/AdditionalProperties.cs @@ -0,0 +1,353 @@ +using global::System.Collections; +using global::System.Collections.ObjectModel; +using global::System.Text.Json; +using global::System.Text.Json.Nodes; +using SeedInferredAuthImplicit.Core; + +namespace SeedInferredAuthImplicit; + +public record ReadOnlyAdditionalProperties : ReadOnlyAdditionalProperties +{ + internal ReadOnlyAdditionalProperties() { } + + internal ReadOnlyAdditionalProperties(IDictionary properties) + : base(properties) { } +} + +public record ReadOnlyAdditionalProperties : IReadOnlyDictionary +{ + private readonly Dictionary _extensionData = new(); + private readonly Dictionary _convertedCache = new(); + + internal ReadOnlyAdditionalProperties() + { + _extensionData = new Dictionary(); + _convertedCache = new Dictionary(); + } + + internal ReadOnlyAdditionalProperties(IDictionary properties) + { + _extensionData = new Dictionary(properties.Count); + _convertedCache = new Dictionary(properties.Count); + foreach (var kvp in properties) + { + if (kvp.Value is JsonElement element) + { + _extensionData.Add(kvp.Key, element); + } + else + { + _extensionData[kvp.Key] = JsonUtils.SerializeToElement(kvp.Value); + } + + _convertedCache[kvp.Key] = kvp.Value; + } + } + + private static T ConvertToT(JsonElement value) + { + if (typeof(T) == typeof(JsonElement)) + { + return (T)(object)value; + } + + return value.Deserialize(JsonOptions.JsonSerializerOptions)!; + } + + internal void CopyFromExtensionData(IDictionary extensionData) + { + _extensionData.Clear(); + _convertedCache.Clear(); + foreach (var kvp in extensionData) + { + _extensionData[kvp.Key] = kvp.Value; + if (kvp.Value is T value) + { + _convertedCache[kvp.Key] = value; + } + } + } + + private T GetCached(string key) + { + if (_convertedCache.TryGetValue(key, out var cached)) + { + return cached; + } + + var value = ConvertToT(_extensionData[key]); + _convertedCache[key] = value; + return value; + } + + public IEnumerator> GetEnumerator() + { + return _extensionData + .Select(kvp => new KeyValuePair(kvp.Key, GetCached(kvp.Key))) + .GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public int Count => _extensionData.Count; + + public bool ContainsKey(string key) => _extensionData.ContainsKey(key); + + public bool TryGetValue(string key, out T value) + { + if (_convertedCache.TryGetValue(key, out value!)) + { + return true; + } + + if (_extensionData.TryGetValue(key, out var element)) + { + value = ConvertToT(element); + _convertedCache[key] = value; + return true; + } + + return false; + } + + public T this[string key] => GetCached(key); + + public IEnumerable Keys => _extensionData.Keys; + + public IEnumerable Values => Keys.Select(GetCached); +} + +public record AdditionalProperties : AdditionalProperties +{ + public AdditionalProperties() { } + + public AdditionalProperties(IDictionary properties) + : base(properties) { } +} + +public record AdditionalProperties : IDictionary +{ + private readonly Dictionary _extensionData; + private readonly Dictionary _convertedCache; + + public AdditionalProperties() + { + _extensionData = new Dictionary(); + _convertedCache = new Dictionary(); + } + + public AdditionalProperties(IDictionary properties) + { + _extensionData = new Dictionary(properties.Count); + _convertedCache = new Dictionary(properties.Count); + foreach (var kvp in properties) + { + _extensionData[kvp.Key] = kvp.Value; + _convertedCache[kvp.Key] = kvp.Value; + } + } + + private static T ConvertToT(object? extensionDataValue) + { + return extensionDataValue switch + { + T value => value, + JsonElement jsonElement => jsonElement.Deserialize( + JsonOptions.JsonSerializerOptions + )!, + JsonNode jsonNode => jsonNode.Deserialize(JsonOptions.JsonSerializerOptions)!, + _ => JsonUtils + .SerializeToElement(extensionDataValue) + .Deserialize(JsonOptions.JsonSerializerOptions)!, + }; + } + + internal void CopyFromExtensionData(IDictionary extensionData) + { + _extensionData.Clear(); + _convertedCache.Clear(); + foreach (var kvp in extensionData) + { + _extensionData[kvp.Key] = kvp.Value; + if (kvp.Value is T value) + { + _convertedCache[kvp.Key] = value; + } + } + } + + internal void CopyToExtensionData(IDictionary extensionData) + { + extensionData.Clear(); + foreach (var kvp in _extensionData) + { + extensionData[kvp.Key] = kvp.Value; + } + } + + public JsonObject ToJsonObject() => + ( + JsonUtils.SerializeToNode(_extensionData) + ?? throw new InvalidOperationException( + "Failed to serialize AdditionalProperties to JSON Node." + ) + ).AsObject(); + + public JsonNode ToJsonNode() => + JsonUtils.SerializeToNode(_extensionData) + ?? throw new InvalidOperationException( + "Failed to serialize AdditionalProperties to JSON Node." + ); + + public JsonElement ToJsonElement() => JsonUtils.SerializeToElement(_extensionData); + + public JsonDocument ToJsonDocument() => JsonUtils.SerializeToDocument(_extensionData); + + public IReadOnlyDictionary ToJsonElementDictionary() + { + return new ReadOnlyDictionary( + _extensionData.ToDictionary( + kvp => kvp.Key, + kvp => + { + if (kvp.Value is JsonElement jsonElement) + { + return jsonElement; + } + + return JsonUtils.SerializeToElement(kvp.Value); + } + ) + ); + } + + public ICollection Keys => _extensionData.Keys; + + public ICollection Values + { + get + { + var values = new T[_extensionData.Count]; + var i = 0; + foreach (var key in Keys) + { + values[i++] = GetCached(key); + } + + return values; + } + } + + private T GetCached(string key) + { + if (_convertedCache.TryGetValue(key, out var value)) + { + return value; + } + + value = ConvertToT(_extensionData[key]); + _convertedCache.Add(key, value); + return value; + } + + private void SetCached(string key, T value) + { + _extensionData[key] = value; + _convertedCache[key] = value; + } + + private void AddCached(string key, T value) + { + _extensionData.Add(key, value); + _convertedCache.Add(key, value); + } + + private bool RemoveCached(string key) + { + var isRemoved = _extensionData.Remove(key); + _convertedCache.Remove(key); + return isRemoved; + } + + public int Count => _extensionData.Count; + public bool IsReadOnly => false; + + public T this[string key] + { + get => GetCached(key); + set => SetCached(key, value); + } + + public void Add(string key, T value) => AddCached(key, value); + + public void Add(KeyValuePair item) => AddCached(item.Key, item.Value); + + public bool Remove(string key) => RemoveCached(key); + + public bool Remove(KeyValuePair item) => RemoveCached(item.Key); + + public bool ContainsKey(string key) => _extensionData.ContainsKey(key); + + public bool Contains(KeyValuePair item) + { + return _extensionData.ContainsKey(item.Key) + && EqualityComparer.Default.Equals(GetCached(item.Key), item.Value); + } + + public bool TryGetValue(string key, out T value) + { + if (_convertedCache.TryGetValue(key, out value!)) + { + return true; + } + + if (_extensionData.TryGetValue(key, out var extensionDataValue)) + { + value = ConvertToT(extensionDataValue); + _convertedCache[key] = value; + return true; + } + + return false; + } + + public void Clear() + { + _extensionData.Clear(); + _convertedCache.Clear(); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + if (array is null) + { + throw new ArgumentNullException(nameof(array)); + } + + if (arrayIndex < 0 || arrayIndex > array.Length) + { + throw new ArgumentOutOfRangeException(nameof(arrayIndex)); + } + + if (array.Length - arrayIndex < _extensionData.Count) + { + throw new ArgumentException( + "The array does not have enough space to copy the elements." + ); + } + + foreach (var kvp in _extensionData) + { + array[arrayIndex++] = new KeyValuePair(kvp.Key, GetCached(kvp.Key)); + } + } + + public IEnumerator> GetEnumerator() + { + return _extensionData + .Select(kvp => new KeyValuePair(kvp.Key, GetCached(kvp.Key))) + .GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); +} diff --git a/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/Public/FileParameter.cs b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/Public/FileParameter.cs new file mode 100644 index 000000000000..34a7ae32ebd1 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/Public/FileParameter.cs @@ -0,0 +1,63 @@ +namespace SeedInferredAuthImplicit; + +/// +/// File parameter for uploading files. +/// +public record FileParameter : IDisposable +#if NET6_0_OR_GREATER + , IAsyncDisposable +#endif +{ + private bool _disposed; + + /// + /// The name of the file to be uploaded. + /// + public string? FileName { get; set; } + + /// + /// The content type of the file to be uploaded. + /// + public string? ContentType { get; set; } + + /// + /// The content of the file to be uploaded. + /// + public required Stream Stream { get; set; } + + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + protected virtual void Dispose(bool disposing) + { + if (_disposed) + return; + if (disposing) + { + Stream.Dispose(); + } + + _disposed = true; + } + +#if NET6_0_OR_GREATER + /// + public async ValueTask DisposeAsync() + { + if (!_disposed) + { + await Stream.DisposeAsync().ConfigureAwait(false); + _disposed = true; + } + + GC.SuppressFinalize(this); + } +#endif + + public static implicit operator FileParameter(Stream stream) => new() { Stream = stream }; +} diff --git a/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/Public/Version.cs b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/Public/Version.cs new file mode 100644 index 000000000000..e6999e7e080d --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/Public/Version.cs @@ -0,0 +1,7 @@ +namespace SeedInferredAuthImplicit; + +[Serializable] +internal class Version +{ + public const string Current = "0.0.1"; +} diff --git a/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/StringEnum.cs b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/StringEnum.cs new file mode 100644 index 000000000000..f2dbf4a377fc --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/StringEnum.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace SeedInferredAuthImplicit.Core; + +public interface IStringEnum : IEquatable +{ + public string Value { get; } +} diff --git a/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/StringEnumExtensions.cs b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/StringEnumExtensions.cs new file mode 100644 index 000000000000..6b8070df3e70 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/StringEnumExtensions.cs @@ -0,0 +1,6 @@ +namespace SeedInferredAuthImplicit.Core; + +internal static class StringEnumExtensions +{ + public static string Stringify(this IStringEnum stringEnum) => stringEnum.Value; +} diff --git a/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/StringEnumSerializer.cs b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/StringEnumSerializer.cs new file mode 100644 index 000000000000..098356479f88 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/StringEnumSerializer.cs @@ -0,0 +1,25 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace SeedInferredAuthImplicit.Core; + +internal class StringEnumSerializer : JsonConverter + where T : IStringEnum +{ + public override T? Read( + ref Utf8JsonReader reader, + global::System.Type typeToConvert, + JsonSerializerOptions options + ) + { + var stringValue = + reader.GetString() + ?? throw new global::System.Exception("The JSON value could not be read as a string."); + return (T?)Activator.CreateInstance(typeToConvert, stringValue); + } + + public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.Value); + } +} diff --git a/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/SeedInferredAuthImplicit.Custom.props b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/SeedInferredAuthImplicit.Custom.props new file mode 100644 index 000000000000..17a84cada530 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/SeedInferredAuthImplicit.Custom.props @@ -0,0 +1,20 @@ + + + + diff --git a/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/SeedInferredAuthImplicit.csproj b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/SeedInferredAuthImplicit.csproj new file mode 100644 index 000000000000..b763226e2269 --- /dev/null +++ b/seed/csharp-model/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/SeedInferredAuthImplicit.csproj @@ -0,0 +1,61 @@ + + + net462;net8.0;net7.0;net6.0;netstandard2.0 + enable + 12 + enable + 0.0.1 + $(Version) + $(Version) + README.md + https://github.com/inferred-auth-implicit-reference/fern + true + + + + false + + + $(DefineConstants);USE_PORTABLE_DATE_ONLY + true + + + + + + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + + + + + <_Parameter1>SeedInferredAuthImplicit.Test + + + + + diff --git a/seed/csharp-model/seed.yml b/seed/csharp-model/seed.yml index 799094cd8300..f842dbdcd42f 100644 --- a/seed/csharp-model/seed.yml +++ b/seed/csharp-model/seed.yml @@ -1,4 +1,4 @@ -irVersion: v61 +irVersion: v62 displayName: C# Model image: fernapi/fern-csharp-model changelogLocation: ../../generators/csharp/model/versions.yml diff --git a/seed/csharp-sdk/inferred-auth-explicit/README.md b/seed/csharp-sdk/inferred-auth-explicit/README.md index 67f542b980fa..79bb955826cf 100644 --- a/seed/csharp-sdk/inferred-auth-explicit/README.md +++ b/seed/csharp-sdk/inferred-auth-explicit/README.md @@ -38,7 +38,7 @@ Instantiate and use the client with the following: ```csharp using SeedInferredAuthExplicit; -var client = new SeedInferredAuthExplicitClient(); +var client = new SeedInferredAuthExplicitClient("X-Api-Key", "client_id", "client_secret", "scope"); await client.Auth.GetTokenWithClientCredentialsAsync( new GetTokenRequest { diff --git a/seed/csharp-sdk/inferred-auth-explicit/snippet.json b/seed/csharp-sdk/inferred-auth-explicit/snippet.json index ce7260641571..b0aff322da2d 100644 --- a/seed/csharp-sdk/inferred-auth-explicit/snippet.json +++ b/seed/csharp-sdk/inferred-auth-explicit/snippet.json @@ -10,7 +10,7 @@ }, "snippet": { "type": "csharp", - "client": "using SeedInferredAuthExplicit;\n\nvar client = new SeedInferredAuthExplicitClient();\nawait client.Auth.GetTokenWithClientCredentialsAsync(\n new GetTokenRequest\n {\n XApiKey = \"X-Api-Key\",\n ClientId = \"client_id\",\n ClientSecret = \"client_secret\",\n Audience = \"https://api.example.com\",\n GrantType = \"client_credentials\",\n Scope = \"scope\",\n }\n);\n" + "client": "using SeedInferredAuthExplicit;\n\nvar client = new SeedInferredAuthExplicitClient(\"X-Api-Key\", \"client_id\", \"client_secret\", \"scope\");\nawait client.Auth.GetTokenWithClientCredentialsAsync(\n new GetTokenRequest\n {\n XApiKey = \"X-Api-Key\",\n ClientId = \"client_id\",\n ClientSecret = \"client_secret\",\n Audience = \"https://api.example.com\",\n GrantType = \"client_credentials\",\n Scope = \"scope\",\n }\n);\n" } }, { @@ -22,7 +22,7 @@ }, "snippet": { "type": "csharp", - "client": "using SeedInferredAuthExplicit;\n\nvar client = new SeedInferredAuthExplicitClient();\nawait client.Auth.RefreshTokenAsync(\n new RefreshTokenRequest\n {\n XApiKey = \"X-Api-Key\",\n ClientId = \"client_id\",\n ClientSecret = \"client_secret\",\n RefreshToken = \"refresh_token\",\n Audience = \"https://api.example.com\",\n GrantType = \"refresh_token\",\n Scope = \"scope\",\n }\n);\n" + "client": "using SeedInferredAuthExplicit;\n\nvar client = new SeedInferredAuthExplicitClient(\"X-Api-Key\", \"client_id\", \"client_secret\", \"scope\");\nawait client.Auth.RefreshTokenAsync(\n new RefreshTokenRequest\n {\n XApiKey = \"X-Api-Key\",\n ClientId = \"client_id\",\n ClientSecret = \"client_secret\",\n RefreshToken = \"refresh_token\",\n Audience = \"https://api.example.com\",\n GrantType = \"refresh_token\",\n Scope = \"scope\",\n }\n);\n" } }, { @@ -34,7 +34,7 @@ }, "snippet": { "type": "csharp", - "client": "using SeedInferredAuthExplicit;\n\nvar client = new SeedInferredAuthExplicitClient();\nawait client.NestedNoAuth.Api.GetSomethingAsync();\n" + "client": "using SeedInferredAuthExplicit;\n\nvar client = new SeedInferredAuthExplicitClient(\"X-Api-Key\", \"client_id\", \"client_secret\", \"scope\");\nawait client.NestedNoAuth.Api.GetSomethingAsync();\n" } }, { @@ -46,7 +46,7 @@ }, "snippet": { "type": "csharp", - "client": "using SeedInferredAuthExplicit;\n\nvar client = new SeedInferredAuthExplicitClient();\nawait client.Nested.Api.GetSomethingAsync();\n" + "client": "using SeedInferredAuthExplicit;\n\nvar client = new SeedInferredAuthExplicitClient(\"X-Api-Key\", \"client_id\", \"client_secret\", \"scope\");\nawait client.Nested.Api.GetSomethingAsync();\n" } }, { @@ -58,7 +58,7 @@ }, "snippet": { "type": "csharp", - "client": "using SeedInferredAuthExplicit;\n\nvar client = new SeedInferredAuthExplicitClient();\nawait client.Simple.GetSomethingAsync();\n" + "client": "using SeedInferredAuthExplicit;\n\nvar client = new SeedInferredAuthExplicitClient(\"X-Api-Key\", \"client_id\", \"client_secret\", \"scope\");\nawait client.Simple.GetSomethingAsync();\n" } } ] diff --git a/seed/csharp-sdk/inferred-auth-explicit/src/SeedApi.DynamicSnippets/Example0.cs b/seed/csharp-sdk/inferred-auth-explicit/src/SeedApi.DynamicSnippets/Example0.cs index a2972ad5da45..e114a9c57b27 100644 --- a/seed/csharp-sdk/inferred-auth-explicit/src/SeedApi.DynamicSnippets/Example0.cs +++ b/seed/csharp-sdk/inferred-auth-explicit/src/SeedApi.DynamicSnippets/Example0.cs @@ -6,6 +6,10 @@ public class Example0 { public async Task Do() { var client = new SeedInferredAuthExplicitClient( + xApiKey: "X-Api-Key", + clientId: "client_id", + clientSecret: "client_secret", + scope: "scope", clientOptions: new ClientOptions { BaseUrl = "https://api.fern.com" } diff --git a/seed/csharp-sdk/inferred-auth-explicit/src/SeedApi.DynamicSnippets/Example1.cs b/seed/csharp-sdk/inferred-auth-explicit/src/SeedApi.DynamicSnippets/Example1.cs index c2ea123ca5ce..69cb1acc22f3 100644 --- a/seed/csharp-sdk/inferred-auth-explicit/src/SeedApi.DynamicSnippets/Example1.cs +++ b/seed/csharp-sdk/inferred-auth-explicit/src/SeedApi.DynamicSnippets/Example1.cs @@ -6,6 +6,10 @@ public class Example1 { public async Task Do() { var client = new SeedInferredAuthExplicitClient( + xApiKey: "X-Api-Key", + clientId: "client_id", + clientSecret: "client_secret", + scope: "scope", clientOptions: new ClientOptions { BaseUrl = "https://api.fern.com" } diff --git a/seed/csharp-sdk/inferred-auth-explicit/src/SeedApi.DynamicSnippets/Example2.cs b/seed/csharp-sdk/inferred-auth-explicit/src/SeedApi.DynamicSnippets/Example2.cs index f16b1d099045..9e02f36fdcd5 100644 --- a/seed/csharp-sdk/inferred-auth-explicit/src/SeedApi.DynamicSnippets/Example2.cs +++ b/seed/csharp-sdk/inferred-auth-explicit/src/SeedApi.DynamicSnippets/Example2.cs @@ -6,6 +6,10 @@ public class Example2 { public async Task Do() { var client = new SeedInferredAuthExplicitClient( + xApiKey: "X-Api-Key", + clientId: "client_id", + clientSecret: "client_secret", + scope: "scope", clientOptions: new ClientOptions { BaseUrl = "https://api.fern.com" } diff --git a/seed/csharp-sdk/inferred-auth-explicit/src/SeedApi.DynamicSnippets/Example3.cs b/seed/csharp-sdk/inferred-auth-explicit/src/SeedApi.DynamicSnippets/Example3.cs index 47e4d32dbf6b..585abd2e50f5 100644 --- a/seed/csharp-sdk/inferred-auth-explicit/src/SeedApi.DynamicSnippets/Example3.cs +++ b/seed/csharp-sdk/inferred-auth-explicit/src/SeedApi.DynamicSnippets/Example3.cs @@ -6,6 +6,10 @@ public class Example3 { public async Task Do() { var client = new SeedInferredAuthExplicitClient( + xApiKey: "X-Api-Key", + clientId: "client_id", + clientSecret: "client_secret", + scope: "scope", clientOptions: new ClientOptions { BaseUrl = "https://api.fern.com" } diff --git a/seed/csharp-sdk/inferred-auth-explicit/src/SeedApi.DynamicSnippets/Example4.cs b/seed/csharp-sdk/inferred-auth-explicit/src/SeedApi.DynamicSnippets/Example4.cs index cf546144aac2..bd36c0f2eb5d 100644 --- a/seed/csharp-sdk/inferred-auth-explicit/src/SeedApi.DynamicSnippets/Example4.cs +++ b/seed/csharp-sdk/inferred-auth-explicit/src/SeedApi.DynamicSnippets/Example4.cs @@ -6,6 +6,10 @@ public class Example4 { public async Task Do() { var client = new SeedInferredAuthExplicitClient( + xApiKey: "X-Api-Key", + clientId: "client_id", + clientSecret: "client_secret", + scope: "scope", clientOptions: new ClientOptions { BaseUrl = "https://api.fern.com" } diff --git a/seed/csharp-sdk/inferred-auth-explicit/src/SeedInferredAuthExplicit.Test/Unit/MockServer/BaseMockServerTest.cs b/seed/csharp-sdk/inferred-auth-explicit/src/SeedInferredAuthExplicit.Test/Unit/MockServer/BaseMockServerTest.cs index 4fdd6a1d6322..91d721f5ec54 100644 --- a/seed/csharp-sdk/inferred-auth-explicit/src/SeedInferredAuthExplicit.Test/Unit/MockServer/BaseMockServerTest.cs +++ b/seed/csharp-sdk/inferred-auth-explicit/src/SeedInferredAuthExplicit.Test/Unit/MockServer/BaseMockServerTest.cs @@ -15,6 +15,43 @@ public class BaseMockServerTest protected static RequestOptions RequestOptions { get; set; } = new(); + private void MockInferredAuthEndpoint() + { + const string requestJson = """ + { + "client_id": "client_id", + "client_secret": "client_secret", + "audience": "https://api.example.com", + "grant_type": "client_credentials", + "scope": "scope" + } + """; + + const string mockResponse = """ + { + "access_token": "access_token", + "expires_in": 1, + "refresh_token": "refresh_token" + } + """; + + Server + .Given( + WireMock + .RequestBuilders.Request.Create() + .WithPath("/token") + .WithHeader("X-Api-Key", "X-Api-Key") + .UsingPost() + .WithBodyAsJson(requestJson) + ) + .RespondWith( + WireMock + .ResponseBuilders.Response.Create() + .WithStatusCode(200) + .WithBody(mockResponse) + ); + } + [OneTimeSetUp] public void GlobalSetup() { @@ -25,8 +62,13 @@ public void GlobalSetup() // Initialize the Client Client = new SeedInferredAuthExplicitClient( + "X-Api-Key", + "client_id", + "client_secret", + "scope", clientOptions: new ClientOptions { BaseUrl = Server.Urls[0], MaxRetries = 0 } ); + MockInferredAuthEndpoint(); } [OneTimeTearDown] diff --git a/seed/csharp-sdk/inferred-auth-explicit/src/SeedInferredAuthExplicit/Core/InferredAuthTokenProvider.cs b/seed/csharp-sdk/inferred-auth-explicit/src/SeedInferredAuthExplicit/Core/InferredAuthTokenProvider.cs new file mode 100644 index 000000000000..fdfab6bf406a --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-explicit/src/SeedInferredAuthExplicit/Core/InferredAuthTokenProvider.cs @@ -0,0 +1,83 @@ +using SeedInferredAuthExplicit; + +namespace SeedInferredAuthExplicit.Core; + +internal partial class InferredAuthTokenProvider +{ + private const double BufferInMinutes = 2; + + private AuthClient _client; + + private IDictionary? _cachedHeaders; + + private DateTime? _expiresAt; + + private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1); + + private string _xApiKey; + + private string _clientId; + + private string _clientSecret; + + private string? _scope; + + internal InferredAuthTokenProvider( + string xApiKey, + string clientId, + string clientSecret, + string? scope, + AuthClient client + ) + { + _xApiKey = xApiKey; + _clientId = clientId; + _clientSecret = clientSecret; + _scope = scope; + _client = client; + } + + internal async Task> GetAuthHeadersAsync() + { + if (_cachedHeaders == null || DateTime.UtcNow >= _expiresAt) + { + await _lock.WaitAsync().ConfigureAwait(false); + try + { + if (_cachedHeaders == null || DateTime.UtcNow >= _expiresAt) + { + try + { + var tokenResponse = await _client + .GetTokenWithClientCredentialsAsync( + new GetTokenRequest + { + XApiKey = _xApiKey, + ClientId = _clientId, + ClientSecret = _clientSecret, + Scope = _scope, + } + ) + .ConfigureAwait(false); + _cachedHeaders = new Dictionary(); + _cachedHeaders["Authorization"] = $"Bearer {tokenResponse.AccessToken}"; + _expiresAt = DateTime + .UtcNow.AddSeconds(tokenResponse.ExpiresIn) + .AddMinutes(-BufferInMinutes); + } + catch + { + _cachedHeaders = null; + _expiresAt = null; + throw; + } + } + } + finally + { + _lock.Release(); + } + } + return _cachedHeaders; + } +} diff --git a/seed/csharp-sdk/inferred-auth-explicit/src/SeedInferredAuthExplicit/SeedInferredAuthExplicitClient.cs b/seed/csharp-sdk/inferred-auth-explicit/src/SeedInferredAuthExplicit/SeedInferredAuthExplicitClient.cs index 31c7a80f93ab..afca807c986a 100644 --- a/seed/csharp-sdk/inferred-auth-explicit/src/SeedInferredAuthExplicit/SeedInferredAuthExplicitClient.cs +++ b/seed/csharp-sdk/inferred-auth-explicit/src/SeedInferredAuthExplicit/SeedInferredAuthExplicitClient.cs @@ -8,7 +8,13 @@ public partial class SeedInferredAuthExplicitClient { private readonly RawClient _client; - public SeedInferredAuthExplicitClient(ClientOptions? clientOptions = null) + public SeedInferredAuthExplicitClient( + string xApiKey, + string clientId, + string clientSecret, + string? scope = null, + ClientOptions? clientOptions = null + ) { var defaultHeaders = new Headers( new Dictionary() @@ -27,6 +33,19 @@ public SeedInferredAuthExplicitClient(ClientOptions? clientOptions = null) clientOptions.Headers[header.Key] = header.Value; } } + var inferredAuthProvider = new InferredAuthTokenProvider( + xApiKey, + clientId, + clientSecret, + scope, + new AuthClient(new RawClient(clientOptions.Clone())) + ); + clientOptions.Headers["Authorization"] = + new Func>(async () => + (await inferredAuthProvider.GetAuthHeadersAsync().ConfigureAwait(false)) + .First() + .Value + ); _client = new RawClient(clientOptions); Auth = new AuthClient(_client); NestedNoAuth = new NestedNoAuthClient(_client); diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/.editorconfig b/seed/csharp-sdk/inferred-auth-implicit-api-key/.editorconfig new file mode 100644 index 000000000000..1e7a0adbac80 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/.editorconfig @@ -0,0 +1,35 @@ +root = true + +[*.cs] +resharper_arrange_object_creation_when_type_evident_highlighting = hint +resharper_auto_property_can_be_made_get_only_global_highlighting = hint +resharper_check_namespace_highlighting = hint +resharper_class_never_instantiated_global_highlighting = hint +resharper_class_never_instantiated_local_highlighting = hint +resharper_collection_never_updated_global_highlighting = hint +resharper_convert_type_check_pattern_to_null_check_highlighting = hint +resharper_inconsistent_naming_highlighting = hint +resharper_member_can_be_private_global_highlighting = hint +resharper_member_hides_static_from_outer_class_highlighting = hint +resharper_not_accessed_field_local_highlighting = hint +resharper_nullable_warning_suppression_is_used_highlighting = suggestion +resharper_partial_type_with_single_part_highlighting = hint +resharper_prefer_concrete_value_over_default_highlighting = none +resharper_private_field_can_be_converted_to_local_variable_highlighting = hint +resharper_property_can_be_made_init_only_global_highlighting = hint +resharper_property_can_be_made_init_only_local_highlighting = hint +resharper_redundant_name_qualifier_highlighting = none +resharper_redundant_using_directive_highlighting = hint +resharper_replace_slice_with_range_indexer_highlighting = none +resharper_unused_auto_property_accessor_global_highlighting = hint +resharper_unused_auto_property_accessor_local_highlighting = hint +resharper_unused_member_global_highlighting = hint +resharper_unused_type_global_highlighting = hint +resharper_use_string_interpolation_highlighting = hint +dotnet_diagnostic.CS1591.severity = suggestion + +[src/**/Types/*.cs] +resharper_check_namespace_highlighting = none + +[src/**/Core/Public/*.cs] +resharper_check_namespace_highlighting = none \ No newline at end of file diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/.fern/metadata.json b/seed/csharp-sdk/inferred-auth-implicit-api-key/.fern/metadata.json new file mode 100644 index 000000000000..c48ce7a5a66a --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/.fern/metadata.json @@ -0,0 +1,6 @@ +{ + "cliVersion": "DUMMY", + "generatorName": "fernapi/fern-csharp-sdk", + "generatorVersion": "latest", + "generatorConfig": {} +} \ No newline at end of file diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/.github/workflows/ci.yml b/seed/csharp-sdk/inferred-auth-implicit-api-key/.github/workflows/ci.yml new file mode 100644 index 000000000000..1ff248b5263a --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/.github/workflows/ci.yml @@ -0,0 +1,97 @@ +name: ci + +on: [push] + +env: + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true + DOTNET_NOLOGO: true + +jobs: + compile: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v5 + + - uses: actions/checkout@master + + - name: Setup .NET + uses: actions/setup-dotnet@v5 + with: + dotnet-version: 10.x + + - name: Print .NET info + run: dotnet --info + + - name: Install tools + run: dotnet tool restore + + - name: Restore dependencies + run: dotnet restore src/SeedInferredAuthImplicitApiKey/SeedInferredAuthImplicitApiKey.csproj + + - name: Build Release + run: dotnet build src/SeedInferredAuthImplicitApiKey/SeedInferredAuthImplicitApiKey.csproj -c Release --no-restore + + unit-tests: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v5 + + - name: Setup .NET + uses: actions/setup-dotnet@v5 + with: + dotnet-version: 10.x + + - name: Print .NET info + run: dotnet --info + + - name: Install tools + run: | + dotnet tool restore + + - name: Restore dependencies + run: dotnet restore src/SeedInferredAuthImplicitApiKey.Test/SeedInferredAuthImplicitApiKey.Test.csproj + + - name: Build Release + run: dotnet build src/SeedInferredAuthImplicitApiKey.Test/SeedInferredAuthImplicitApiKey.Test.csproj -c Release --no-restore + + - name: Run Tests + run: dotnet test src/SeedInferredAuthImplicitApiKey.Test/SeedInferredAuthImplicitApiKey.Test.csproj -c Release --no-build --no-restore + + + publish: + needs: [compile] + if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v5 + + - name: Setup .NET + uses: actions/setup-dotnet@v5 + with: + dotnet-version: 10.x + + - name: Print .NET info + run: dotnet --info + + - name: Install tools + run: dotnet tool restore + + - name: Restore dependencies + run: dotnet restore src/SeedInferredAuthImplicitApiKey/SeedInferredAuthImplicitApiKey.csproj + + - name: Build Release + run: dotnet build src/SeedInferredAuthImplicitApiKey/SeedInferredAuthImplicitApiKey.csproj -c Release --no-restore + + - name: Publish + env: + NUGET_API_KEY: ${{ secrets.NUGET_API_TOKEN }} + run: | + dotnet pack src/SeedInferredAuthImplicitApiKey/SeedInferredAuthImplicitApiKey.csproj -c Release --no-build --no-restore + dotnet nuget push src/SeedInferredAuthImplicitApiKey/bin/Release/*.nupkg --api-key $NUGET_API_KEY --source "nuget.org" + diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/.gitignore b/seed/csharp-sdk/inferred-auth-implicit-api-key/.gitignore new file mode 100644 index 000000000000..11014f2b33d7 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/.gitignore @@ -0,0 +1,484 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +## This is based on `dotnet new gitignore` and customized by Fern + +# dotenv files +.env + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +# [Rr]elease/ (Ignored by Fern) +# [Rr]eleases/ (Ignored by Fern) +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +# [Ll]og/ (Ignored by Fern) +# [Ll]ogs/ (Ignored by Fern) + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET +project.lock.json +project.fragment.lock.json +artifacts/ + +# Tye +.tye/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml +.idea + +## +## Visual studio for Mac +## + + +# globs +Makefile.in +*.userprefs +*.usertasks +config.make +config.status +aclocal.m4 +install-sh +autom4te.cache/ +*.tar.gz +tarballs/ +test-results/ + +# Mac bundle stuff +*.dmg +*.app + +# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# Vim temporary swap files +*.swp diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/README.md b/seed/csharp-sdk/inferred-auth-implicit-api-key/README.md new file mode 100644 index 000000000000..a1fa046a56dd --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/README.md @@ -0,0 +1,107 @@ +# Seed C# Library + +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FC%23) +[![nuget shield](https://img.shields.io/nuget/v/SeedInferredAuthImplicitApiKey)](https://nuget.org/packages/SeedInferredAuthImplicitApiKey) + +The Seed C# library provides convenient access to the Seed APIs from C#. + +## Table of Contents + +- [Requirements](#requirements) +- [Installation](#installation) +- [Reference](#reference) +- [Usage](#usage) +- [Exception Handling](#exception-handling) +- [Advanced](#advanced) + - [Retries](#retries) + - [Timeouts](#timeouts) +- [Contributing](#contributing) + +## Requirements + +This SDK requires: + +## Installation + +```sh +dotnet add package SeedInferredAuthImplicitApiKey +``` + +## Reference + +A full reference for this library is available [here](./reference.md). + +## Usage + +Instantiate and use the client with the following: + +```csharp +using SeedInferredAuthImplicitApiKey; + +var client = new SeedInferredAuthImplicitApiKeyClient("X-Api-Key"); +await client.Auth.GetTokenAsync(new GetTokenRequest { ApiKey = "api_key" }); +``` + +## Exception Handling + +When the API returns a non-success status code (4xx or 5xx response), a subclass of the following error +will be thrown. + +```csharp +using SeedInferredAuthImplicitApiKey; + +try { + var response = await client.Auth.GetTokenAsync(...); +} catch (SeedInferredAuthImplicitApiKeyApiException e) { + System.Console.WriteLine(e.Body); + System.Console.WriteLine(e.StatusCode); +} +``` + +## Advanced + +### Retries + +The SDK is instrumented with automatic retries with exponential backoff. A request will be retried as long +as the request is deemed retryable and the number of retry attempts has not grown larger than the configured +retry limit (default: 2). + +A request is deemed retryable when any of the following HTTP status codes is returned: + +- [408](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/408) (Timeout) +- [429](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429) (Too Many Requests) +- [5XX](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500) (Internal Server Errors) + +Use the `MaxRetries` request option to configure this behavior. + +```csharp +var response = await client.Auth.GetTokenAsync( + ..., + new RequestOptions { + MaxRetries: 0 // Override MaxRetries at the request level + } +); +``` + +### Timeouts + +The SDK defaults to a 30 second timeout. Use the `Timeout` option to configure this behavior. + +```csharp +var response = await client.Auth.GetTokenAsync( + ..., + new RequestOptions { + Timeout: TimeSpan.FromSeconds(3) // Override timeout to 3s + } +); +``` + +## Contributing + +While we value open-source contributions to this SDK, this library is generated programmatically. +Additions made directly to this library would have to be moved over to our generation code, +otherwise they would be overwritten upon the next generated release. Feel free to open a PR as +a proof of concept, but know that we will not be able to merge it as-is. We suggest opening +an issue first to discuss with us! + +On the other hand, contributions to the README are always very welcome! \ No newline at end of file diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/SeedInferredAuthImplicitApiKey.slnx b/seed/csharp-sdk/inferred-auth-implicit-api-key/SeedInferredAuthImplicitApiKey.slnx new file mode 100644 index 000000000000..c990fc94b736 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/SeedInferredAuthImplicitApiKey.slnx @@ -0,0 +1,4 @@ + + + + diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/reference.md b/seed/csharp-sdk/inferred-auth-implicit-api-key/reference.md new file mode 100644 index 000000000000..afdadc42eb5a --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/reference.md @@ -0,0 +1,119 @@ +# Reference +## Auth +
client.Auth.GetTokenAsync(GetTokenRequest { ... }) -> TokenResponse +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```csharp +await client.Auth.GetTokenAsync(new GetTokenRequest { ApiKey = "api_key" }); +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `GetTokenRequest` + +
+
+
+
+ + +
+
+
+ +## NestedNoAuth Api +
client.NestedNoAuth.Api.GetSomethingAsync() +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```csharp +await client.NestedNoAuth.Api.GetSomethingAsync(); +``` +
+
+
+
+ + +
+
+
+ +## Nested Api +
client.Nested.Api.GetSomethingAsync() +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```csharp +await client.Nested.Api.GetSomethingAsync(); +``` +
+
+
+
+ + +
+
+
+ +## Simple +
client.Simple.GetSomethingAsync() +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```csharp +await client.Simple.GetSomethingAsync(); +``` +
+
+
+
+ + +
+
+
diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/snippet.json b/seed/csharp-sdk/inferred-auth-implicit-api-key/snippet.json new file mode 100644 index 000000000000..26152f6e3460 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/snippet.json @@ -0,0 +1,53 @@ +{ + "types": {}, + "endpoints": [ + { + "example_identifier": null, + "id": { + "path": "/token", + "method": "POST", + "identifier_override": "endpoint_auth.getToken" + }, + "snippet": { + "type": "csharp", + "client": "using SeedInferredAuthImplicitApiKey;\n\nvar client = new SeedInferredAuthImplicitApiKeyClient(\"X-Api-Key\");\nawait client.Auth.GetTokenAsync(new GetTokenRequest { ApiKey = \"api_key\" });\n" + } + }, + { + "example_identifier": null, + "id": { + "path": "/nested-no-auth/get-something", + "method": "GET", + "identifier_override": "endpoint_nested-no-auth/api.getSomething" + }, + "snippet": { + "type": "csharp", + "client": "using SeedInferredAuthImplicitApiKey;\n\nvar client = new SeedInferredAuthImplicitApiKeyClient(\"X-Api-Key\");\nawait client.NestedNoAuth.Api.GetSomethingAsync();\n" + } + }, + { + "example_identifier": null, + "id": { + "path": "/nested/get-something", + "method": "GET", + "identifier_override": "endpoint_nested/api.getSomething" + }, + "snippet": { + "type": "csharp", + "client": "using SeedInferredAuthImplicitApiKey;\n\nvar client = new SeedInferredAuthImplicitApiKeyClient(\"X-Api-Key\");\nawait client.Nested.Api.GetSomethingAsync();\n" + } + }, + { + "example_identifier": null, + "id": { + "path": "/get-something", + "method": "GET", + "identifier_override": "endpoint_simple.getSomething" + }, + "snippet": { + "type": "csharp", + "client": "using SeedInferredAuthImplicitApiKey;\n\nvar client = new SeedInferredAuthImplicitApiKeyClient(\"X-Api-Key\");\nawait client.Simple.GetSomethingAsync();\n" + } + } + ] +} \ No newline at end of file diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedApi.DynamicSnippets/Example0.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedApi.DynamicSnippets/Example0.cs new file mode 100644 index 000000000000..bd22d9f47160 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedApi.DynamicSnippets/Example0.cs @@ -0,0 +1,22 @@ +using SeedInferredAuthImplicitApiKey; + +namespace Usage; + +public class Example0 +{ + public async Task Do() { + var client = new SeedInferredAuthImplicitApiKeyClient( + apiKey: "X-Api-Key", + clientOptions: new ClientOptions { + BaseUrl = "https://api.fern.com" + } + ); + + await client.Auth.GetTokenAsync( + new GetTokenRequest { + ApiKey = "api_key" + } + ); + } + +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedApi.DynamicSnippets/Example1.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedApi.DynamicSnippets/Example1.cs new file mode 100644 index 000000000000..d75d7f40ebbe --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedApi.DynamicSnippets/Example1.cs @@ -0,0 +1,18 @@ +using SeedInferredAuthImplicitApiKey; + +namespace Usage; + +public class Example1 +{ + public async Task Do() { + var client = new SeedInferredAuthImplicitApiKeyClient( + apiKey: "X-Api-Key", + clientOptions: new ClientOptions { + BaseUrl = "https://api.fern.com" + } + ); + + await client.NestedNoAuth.Api.GetSomethingAsync(); + } + +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedApi.DynamicSnippets/Example2.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedApi.DynamicSnippets/Example2.cs new file mode 100644 index 000000000000..c61e724daa11 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedApi.DynamicSnippets/Example2.cs @@ -0,0 +1,18 @@ +using SeedInferredAuthImplicitApiKey; + +namespace Usage; + +public class Example2 +{ + public async Task Do() { + var client = new SeedInferredAuthImplicitApiKeyClient( + apiKey: "X-Api-Key", + clientOptions: new ClientOptions { + BaseUrl = "https://api.fern.com" + } + ); + + await client.Nested.Api.GetSomethingAsync(); + } + +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedApi.DynamicSnippets/Example3.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedApi.DynamicSnippets/Example3.cs new file mode 100644 index 000000000000..efb844ef9502 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedApi.DynamicSnippets/Example3.cs @@ -0,0 +1,18 @@ +using SeedInferredAuthImplicitApiKey; + +namespace Usage; + +public class Example3 +{ + public async Task Do() { + var client = new SeedInferredAuthImplicitApiKeyClient( + apiKey: "X-Api-Key", + clientOptions: new ClientOptions { + BaseUrl = "https://api.fern.com" + } + ); + + await client.Simple.GetSomethingAsync(); + } + +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedApi.DynamicSnippets/SeedApi.DynamicSnippets.csproj b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedApi.DynamicSnippets/SeedApi.DynamicSnippets.csproj new file mode 100644 index 000000000000..3417db2e58e2 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedApi.DynamicSnippets/SeedApi.DynamicSnippets.csproj @@ -0,0 +1,13 @@ + + + + net8.0 + 12 + enable + enable + + + + + + \ No newline at end of file diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/Json/AdditionalPropertiesTests.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/Json/AdditionalPropertiesTests.cs new file mode 100644 index 000000000000..7770db8c3c63 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/Json/AdditionalPropertiesTests.cs @@ -0,0 +1,365 @@ +using global::System.Text.Json; +using global::System.Text.Json.Serialization; +using NUnit.Framework; +using SeedInferredAuthImplicitApiKey.Core; + +namespace SeedInferredAuthImplicitApiKey.Test.Core.Json; + +[TestFixture] +public class AdditionalPropertiesTests +{ + [Test] + public void Record_OnDeserialized_ShouldPopulateAdditionalProperties() + { + // Arrange + const string json = """ + { + "id": "1", + "category": "fiction", + "title": "The Hobbit" + } + """; + + // Act + var record = JsonUtils.Deserialize(json); + + // Assert + Assert.That(record, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(record.Id, Is.EqualTo("1")); + Assert.That(record.AdditionalProperties["category"].GetString(), Is.EqualTo("fiction")); + Assert.That(record.AdditionalProperties["title"].GetString(), Is.EqualTo("The Hobbit")); + }); + } + + [Test] + public void RecordWithWriteableAdditionalProperties_OnSerialization_ShouldIncludeAdditionalProperties() + { + // Arrange + var record = new WriteableRecord + { + Id = "1", + AdditionalProperties = { ["category"] = "fiction", ["title"] = "The Hobbit" }, + }; + + // Act + var json = JsonUtils.Serialize(record); + var deserializedRecord = JsonUtils.Deserialize(json); + + // Assert + Assert.That(deserializedRecord, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(deserializedRecord.Id, Is.EqualTo("1")); + Assert.That( + deserializedRecord.AdditionalProperties["category"], + Is.InstanceOf() + ); + Assert.That( + ((JsonElement)deserializedRecord.AdditionalProperties["category"]!).GetString(), + Is.EqualTo("fiction") + ); + Assert.That( + deserializedRecord.AdditionalProperties["title"], + Is.InstanceOf() + ); + Assert.That( + ((JsonElement)deserializedRecord.AdditionalProperties["title"]!).GetString(), + Is.EqualTo("The Hobbit") + ); + }); + } + + [Test] + public void ReadOnlyAdditionalProperties_ShouldRetrieveValuesCorrectly() + { + // Arrange + var extensionData = new Dictionary + { + ["key1"] = JsonUtils.SerializeToElement("value1"), + ["key2"] = JsonUtils.SerializeToElement(123), + }; + var readOnlyProps = new ReadOnlyAdditionalProperties(); + readOnlyProps.CopyFromExtensionData(extensionData); + + // Act & Assert + Assert.That(readOnlyProps["key1"].GetString(), Is.EqualTo("value1")); + Assert.That(readOnlyProps["key2"].GetInt32(), Is.EqualTo(123)); + } + + [Test] + public void AdditionalProperties_ShouldBehaveAsDictionary() + { + // Arrange + var additionalProps = new AdditionalProperties { ["key1"] = "value1", ["key2"] = 123 }; + + // Act + additionalProps["key3"] = true; + + // Assert + Assert.Multiple(() => + { + Assert.That(additionalProps["key1"], Is.EqualTo("value1")); + Assert.That(additionalProps["key2"], Is.EqualTo(123)); + Assert.That((bool)additionalProps["key3"]!, Is.True); + Assert.That(additionalProps.Count, Is.EqualTo(3)); + }); + } + + [Test] + public void AdditionalProperties_ToJsonObject_ShouldSerializeCorrectly() + { + // Arrange + var additionalProps = new AdditionalProperties { ["key1"] = "value1", ["key2"] = 123 }; + + // Act + var jsonObject = additionalProps.ToJsonObject(); + + Assert.Multiple(() => + { + // Assert + Assert.That(jsonObject["key1"]!.GetValue(), Is.EqualTo("value1")); + Assert.That(jsonObject["key2"]!.GetValue(), Is.EqualTo(123)); + }); + } + + [Test] + public void AdditionalProperties_MixReadAndWrite_ShouldOverwriteDeserializedProperty() + { + // Arrange + const string json = """ + { + "id": "1", + "category": "fiction", + "title": "The Hobbit" + } + """; + var record = JsonUtils.Deserialize(json); + + // Act + record.AdditionalProperties["category"] = "non-fiction"; + + // Assert + Assert.Multiple(() => + { + Assert.That(record, Is.Not.Null); + Assert.That(record.Id, Is.EqualTo("1")); + Assert.That(record.AdditionalProperties["category"], Is.EqualTo("non-fiction")); + Assert.That(record.AdditionalProperties["title"], Is.InstanceOf()); + Assert.That( + ((JsonElement)record.AdditionalProperties["title"]!).GetString(), + Is.EqualTo("The Hobbit") + ); + }); + } + + [Test] + public void RecordWithReadonlyAdditionalPropertiesInts_OnDeserialized_ShouldPopulateAdditionalProperties() + { + // Arrange + const string json = """ + { + "extra1": 42, + "extra2": 99 + } + """; + + // Act + var record = JsonUtils.Deserialize(json); + + // Assert + Assert.That(record, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(record.AdditionalProperties["extra1"], Is.EqualTo(42)); + Assert.That(record.AdditionalProperties["extra2"], Is.EqualTo(99)); + }); + } + + [Test] + public void RecordWithAdditionalPropertiesInts_OnSerialization_ShouldIncludeAdditionalProperties() + { + // Arrange + var record = new WriteableRecordWithInts + { + AdditionalProperties = { ["extra1"] = 42, ["extra2"] = 99 }, + }; + + // Act + var json = JsonUtils.Serialize(record); + var deserializedRecord = JsonUtils.Deserialize(json); + + // Assert + Assert.That(deserializedRecord, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(deserializedRecord.AdditionalProperties["extra1"], Is.EqualTo(42)); + Assert.That(deserializedRecord.AdditionalProperties["extra2"], Is.EqualTo(99)); + }); + } + + [Test] + public void RecordWithReadonlyAdditionalPropertiesDictionaries_OnDeserialized_ShouldPopulateAdditionalProperties() + { + // Arrange + const string json = """ + { + "extra1": { "key1": true, "key2": false }, + "extra2": { "key3": true } + } + """; + + // Act + var record = JsonUtils.Deserialize(json); + + // Assert + Assert.That(record, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(record.AdditionalProperties["extra1"]["key1"], Is.True); + Assert.That(record.AdditionalProperties["extra1"]["key2"], Is.False); + Assert.That(record.AdditionalProperties["extra2"]["key3"], Is.True); + }); + } + + [Test] + public void RecordWithAdditionalPropertiesDictionaries_OnSerialization_ShouldIncludeAdditionalProperties() + { + // Arrange + var record = new WriteableRecordWithDictionaries + { + AdditionalProperties = + { + ["extra1"] = new Dictionary { { "key1", true }, { "key2", false } }, + ["extra2"] = new Dictionary { { "key3", true } }, + }, + }; + + // Act + var json = JsonUtils.Serialize(record); + var deserializedRecord = JsonUtils.Deserialize(json); + + // Assert + Assert.That(deserializedRecord, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(deserializedRecord.AdditionalProperties["extra1"]["key1"], Is.True); + Assert.That(deserializedRecord.AdditionalProperties["extra1"]["key2"], Is.False); + Assert.That(deserializedRecord.AdditionalProperties["extra2"]["key3"], Is.True); + }); + } + + private record Record : IJsonOnDeserialized + { + [JsonPropertyName("id")] + public required string Id { get; set; } + + [JsonExtensionData] + private readonly IDictionary _extensionData = + new Dictionary(); + + [JsonIgnore] + public ReadOnlyAdditionalProperties AdditionalProperties { get; } = new(); + + void IJsonOnDeserialized.OnDeserialized() + { + AdditionalProperties.CopyFromExtensionData(_extensionData); + } + } + + private record WriteableRecord : IJsonOnDeserialized, IJsonOnSerializing + { + [JsonPropertyName("id")] + public required string Id { get; set; } + + [JsonExtensionData] + private readonly IDictionary _extensionData = + new Dictionary(); + + [JsonIgnore] + public AdditionalProperties AdditionalProperties { get; set; } = new(); + + void IJsonOnDeserialized.OnDeserialized() + { + AdditionalProperties.CopyFromExtensionData(_extensionData); + } + + void IJsonOnSerializing.OnSerializing() + { + AdditionalProperties.CopyToExtensionData(_extensionData); + } + } + + private record RecordWithInts : IJsonOnDeserialized + { + [JsonExtensionData] + private readonly IDictionary _extensionData = + new Dictionary(); + + [JsonIgnore] + public ReadOnlyAdditionalProperties AdditionalProperties { get; } = new(); + + void IJsonOnDeserialized.OnDeserialized() + { + AdditionalProperties.CopyFromExtensionData(_extensionData); + } + } + + private record WriteableRecordWithInts : IJsonOnDeserialized, IJsonOnSerializing + { + [JsonExtensionData] + private readonly IDictionary _extensionData = + new Dictionary(); + + [JsonIgnore] + public AdditionalProperties AdditionalProperties { get; } = new(); + + void IJsonOnDeserialized.OnDeserialized() + { + AdditionalProperties.CopyFromExtensionData(_extensionData); + } + + void IJsonOnSerializing.OnSerializing() + { + AdditionalProperties.CopyToExtensionData(_extensionData); + } + } + + private record RecordWithDictionaries : IJsonOnDeserialized + { + [JsonExtensionData] + private readonly IDictionary _extensionData = + new Dictionary(); + + [JsonIgnore] + public ReadOnlyAdditionalProperties< + Dictionary + > AdditionalProperties { get; } = new(); + + void IJsonOnDeserialized.OnDeserialized() + { + AdditionalProperties.CopyFromExtensionData(_extensionData); + } + } + + private record WriteableRecordWithDictionaries : IJsonOnDeserialized, IJsonOnSerializing + { + [JsonExtensionData] + private readonly IDictionary _extensionData = + new Dictionary(); + + [JsonIgnore] + public AdditionalProperties> AdditionalProperties { get; } = new(); + + void IJsonOnDeserialized.OnDeserialized() + { + AdditionalProperties.CopyFromExtensionData(_extensionData); + } + + void IJsonOnSerializing.OnSerializing() + { + AdditionalProperties.CopyToExtensionData(_extensionData); + } + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/Json/DateOnlyJsonTests.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/Json/DateOnlyJsonTests.cs new file mode 100644 index 000000000000..2e0120b089d7 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/Json/DateOnlyJsonTests.cs @@ -0,0 +1,76 @@ +using NUnit.Framework; +using SeedInferredAuthImplicitApiKey.Core; + +namespace SeedInferredAuthImplicitApiKey.Test.Core.Json; + +[TestFixture] +public class DateOnlyJsonTests +{ + [Test] + public void SerializeDateOnly_ShouldMatchExpectedFormat() + { + (DateOnly dateOnly, string expected)[] testCases = + [ + (new DateOnly(2023, 10, 5), "\"2023-10-05\""), + (new DateOnly(2023, 1, 1), "\"2023-01-01\""), + (new DateOnly(2023, 12, 31), "\"2023-12-31\""), + (new DateOnly(2023, 6, 15), "\"2023-06-15\""), + (new DateOnly(2023, 3, 10), "\"2023-03-10\""), + ]; + foreach (var (dateOnly, expected) in testCases) + { + var json = JsonUtils.Serialize(dateOnly); + Assert.That(json, Is.EqualTo(expected)); + } + } + + [Test] + public void DeserializeDateOnly_ShouldMatchExpectedDateOnly() + { + (DateOnly expected, string json)[] testCases = + [ + (new DateOnly(2023, 10, 5), "\"2023-10-05\""), + (new DateOnly(2023, 1, 1), "\"2023-01-01\""), + (new DateOnly(2023, 12, 31), "\"2023-12-31\""), + (new DateOnly(2023, 6, 15), "\"2023-06-15\""), + (new DateOnly(2023, 3, 10), "\"2023-03-10\""), + ]; + + foreach (var (expected, json) in testCases) + { + var dateOnly = JsonUtils.Deserialize(json); + Assert.That(dateOnly, Is.EqualTo(expected)); + } + } + + [Test] + public void SerializeNullableDateOnly_ShouldMatchExpectedFormat() + { + (DateOnly? dateOnly, string expected)[] testCases = + [ + (new DateOnly(2023, 10, 5), "\"2023-10-05\""), + (null, "null"), + ]; + foreach (var (dateOnly, expected) in testCases) + { + var json = JsonUtils.Serialize(dateOnly); + Assert.That(json, Is.EqualTo(expected)); + } + } + + [Test] + public void DeserializeNullableDateOnly_ShouldMatchExpectedDateOnly() + { + (DateOnly? expected, string json)[] testCases = + [ + (new DateOnly(2023, 10, 5), "\"2023-10-05\""), + (null, "null"), + ]; + + foreach (var (expected, json) in testCases) + { + var dateOnly = JsonUtils.Deserialize(json); + Assert.That(dateOnly, Is.EqualTo(expected)); + } + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/Json/DateTimeJsonTests.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/Json/DateTimeJsonTests.cs new file mode 100644 index 000000000000..3c8de3eb25a7 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/Json/DateTimeJsonTests.cs @@ -0,0 +1,110 @@ +using NUnit.Framework; +using SeedInferredAuthImplicitApiKey.Core; + +namespace SeedInferredAuthImplicitApiKey.Test.Core.Json; + +[TestFixture] +public class DateTimeJsonTests +{ + [Test] + public void SerializeDateTime_ShouldMatchExpectedFormat() + { + (DateTime dateTime, string expected)[] testCases = + [ + ( + new DateTime(2023, 10, 5, 14, 30, 0, DateTimeKind.Utc), + "\"2023-10-05T14:30:00.000Z\"" + ), + (new DateTime(2023, 1, 1, 0, 0, 0, DateTimeKind.Utc), "\"2023-01-01T00:00:00.000Z\""), + ( + new DateTime(2023, 12, 31, 23, 59, 59, DateTimeKind.Utc), + "\"2023-12-31T23:59:59.000Z\"" + ), + (new DateTime(2023, 6, 15, 12, 0, 0, DateTimeKind.Utc), "\"2023-06-15T12:00:00.000Z\""), + ( + new DateTime(2023, 3, 10, 8, 45, 30, DateTimeKind.Utc), + "\"2023-03-10T08:45:30.000Z\"" + ), + ( + new DateTime(2023, 3, 10, 8, 45, 30, 123, DateTimeKind.Utc), + "\"2023-03-10T08:45:30.123Z\"" + ), + ]; + foreach (var (dateTime, expected) in testCases) + { + var json = JsonUtils.Serialize(dateTime); + Assert.That(json, Is.EqualTo(expected)); + } + } + + [Test] + public void DeserializeDateTime_ShouldMatchExpectedDateTime() + { + (DateTime expected, string json)[] testCases = + [ + ( + new DateTime(2023, 10, 5, 14, 30, 0, DateTimeKind.Utc), + "\"2023-10-05T14:30:00.000Z\"" + ), + (new DateTime(2023, 1, 1, 0, 0, 0, DateTimeKind.Utc), "\"2023-01-01T00:00:00.000Z\""), + ( + new DateTime(2023, 12, 31, 23, 59, 59, DateTimeKind.Utc), + "\"2023-12-31T23:59:59.000Z\"" + ), + (new DateTime(2023, 6, 15, 12, 0, 0, DateTimeKind.Utc), "\"2023-06-15T12:00:00.000Z\""), + ( + new DateTime(2023, 3, 10, 8, 45, 30, DateTimeKind.Utc), + "\"2023-03-10T08:45:30.000Z\"" + ), + (new DateTime(2023, 3, 10, 8, 45, 30, DateTimeKind.Utc), "\"2023-03-10T08:45:30Z\""), + ( + new DateTime(2023, 3, 10, 8, 45, 30, 123, DateTimeKind.Utc), + "\"2023-03-10T08:45:30.123Z\"" + ), + ]; + + foreach (var (expected, json) in testCases) + { + var dateTime = JsonUtils.Deserialize(json); + Assert.That(dateTime, Is.EqualTo(expected)); + } + } + + [Test] + public void SerializeNullableDateTime_ShouldMatchExpectedFormat() + { + (DateTime? expected, string json)[] testCases = + [ + ( + new DateTime(2023, 10, 5, 14, 30, 0, DateTimeKind.Utc), + "\"2023-10-05T14:30:00.000Z\"" + ), + (null, "null"), + ]; + + foreach (var (expected, json) in testCases) + { + var dateTime = JsonUtils.Deserialize(json); + Assert.That(dateTime, Is.EqualTo(expected)); + } + } + + [Test] + public void DeserializeNullableDateTime_ShouldMatchExpectedDateTime() + { + (DateTime? expected, string json)[] testCases = + [ + ( + new DateTime(2023, 10, 5, 14, 30, 0, DateTimeKind.Utc), + "\"2023-10-05T14:30:00.000Z\"" + ), + (null, "null"), + ]; + + foreach (var (expected, json) in testCases) + { + var dateTime = JsonUtils.Deserialize(json); + Assert.That(dateTime, Is.EqualTo(expected)); + } + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/Json/JsonAccessAttributeTests.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/Json/JsonAccessAttributeTests.cs new file mode 100644 index 000000000000..0e8ca22b9358 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/Json/JsonAccessAttributeTests.cs @@ -0,0 +1,160 @@ +using global::System.Text.Json.Serialization; +using NUnit.Framework; +using SeedInferredAuthImplicitApiKey.Core; + +namespace SeedInferredAuthImplicitApiKey.Test.Core.Json; + +[TestFixture] +public class JsonAccessAttributeTests +{ + private class MyClass + { + [JsonPropertyName("read_only_prop")] + [JsonAccess(JsonAccessType.ReadOnly)] + public string? ReadOnlyProp { get; set; } + + [JsonPropertyName("write_only_prop")] + [JsonAccess(JsonAccessType.WriteOnly)] + public string? WriteOnlyProp { get; set; } + + [JsonPropertyName("normal_prop")] + public string? NormalProp { get; set; } + + [JsonPropertyName("read_only_nullable_list")] + [JsonAccess(JsonAccessType.ReadOnly)] + public IEnumerable? ReadOnlyNullableList { get; set; } + + [JsonPropertyName("read_only_list")] + [JsonAccess(JsonAccessType.ReadOnly)] + public IEnumerable ReadOnlyList { get; set; } = []; + + [JsonPropertyName("write_only_nullable_list")] + [JsonAccess(JsonAccessType.WriteOnly)] + public IEnumerable? WriteOnlyNullableList { get; set; } + + [JsonPropertyName("write_only_list")] + [JsonAccess(JsonAccessType.WriteOnly)] + public IEnumerable WriteOnlyList { get; set; } = []; + + [JsonPropertyName("normal_list")] + public IEnumerable NormalList { get; set; } = []; + + [JsonPropertyName("normal_nullable_list")] + public IEnumerable? NullableNormalList { get; set; } + } + + [Test] + public void JsonAccessAttribute_ShouldWorkAsExpected() + { + const string json = """ + { + "read_only_prop": "read", + "write_only_prop": "write", + "normal_prop": "normal_prop", + "read_only_nullable_list": ["item1", "item2"], + "read_only_list": ["item3", "item4"], + "write_only_nullable_list": ["item5", "item6"], + "write_only_list": ["item7", "item8"], + "normal_list": ["normal1", "normal2"], + "normal_nullable_list": ["normal1", "normal2"] + } + """; + var obj = JsonUtils.Deserialize(json); + + Assert.Multiple(() => + { + // String properties + Assert.That(obj.ReadOnlyProp, Is.EqualTo("read")); + Assert.That(obj.WriteOnlyProp, Is.Null); + Assert.That(obj.NormalProp, Is.EqualTo("normal_prop")); + + // List properties - read only + var nullableReadOnlyList = obj.ReadOnlyNullableList?.ToArray(); + Assert.That(nullableReadOnlyList, Is.Not.Null); + Assert.That(nullableReadOnlyList, Has.Length.EqualTo(2)); + Assert.That(nullableReadOnlyList![0], Is.EqualTo("item1")); + Assert.That(nullableReadOnlyList![1], Is.EqualTo("item2")); + + var readOnlyList = obj.ReadOnlyList.ToArray(); + Assert.That(readOnlyList, Is.Not.Null); + Assert.That(readOnlyList, Has.Length.EqualTo(2)); + Assert.That(readOnlyList[0], Is.EqualTo("item3")); + Assert.That(readOnlyList[1], Is.EqualTo("item4")); + + // List properties - write only + Assert.That(obj.WriteOnlyNullableList, Is.Null); + Assert.That(obj.WriteOnlyList, Is.Not.Null); + Assert.That(obj.WriteOnlyList, Is.Empty); + + // Normal list property + var normalList = obj.NormalList.ToArray(); + Assert.That(normalList, Is.Not.Null); + Assert.That(normalList, Has.Length.EqualTo(2)); + Assert.That(normalList[0], Is.EqualTo("normal1")); + Assert.That(normalList[1], Is.EqualTo("normal2")); + }); + + // Set up values for serialization + obj.WriteOnlyProp = "write"; + obj.NormalProp = "new_value"; + obj.WriteOnlyNullableList = new List { "write1", "write2" }; + obj.WriteOnlyList = new List { "write3", "write4" }; + obj.NormalList = new List { "new_normal" }; + obj.NullableNormalList = new List { "new_normal" }; + + var serializedJson = JsonUtils.Serialize(obj); + const string expectedJson = """ + { + "write_only_prop": "write", + "normal_prop": "new_value", + "write_only_nullable_list": [ + "write1", + "write2" + ], + "write_only_list": [ + "write3", + "write4" + ], + "normal_list": [ + "new_normal" + ], + "normal_nullable_list": [ + "new_normal" + ] + } + """; + Assert.That(serializedJson, Is.EqualTo(expectedJson).IgnoreWhiteSpace); + } + + [Test] + public void JsonAccessAttribute_WithNullListsInJson_ShouldWorkAsExpected() + { + const string json = """ + { + "read_only_prop": "read", + "normal_prop": "normal_prop", + "read_only_nullable_list": null, + "read_only_list": [] + } + """; + var obj = JsonUtils.Deserialize(json); + + Assert.Multiple(() => + { + // Read-only nullable list should be null when JSON contains null + var nullableReadOnlyList = obj.ReadOnlyNullableList?.ToArray(); + Assert.That(nullableReadOnlyList, Is.Null); + + // Read-only non-nullable list should never be null, but empty when JSON contains null + var readOnlyList = obj.ReadOnlyList.ToArray(); // This should be initialized to an empty list by default + Assert.That(readOnlyList, Is.Not.Null); + Assert.That(readOnlyList, Is.Empty); + }); + + // Serialize and verify read-only lists are not included + var serializedJson = JsonUtils.Serialize(obj); + Assert.That(serializedJson, Does.Not.Contain("read_only_prop")); + Assert.That(serializedJson, Does.Not.Contain("read_only_nullable_list")); + Assert.That(serializedJson, Does.Not.Contain("read_only_list")); + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/Json/OneOfSerializerTests.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/Json/OneOfSerializerTests.cs new file mode 100644 index 000000000000..f8dcd3076895 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/Json/OneOfSerializerTests.cs @@ -0,0 +1,314 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using NUnit.Framework; +using OneOf; +using SeedInferredAuthImplicitApiKey.Core; + +namespace SeedInferredAuthImplicitApiKey.Test.Core.Json; + +[TestFixture] +[Parallelizable(ParallelScope.All)] +public class OneOfSerializerTests +{ + private class Foo + { + [JsonPropertyName("string_prop")] + public required string StringProp { get; set; } + } + + private class Bar + { + [JsonPropertyName("int_prop")] + public required int IntProp { get; set; } + } + + private static readonly OneOf OneOf1 = OneOf< + string, + int, + object, + Foo, + Bar + >.FromT2(new { }); + private const string OneOf1String = "{}"; + + private static readonly OneOf OneOf2 = OneOf< + string, + int, + object, + Foo, + Bar + >.FromT0("test"); + private const string OneOf2String = "\"test\""; + + private static readonly OneOf OneOf3 = OneOf< + string, + int, + object, + Foo, + Bar + >.FromT1(123); + private const string OneOf3String = "123"; + + private static readonly OneOf OneOf4 = OneOf< + string, + int, + object, + Foo, + Bar + >.FromT3(new Foo { StringProp = "test" }); + private const string OneOf4String = "{\"string_prop\": \"test\"}"; + + private static readonly OneOf OneOf5 = OneOf< + string, + int, + object, + Foo, + Bar + >.FromT4(new Bar { IntProp = 5 }); + private const string OneOf5String = "{\"int_prop\": 5}"; + + [Test] + public void Serialize_OneOfs_Should_Return_Expected_String() + { + (OneOf, string)[] testData = + [ + (OneOf1, OneOf1String), + (OneOf2, OneOf2String), + (OneOf3, OneOf3String), + (OneOf4, OneOf4String), + (OneOf5, OneOf5String), + ]; + Assert.Multiple(() => + { + foreach (var (oneOf, expected) in testData) + { + var result = JsonUtils.Serialize(oneOf); + Assert.That(result, Is.EqualTo(expected).IgnoreWhiteSpace); + } + }); + } + + [Test] + public void OneOfs_Should_Deserialize_From_String() + { + (OneOf, string)[] testData = + [ + (OneOf1, OneOf1String), + (OneOf2, OneOf2String), + (OneOf3, OneOf3String), + (OneOf4, OneOf4String), + (OneOf5, OneOf5String), + ]; + Assert.Multiple(() => + { + foreach (var (oneOf, json) in testData) + { + var result = JsonUtils.Deserialize>(json); + Assert.That(result.Index, Is.EqualTo(oneOf.Index)); + Assert.That(json, Is.EqualTo(JsonUtils.Serialize(result.Value)).IgnoreWhiteSpace); + } + }); + } + + private static readonly OneOf? NullableOneOf1 = null; + private const string NullableOneOf1String = "null"; + + private static readonly OneOf? NullableOneOf2 = OneOf< + string, + int, + object, + Foo, + Bar + >.FromT4(new Bar { IntProp = 5 }); + private const string NullableOneOf2String = "{\"int_prop\": 5}"; + + [Test] + public void Serialize_NullableOneOfs_Should_Return_Expected_String() + { + (OneOf?, string)[] testData = + [ + (NullableOneOf1, NullableOneOf1String), + (NullableOneOf2, NullableOneOf2String), + ]; + Assert.Multiple(() => + { + foreach (var (oneOf, expected) in testData) + { + var result = JsonUtils.Serialize(oneOf); + Assert.That(result, Is.EqualTo(expected).IgnoreWhiteSpace); + } + }); + } + + [Test] + public void NullableOneOfs_Should_Deserialize_From_String() + { + (OneOf?, string)[] testData = + [ + (NullableOneOf1, NullableOneOf1String), + (NullableOneOf2, NullableOneOf2String), + ]; + Assert.Multiple(() => + { + foreach (var (oneOf, json) in testData) + { + var result = JsonUtils.Deserialize?>(json); + Assert.That(result?.Index, Is.EqualTo(oneOf?.Index)); + Assert.That(json, Is.EqualTo(JsonUtils.Serialize(result?.Value)).IgnoreWhiteSpace); + } + }); + } + + private static readonly OneOf OneOfWithNullable1 = OneOf< + string, + int, + Foo? + >.FromT2(null); + private const string OneOfWithNullable1String = "null"; + + private static readonly OneOf OneOfWithNullable2 = OneOf< + string, + int, + Foo? + >.FromT2(new Foo { StringProp = "test" }); + private const string OneOfWithNullable2String = "{\"string_prop\": \"test\"}"; + + private static readonly OneOf OneOfWithNullable3 = OneOf< + string, + int, + Foo? + >.FromT0("test"); + private const string OneOfWithNullable3String = "\"test\""; + + [Test] + public void Serialize_OneOfWithNullables_Should_Return_Expected_String() + { + (OneOf, string)[] testData = + [ + (OneOfWithNullable1, OneOfWithNullable1String), + (OneOfWithNullable2, OneOfWithNullable2String), + (OneOfWithNullable3, OneOfWithNullable3String), + ]; + Assert.Multiple(() => + { + foreach (var (oneOf, expected) in testData) + { + var result = JsonUtils.Serialize(oneOf); + Assert.That(result, Is.EqualTo(expected).IgnoreWhiteSpace); + } + }); + } + + [Test] + public void OneOfWithNullables_Should_Deserialize_From_String() + { + (OneOf, string)[] testData = + [ + // (OneOfWithNullable1, OneOfWithNullable1String), // not possible with .NET's JSON serializer + (OneOfWithNullable2, OneOfWithNullable2String), + (OneOfWithNullable3, OneOfWithNullable3String), + ]; + Assert.Multiple(() => + { + foreach (var (oneOf, json) in testData) + { + var result = JsonUtils.Deserialize>(json); + Assert.That(result.Index, Is.EqualTo(oneOf.Index)); + Assert.That(json, Is.EqualTo(JsonUtils.Serialize(result.Value)).IgnoreWhiteSpace); + } + }); + } + + [Test] + public void Serialize_OneOfWithObjectLast_Should_Return_Expected_String() + { + var oneOfWithObjectLast = OneOf.FromT4( + new { random = "data" } + ); + const string oneOfWithObjectLastString = "{\"random\": \"data\"}"; + + var result = JsonUtils.Serialize(oneOfWithObjectLast); + Assert.That(result, Is.EqualTo(oneOfWithObjectLastString).IgnoreWhiteSpace); + } + + [Test] + public void OneOfWithObjectLast_Should_Deserialize_From_String() + { + const string oneOfWithObjectLastString = "{\"random\": \"data\"}"; + var result = JsonUtils.Deserialize>( + oneOfWithObjectLastString + ); + Assert.Multiple(() => + { + Assert.That(result.Index, Is.EqualTo(4)); + Assert.That(result.Value, Is.InstanceOf()); + Assert.That( + JsonUtils.Serialize(result.Value), + Is.EqualTo(oneOfWithObjectLastString).IgnoreWhiteSpace + ); + }); + } + + [Test] + public void Serialize_OneOfWithObjectNotLast_Should_Return_Expected_String() + { + var oneOfWithObjectNotLast = OneOf.FromT1( + new { random = "data" } + ); + const string oneOfWithObjectNotLastString = "{\"random\": \"data\"}"; + + var result = JsonUtils.Serialize(oneOfWithObjectNotLast); + Assert.That(result, Is.EqualTo(oneOfWithObjectNotLastString).IgnoreWhiteSpace); + } + + [Test] + public void OneOfWithObjectNotLast_Should_Deserialize_From_String() + { + const string oneOfWithObjectNotLastString = "{\"random\": \"data\"}"; + var result = JsonUtils.Deserialize>( + oneOfWithObjectNotLastString + ); + Assert.Multiple(() => + { + Assert.That(result.Index, Is.EqualTo(1)); + Assert.That(result.Value, Is.InstanceOf()); + Assert.That( + JsonUtils.Serialize(result.Value), + Is.EqualTo(oneOfWithObjectNotLastString).IgnoreWhiteSpace + ); + }); + } + + [Test] + public void Serialize_OneOfSingleType_Should_Return_Expected_String() + { + var oneOfSingle = OneOf.FromT0("single"); + const string oneOfSingleString = "\"single\""; + + var result = JsonUtils.Serialize(oneOfSingle); + Assert.That(result, Is.EqualTo(oneOfSingleString)); + } + + [Test] + public void OneOfSingleType_Should_Deserialize_From_String() + { + const string oneOfSingleString = "\"single\""; + var result = JsonUtils.Deserialize>(oneOfSingleString); + Assert.Multiple(() => + { + Assert.That(result.Index, Is.EqualTo(0)); + Assert.That(result.Value, Is.EqualTo("single")); + }); + } + + [Test] + public void Deserialize_InvalidData_Should_Throw_Exception() + { + const string invalidJson = "{\"invalid\": \"data\"}"; + + Assert.Throws(() => + { + JsonUtils.Deserialize>(invalidJson); + }); + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/Json/StringEnumSerializerTests.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/Json/StringEnumSerializerTests.cs new file mode 100644 index 000000000000..52f0b414861d --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/Json/StringEnumSerializerTests.cs @@ -0,0 +1,138 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using NUnit.Framework; +using SeedInferredAuthImplicitApiKey.Core; + +namespace SeedInferredAuthImplicitApiKey.Test.Core.Json; + +[TestFixture] +[Parallelizable(ParallelScope.All)] +public class StringEnumSerializerTests +{ + private static readonly JsonSerializerOptions JsonOptions = new() { WriteIndented = true }; + + private static readonly DummyEnum KnownEnumValue2 = DummyEnum.KnownValue2; + private static readonly DummyEnum UnknownEnumValue = DummyEnum.FromCustom("unknown_value"); + + private static readonly string JsonWithKnownEnum2 = $$""" + { + "enum_property": "{{KnownEnumValue2}}" + } + """; + + private static readonly string JsonWithUnknownEnum = $$""" + { + "enum_property": "{{UnknownEnumValue}}" + } + """; + + [Test] + public void ShouldParseKnownEnumValue2() + { + var obj = JsonSerializer.Deserialize(JsonWithKnownEnum2, JsonOptions); + Assert.That(obj, Is.Not.Null); + Assert.That(obj.EnumProperty, Is.EqualTo(KnownEnumValue2)); + } + + [Test] + public void ShouldParseUnknownEnum() + { + var obj = JsonSerializer.Deserialize(JsonWithUnknownEnum, JsonOptions); + Assert.That(obj, Is.Not.Null); + Assert.That(obj.EnumProperty, Is.EqualTo(UnknownEnumValue)); + } + + [Test] + public void ShouldSerializeKnownEnumValue2() + { + var json = JsonSerializer.SerializeToElement( + new DummyObject { EnumProperty = KnownEnumValue2 }, + JsonOptions + ); + TestContext.Out.WriteLine("Serialized JSON: \n" + json); + var enumString = json.GetProperty("enum_property").GetString(); + Assert.That(enumString, Is.Not.Null); + Assert.That(enumString, Is.EqualTo(KnownEnumValue2)); + } + + [Test] + public void ShouldSerializeUnknownEnum() + { + var json = JsonSerializer.SerializeToElement( + new DummyObject { EnumProperty = UnknownEnumValue }, + JsonOptions + ); + TestContext.Out.WriteLine("Serialized JSON: \n" + json); + var enumString = json.GetProperty("enum_property").GetString(); + Assert.That(enumString, Is.Not.Null); + Assert.That(enumString, Is.EqualTo(UnknownEnumValue)); + } +} + +public class DummyObject +{ + [JsonPropertyName("enum_property")] + public DummyEnum EnumProperty { get; set; } +} + +[JsonConverter(typeof(StringEnumSerializer))] +public readonly record struct DummyEnum : IStringEnum +{ + public DummyEnum(string value) + { + Value = value; + } + + /// + /// The string value of the enum. + /// + public string Value { get; } + + public static readonly DummyEnum KnownValue1 = FromCustom(Values.KnownValue1); + + public static readonly DummyEnum KnownValue2 = FromCustom(Values.KnownValue2); + + /// + /// Constant strings for enum values + /// + public static class Values + { + public const string KnownValue1 = "known_value1"; + + public const string KnownValue2 = "known_value2"; + } + + /// + /// Create a string enum with the given value. + /// + public static DummyEnum FromCustom(string value) + { + return new DummyEnum(value); + } + + /// + /// Returns the string value of the enum. + /// + public override string ToString() + { + return Value; + } + + public bool Equals(string? other) + { + return Value.Equals(other); + } + + public override int GetHashCode() + { + return Value.GetHashCode(); + } + + public static explicit operator string(DummyEnum value) => value.Value; + + public static explicit operator DummyEnum(string value) => new(value); + + public static bool operator ==(DummyEnum value1, string value2) => value1.Value.Equals(value2); + + public static bool operator !=(DummyEnum value1, string value2) => !value1.Value.Equals(value2); +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/QueryStringConverterTests.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/QueryStringConverterTests.cs new file mode 100644 index 000000000000..c7fe7235299d --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/QueryStringConverterTests.cs @@ -0,0 +1,124 @@ +using NUnit.Framework; +using SeedInferredAuthImplicitApiKey.Core; + +namespace SeedInferredAuthImplicitApiKey.Test.Core; + +[TestFixture] +public class QueryStringConverterTests +{ + [Test] + public void ToQueryStringCollection_Form() + { + var obj = new + { + Name = "John", + Age = 30, + Address = new + { + Street = "123 Main St", + City = "Anytown", + Coordinates = new[] { 39.781721f, -89.650148f }, + }, + Tags = new[] { "Developer", "Blogger" }, + }; + var result = QueryStringConverter.ToForm(obj); + var expected = new List> + { + new("Name", "John"), + new("Age", "30"), + new("Address[Street]", "123 Main St"), + new("Address[City]", "Anytown"), + new("Address[Coordinates]", "39.78172,-89.65015"), + new("Tags", "Developer,Blogger"), + }; + Assert.That(result, Is.EqualTo(expected)); + } + + [Test] + public void ToQueryStringCollection_ExplodedForm() + { + var obj = new + { + Name = "John", + Age = 30, + Address = new + { + Street = "123 Main St", + City = "Anytown", + Coordinates = new[] { 39.781721f, -89.650148f }, + }, + Tags = new[] { "Developer", "Blogger" }, + }; + var result = QueryStringConverter.ToExplodedForm(obj); + var expected = new List> + { + new("Name", "John"), + new("Age", "30"), + new("Address[Street]", "123 Main St"), + new("Address[City]", "Anytown"), + new("Address[Coordinates]", "39.78172"), + new("Address[Coordinates]", "-89.65015"), + new("Tags", "Developer"), + new("Tags", "Blogger"), + }; + Assert.That(result, Is.EqualTo(expected)); + } + + [Test] + public void ToQueryStringCollection_DeepObject() + { + var obj = new + { + Name = "John", + Age = 30, + Address = new + { + Street = "123 Main St", + City = "Anytown", + Coordinates = new[] { 39.781721f, -89.650148f }, + }, + Tags = new[] { "Developer", "Blogger" }, + }; + var result = QueryStringConverter.ToDeepObject(obj); + var expected = new List> + { + new("Name", "John"), + new("Age", "30"), + new("Address[Street]", "123 Main St"), + new("Address[City]", "Anytown"), + new("Address[Coordinates][0]", "39.78172"), + new("Address[Coordinates][1]", "-89.65015"), + new("Tags[0]", "Developer"), + new("Tags[1]", "Blogger"), + }; + Assert.That(result, Is.EqualTo(expected)); + } + + [Test] + public void ToQueryStringCollection_OnString_ThrowsException() + { + var exception = Assert.Throws(() => + QueryStringConverter.ToForm("invalid") + ); + Assert.That( + exception.Message, + Is.EqualTo( + "Only objects can be converted to query string collections. Given type is String." + ) + ); + } + + [Test] + public void ToQueryStringCollection_OnArray_ThrowsException() + { + var exception = Assert.Throws(() => + QueryStringConverter.ToForm(Array.Empty()) + ); + Assert.That( + exception.Message, + Is.EqualTo( + "Only objects can be converted to query string collections. Given type is Array." + ) + ); + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/RawClientTests/AdditionalHeadersTests.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/RawClientTests/AdditionalHeadersTests.cs new file mode 100644 index 000000000000..f1e66e9675b0 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/RawClientTests/AdditionalHeadersTests.cs @@ -0,0 +1,138 @@ +using NUnit.Framework; +using SeedInferredAuthImplicitApiKey.Core; +using WireMock.Server; +using SystemTask = global::System.Threading.Tasks.Task; +using WireMockRequest = WireMock.RequestBuilders.Request; +using WireMockResponse = WireMock.ResponseBuilders.Response; + +// ReSharper disable NullableWarningSuppressionIsUsed + +namespace SeedInferredAuthImplicitApiKey.Test.Core.RawClientTests; + +[TestFixture] +[Parallelizable(ParallelScope.Self)] +public class AdditionalHeadersTests +{ + private WireMockServer _server; + private HttpClient _httpClient; + private RawClient _rawClient; + private string _baseUrl; + + [SetUp] + public void SetUp() + { + _server = WireMockServer.Start(); + _baseUrl = _server.Url ?? ""; + _httpClient = new HttpClient { BaseAddress = new Uri(_baseUrl) }; + _rawClient = new RawClient( + new ClientOptions + { + HttpClient = _httpClient, + Headers = new Headers( + new Dictionary + { + ["a"] = "client_headers", + ["b"] = "client_headers", + ["c"] = "client_headers", + ["d"] = "client_headers", + ["e"] = "client_headers", + ["f"] = "client_headers", + ["client_multiple"] = "client_headers", + } + ), + AdditionalHeaders = new List> + { + new("b", "client_additional_headers"), + new("c", "client_additional_headers"), + new("d", "client_additional_headers"), + new("e", null), + new("client_multiple", "client_additional_headers1"), + new("client_multiple", "client_additional_headers2"), + }, + } + ); + } + + [Test] + public async SystemTask SendRequestAsync_AdditionalHeaderParameters() + { + _server + .Given(WireMockRequest.Create().WithPath("/test").UsingGet()) + .RespondWith(WireMockResponse.Create().WithStatusCode(200).WithBody("Success")); + + var request = new JsonRequest + { + BaseUrl = _baseUrl, + Method = HttpMethod.Get, + Path = "/test", + Headers = new Headers( + new Dictionary + { + ["c"] = "request_headers", + ["d"] = "request_headers", + ["request_multiple"] = "request_headers", + } + ), + Options = new RequestOptions + { + AdditionalHeaders = new List> + { + new("d", "request_additional_headers"), + new("f", null), + new("request_multiple", "request_additional_headers1"), + new("request_multiple", "request_additional_headers2"), + }, + }, + }; + + var response = await _rawClient.SendRequestAsync(request); + Assert.That(response.StatusCode, Is.EqualTo(200)); + + var content = await response.Raw.Content.ReadAsStringAsync(); + Assert.Multiple(() => + { + Assert.That(content, Is.EqualTo("Success")); + Assert.That(_server.LogEntries.Count, Is.EqualTo(1)); + var headers = + _server.LogEntries[0].RequestMessage.Headers ?? throw new global::System.Exception( + "Headers are null" + ); + + Assert.That(headers, Contains.Key("client_multiple")); + Assert.That(headers!["client_multiple"][0], Does.Contain("client_additional_headers1")); + Assert.That(headers["client_multiple"][0], Does.Contain("client_additional_headers2")); + + Assert.That(headers, Contains.Key("request_multiple")); + Assert.That( + headers["request_multiple"][0], + Does.Contain("request_additional_headers1") + ); + Assert.That( + headers["request_multiple"][0], + Does.Contain("request_additional_headers2") + ); + + Assert.That(headers, Contains.Key("a")); + Assert.That(headers["a"][0], Does.Contain("client_headers")); + + Assert.That(headers, Contains.Key("b")); + Assert.That(headers["b"][0], Does.Contain("client_additional_headers")); + + Assert.That(headers, Contains.Key("c")); + Assert.That(headers["c"][0], Does.Contain("request_headers")); + + Assert.That(headers, Contains.Key("d")); + Assert.That(headers["d"][0], Does.Contain("request_additional_headers")); + + Assert.That(headers, Does.Not.ContainKey("e")); + Assert.That(headers, Does.Not.ContainKey("f")); + }); + } + + [TearDown] + public void TearDown() + { + _server.Dispose(); + _httpClient.Dispose(); + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/RawClientTests/AdditionalParametersTests.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/RawClientTests/AdditionalParametersTests.cs new file mode 100644 index 000000000000..f569f7e3f18a --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/RawClientTests/AdditionalParametersTests.cs @@ -0,0 +1,300 @@ +using NUnit.Framework; +using SeedInferredAuthImplicitApiKey.Core; +using WireMock.Matchers; +using WireMock.Server; +using SystemTask = global::System.Threading.Tasks.Task; +using WireMockRequest = WireMock.RequestBuilders.Request; +using WireMockResponse = WireMock.ResponseBuilders.Response; + +namespace SeedInferredAuthImplicitApiKey.Test.Core.RawClientTests; + +[TestFixture] +[Parallelizable(ParallelScope.Self)] +public class AdditionalParametersTests +{ + private WireMockServer _server; + private HttpClient _httpClient; + private RawClient _rawClient; + private string _baseUrl; + + [SetUp] + public void SetUp() + { + _server = WireMockServer.Start(); + _baseUrl = _server.Url ?? ""; + _httpClient = new HttpClient { BaseAddress = new Uri(_baseUrl) }; + _rawClient = new RawClient(new ClientOptions { HttpClient = _httpClient }); + } + + [Test] + public async SystemTask SendRequestAsync_AdditionalQueryParameters() + { + _server + .Given(WireMockRequest.Create().WithPath("/test").WithParam("foo", "bar").UsingGet()) + .RespondWith(WireMockResponse.Create().WithStatusCode(200).WithBody("Success")); + + var request = new JsonRequest() + { + BaseUrl = _baseUrl, + Method = HttpMethod.Get, + Path = "/test", + Options = new RequestOptions + { + AdditionalQueryParameters = new List> + { + new("foo", "bar"), + }, + }, + }; + + var response = await _rawClient.SendRequestAsync(request); + Assert.That(response.StatusCode, Is.EqualTo(200)); + + var content = await response.Raw.Content.ReadAsStringAsync(); + Assert.That(content, Is.EqualTo("Success")); + + Assert.That(_server.LogEntries.Count, Is.EqualTo(1)); + } + + [Test] + public async SystemTask SendRequestAsync_AdditionalQueryParameters_Override() + { + _server + .Given(WireMockRequest.Create().WithPath("/test").WithParam("foo", "null").UsingGet()) + .RespondWith(WireMockResponse.Create().WithStatusCode(200).WithBody("Success")); + + var request = new JsonRequest() + { + BaseUrl = _baseUrl, + Method = HttpMethod.Get, + Path = "/test", + Query = new Dictionary { { "foo", "bar" } }, + Options = new RequestOptions + { + AdditionalQueryParameters = new List> + { + new("foo", "null"), + }, + }, + }; + + var response = await _rawClient.SendRequestAsync(request); + Assert.That(response.StatusCode, Is.EqualTo(200)); + + var content = await response.Raw.Content.ReadAsStringAsync(); + Assert.That(content, Is.EqualTo("Success")); + + Assert.That(_server.LogEntries.Count, Is.EqualTo(1)); + } + + [Test] + public async SystemTask SendRequestAsync_AdditionalQueryParameters_Merge() + { + _server + .Given(WireMockRequest.Create().WithPath("/test").UsingGet()) + .RespondWith(WireMockResponse.Create().WithStatusCode(200).WithBody("Success")); + + var request = new JsonRequest() + { + BaseUrl = _baseUrl, + Method = HttpMethod.Get, + Path = "/test", + Query = new Dictionary { { "foo", "baz" } }, + Options = new RequestOptions + { + AdditionalQueryParameters = new List> + { + new("foo", "one"), + new("foo", "two"), + }, + }, + }; + + var response = await _rawClient.SendRequestAsync(request); + Assert.That(response.StatusCode, Is.EqualTo(200)); + + var content = await response.Raw.Content.ReadAsStringAsync(); + Assert.That(content, Is.EqualTo("Success")); + + Assert.That(_server.LogEntries.Count, Is.EqualTo(1)); + + var requestUrl = _server.LogEntries.First().RequestMessage.Url; + Assert.That(requestUrl, Does.Contain("foo=one")); + Assert.That(requestUrl, Does.Contain("foo=two")); + Assert.That(requestUrl, Does.Not.Contain("foo=baz")); + } + + [Test] + public async SystemTask SendRequestAsync_AdditionalBodyProperties() + { + string expectedBody = "{\n \"foo\": \"bar\",\n \"baz\": \"qux\"\n}"; + _server + .Given( + WireMockRequest + .Create() + .WithPath("/test") + .UsingPost() + .WithBody(new JsonMatcher(expectedBody)) + ) + .RespondWith(WireMockResponse.Create().WithStatusCode(200).WithBody("Success")); + + var request = new JsonRequest + { + BaseUrl = _baseUrl, + Method = HttpMethod.Post, + Path = "/test", + Body = new Dictionary { { "foo", "bar" } }, + Options = new RequestOptions + { + AdditionalBodyProperties = new Dictionary { { "baz", "qux" } }, + }, + }; + + var response = await _rawClient.SendRequestAsync(request); + Assert.That(response.StatusCode, Is.EqualTo(200)); + + var content = await response.Raw.Content.ReadAsStringAsync(); + Assert.That(content, Is.EqualTo("Success")); + + Assert.That(_server.LogEntries.Count, Is.EqualTo(1)); + } + + [Test] + public async SystemTask SendRequestAsync_AdditionalBodyProperties_Override() + { + string expectedBody = "{\n \"foo\": null\n}"; + _server + .Given( + WireMockRequest + .Create() + .WithPath("/test") + .UsingPost() + .WithBody(new JsonMatcher(expectedBody)) + ) + .RespondWith(WireMockResponse.Create().WithStatusCode(200).WithBody("Success")); + + var request = new JsonRequest + { + BaseUrl = _baseUrl, + Method = HttpMethod.Post, + Path = "/test", + Body = new Dictionary { { "foo", "bar" } }, + Options = new RequestOptions + { + AdditionalBodyProperties = new Dictionary { { "foo", null } }, + }, + }; + + var response = await _rawClient.SendRequestAsync(request); + Assert.That(response.StatusCode, Is.EqualTo(200)); + + var content = await response.Raw.Content.ReadAsStringAsync(); + Assert.That(content, Is.EqualTo("Success")); + + Assert.That(_server.LogEntries.Count, Is.EqualTo(1)); + } + + [Test] + public async SystemTask SendRequestAsync_AdditionalBodyProperties_DeepMerge() + { + const string expectedBody = """ + { + "foo": { + "inner1": "original", + "inner2": "overridden", + "inner3": { + "deepProp1": "deep-override", + "deepProp2": "original", + "deepProp3": null, + "deepProp4": "new-value" + } + }, + "bar": "new-value", + "baz": ["new","value"] + } + """; + + _server + .Given( + WireMockRequest + .Create() + .WithPath("/test-deep-merge") + .UsingPost() + .WithBody(new JsonMatcher(expectedBody)) + ) + .RespondWith(WireMockResponse.Create().WithStatusCode(200).WithBody("Success")); + + var request = new JsonRequest + { + BaseUrl = _baseUrl, + Method = HttpMethod.Post, + Path = "/test-deep-merge", + Body = new Dictionary + { + { + "foo", + new Dictionary + { + { "inner1", "original" }, + { "inner2", "original" }, + { + "inner3", + new Dictionary + { + { "deepProp1", "deep-original" }, + { "deepProp2", "original" }, + { "deepProp3", "" }, + } + }, + } + }, + { + "baz", + new List { "original" } + }, + }, + Options = new RequestOptions + { + AdditionalBodyProperties = new Dictionary + { + { + "foo", + new Dictionary + { + { "inner2", "overridden" }, + { + "inner3", + new Dictionary + { + { "deepProp1", "deep-override" }, + { "deepProp3", null }, + { "deepProp4", "new-value" }, + } + }, + } + }, + { "bar", "new-value" }, + { + "baz", + new List { "new", "value" } + }, + }, + }, + }; + + var response = await _rawClient.SendRequestAsync(request); + Assert.That(response.StatusCode, Is.EqualTo(200)); + + var content = await response.Raw.Content.ReadAsStringAsync(); + Assert.That(content, Is.EqualTo("Success")); + + Assert.That(_server.LogEntries.Count, Is.EqualTo(1)); + } + + [TearDown] + public void TearDown() + { + _server.Dispose(); + _httpClient.Dispose(); + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/RawClientTests/MultipartFormTests.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/RawClientTests/MultipartFormTests.cs new file mode 100644 index 000000000000..7c71a4a2c9a3 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/RawClientTests/MultipartFormTests.cs @@ -0,0 +1,1120 @@ +using global::System.Net.Http; +using global::System.Text; +using global::System.Text.Json.Serialization; +using NUnit.Framework; +using SeedInferredAuthImplicitApiKey.Core; +using SystemTask = global::System.Threading.Tasks.Task; + +namespace SeedInferredAuthImplicitApiKey.Test.Core.RawClientTests; + +[TestFixture] +[Parallelizable(ParallelScope.Self)] +public class MultipartFormTests +{ + private static SimpleObject _simpleObject = new(); + + private static string _simpleFormEncoded = + "meta=data&Date=2023-10-01&Time=12:00:00&Duration=01:00:00&Id=1a1bb98f-47c6-407b-9481-78476affe52a&IsActive=true&Count=42&Initial=A&Values=data,2023-10-01,12:00:00,01:00:00,1a1bb98f-47c6-407b-9481-78476affe52a,true,42,A"; + + private static string _simpleExplodedFormEncoded = + "meta=data&Date=2023-10-01&Time=12:00:00&Duration=01:00:00&Id=1a1bb98f-47c6-407b-9481-78476affe52a&IsActive=true&Count=42&Initial=A&Values=data&Values=2023-10-01&Values=12:00:00&Values=01:00:00&Values=1a1bb98f-47c6-407b-9481-78476affe52a&Values=true&Values=42&Values=A"; + + private static ComplexObject _complexObject = new(); + + private static string _complexJson = """ + { + "meta": "data", + "Nested": { + "foo": "value" + }, + "NestedDictionary": { + "key": { + "foo": "value" + } + }, + "ListOfObjects": [ + { + "foo": "value" + }, + { + "foo": "value2" + } + ], + "Date": "2023-10-01", + "Time": "12:00:00", + "Duration": "01:00:00", + "Id": "1a1bb98f-47c6-407b-9481-78476affe52a", + "IsActive": true, + "Count": 42, + "Initial": "A" + } + """; + + [Test] + public async SystemTask ShouldAddStringPart() + { + const string partInput = "string content"; + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddStringPart("string", partInput); + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: text/plain + Content-Disposition: form-data; name=string + + {partInput} + --{boundary}-- + """; + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddStringParts() + { + const string partInput = "string content"; + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddStringParts("strings", [partInput, partInput]); + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: text/plain + Content-Disposition: form-data; name=strings + + {partInput} + --{boundary} + Content-Type: text/plain + Content-Disposition: form-data; name=strings + + {partInput} + --{boundary}-- + """; + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask GivenNull_ShouldNotAddStringPart() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddStringPart("string", null); + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + --{boundary}-- + """; + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddStringParts_WithNullsInList() + { + const string partInput = "string content"; + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddStringParts("strings", [partInput, null, partInput]); + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: text/plain + Content-Disposition: form-data; name=strings + + {partInput} + --{boundary} + Content-Type: text/plain + Content-Disposition: form-data; name=strings + + {partInput} + --{boundary}-- + """; + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddStringPart_WithContentType() + { + const string partInput = "string content"; + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddStringPart("string", partInput, "text/xml"); + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: text/xml + Content-Disposition: form-data; name=string + + {partInput} + --{boundary}-- + """; + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddStringPart_WithContentTypeAndCharset() + { + const string partInput = "string content"; + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddStringPart("string", partInput, "text/xml; charset=utf-8"); + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: text/xml; charset=utf-8 + Content-Disposition: form-data; name=string + + {partInput} + --{boundary}-- + """; + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddStringParts_WithContentType() + { + const string partInput = "string content"; + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddStringParts("strings", [partInput, partInput], "text/xml"); + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: text/xml + Content-Disposition: form-data; name=strings + + {partInput} + --{boundary} + Content-Type: text/xml + Content-Disposition: form-data; name=strings + + {partInput} + --{boundary}-- + """; + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddStringParts_WithContentTypeAndCharset() + { + const string partInput = "string content"; + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddStringParts( + "strings", + [partInput, partInput], + "text/xml; charset=utf-8" + ); + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: text/xml; charset=utf-8 + Content-Disposition: form-data; name=strings + + {partInput} + --{boundary} + Content-Type: text/xml; charset=utf-8 + Content-Disposition: form-data; name=strings + + {partInput} + --{boundary}-- + """; + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddFileParameter_WithFileName() + { + var (partInput, partExpectedString) = GetFileParameterTestData(); + var file = new FileParameter { Stream = partInput, FileName = "test.txt" }; + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddFileParameterPart("file", file); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: application/octet-stream + Content-Disposition: form-data; name=file; filename=test.txt; filename*=utf-8''test.txt + + {partExpectedString} + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddFileParameter_WithoutFileName() + { + var (partInput, partExpectedString) = GetFileParameterTestData(); + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddFileParameterPart("file", partInput); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: application/octet-stream + Content-Disposition: form-data; name=file + + {partExpectedString} + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddFileParameter_WithContentType() + { + var (partInput, partExpectedString) = GetFileParameterTestData(); + var file = new FileParameter + { + Stream = partInput, + FileName = "test.txt", + ContentType = "text/plain", + }; + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddFileParameterPart("file", file, "ignored-fallback-content-type"); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: text/plain + Content-Disposition: form-data; name=file; filename=test.txt; filename*=utf-8''test.txt + + {partExpectedString} + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddFileParameter_WithContentTypeAndCharset() + { + var (partInput, partExpectedString) = GetFileParameterTestData(); + var file = new FileParameter + { + Stream = partInput, + FileName = "test.txt", + ContentType = "text/plain; charset=utf-8", + }; + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddFileParameterPart("file", file, "ignored-fallback-content-type"); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: text/plain; charset=utf-8 + Content-Disposition: form-data; name=file; filename=test.txt; filename*=utf-8''test.txt + + {partExpectedString} + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddFileParameter_WithFallbackContentType() + { + var (partInput, partExpectedString) = GetFileParameterTestData(); + var file = new FileParameter { Stream = partInput, FileName = "test.txt" }; + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddFileParameterPart("file", file, "text/plain"); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: text/plain + Content-Disposition: form-data; name=file; filename=test.txt; filename*=utf-8''test.txt + + {partExpectedString} + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddFileParameter_WithFallbackContentTypeAndCharset() + { + var (partInput, partExpectedString) = GetFileParameterTestData(); + var file = new FileParameter { Stream = partInput, FileName = "test.txt" }; + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddFileParameterPart("file", file, "text/plain; charset=utf-8"); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: text/plain; charset=utf-8 + Content-Disposition: form-data; name=file; filename=test.txt; filename*=utf-8''test.txt + + {partExpectedString} + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddFileParameters() + { + var (partInput1, partExpectedString1) = GetFileParameterTestData(); + var (partInput2, partExpectedString2) = GetFileParameterTestData(); + var file1 = new FileParameter { Stream = partInput1, FileName = "test1.txt" }; + var file2 = new FileParameter { Stream = partInput2, FileName = "test2.txt" }; + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddFileParameterParts("file", [file1, file2]); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: application/octet-stream + Content-Disposition: form-data; name=file; filename=test1.txt; filename*=utf-8''test1.txt + + {partExpectedString1} + --{boundary} + Content-Type: application/octet-stream + Content-Disposition: form-data; name=file; filename=test2.txt; filename*=utf-8''test2.txt + + {partExpectedString2} + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddFileParameters_WithNullsInList() + { + var (partInput1, partExpectedString1) = GetFileParameterTestData(); + var (partInput2, partExpectedString2) = GetFileParameterTestData(); + var file1 = new FileParameter { Stream = partInput1, FileName = "test1.txt" }; + var file2 = new FileParameter { Stream = partInput2, FileName = "test2.txt" }; + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddFileParameterParts("file", [file1, null, file2]); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: application/octet-stream + Content-Disposition: form-data; name=file; filename=test1.txt; filename*=utf-8''test1.txt + + {partExpectedString1} + --{boundary} + Content-Type: application/octet-stream + Content-Disposition: form-data; name=file; filename=test2.txt; filename*=utf-8''test2.txt + + {partExpectedString2} + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask GivenNull_ShouldNotAddFileParameter() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddFileParameterPart("file", null); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddJsonPart_WithComplexObject() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddJsonPart("object", _complexObject); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: application/json + Content-Disposition: form-data; name=object + + {_complexJson} + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddJsonPart_WithComplexObjectList() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddJsonParts("objects", [_complexObject, _complexObject]); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: application/json + Content-Disposition: form-data; name=objects + + {_complexJson} + --{boundary} + Content-Type: application/json + Content-Disposition: form-data; name=objects + + {_complexJson} + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask GivenNull_ShouldNotAddJsonPart() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddJsonPart("object", null); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddJsonParts_WithNullsInList() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddJsonParts("objects", [_complexObject, null]); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: application/json + Content-Disposition: form-data; name=objects + + {_complexJson} + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddJsonParts_WithContentType() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddJsonParts("objects", [new { }], "application/json-patch+json"); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $$""" + --{{boundary}} + Content-Type: application/json-patch+json + Content-Disposition: form-data; name=objects + + {} + --{{boundary}}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddFormEncodedParts_WithSimpleObject() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddFormEncodedPart("object", _simpleObject); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: application/x-www-form-urlencoded + Content-Disposition: form-data; name=object + + {EscapeFormEncodedString(_simpleFormEncoded)} + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddFormEncodedParts_WithSimpleObjectList() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddFormEncodedParts("objects", [_simpleObject, _simpleObject]); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: application/x-www-form-urlencoded + Content-Disposition: form-data; name=objects + + {EscapeFormEncodedString(_simpleFormEncoded)} + --{boundary} + Content-Type: application/x-www-form-urlencoded + Content-Disposition: form-data; name=objects + + {EscapeFormEncodedString(_simpleFormEncoded)} + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldNotAddFormEncodedParts_WithNull() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddFormEncodedParts("object", null); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldNotAddFormEncodedParts_WithNullsInList() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddFormEncodedParts("objects", [_simpleObject, null]); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: application/x-www-form-urlencoded + Content-Disposition: form-data; name=objects + + {EscapeFormEncodedString(_simpleFormEncoded)} + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddFormEncodedPart_WithContentType() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddFormEncodedPart( + "objects", + new { foo = "bar" }, + "application/x-www-form-urlencoded" + ); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: application/x-www-form-urlencoded + Content-Disposition: form-data; name=objects + + foo=bar + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddFormEncodedPart_WithContentTypeAndCharset() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddFormEncodedPart( + "objects", + new { foo = "bar" }, + "application/x-www-form-urlencoded; charset=utf-8" + ); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: application/x-www-form-urlencoded; charset=utf-8 + Content-Disposition: form-data; name=objects + + foo=bar + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddFormEncodedParts_WithContentType() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddFormEncodedParts( + "objects", + [new { foo = "bar" }], + "application/x-www-form-urlencoded" + ); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: application/x-www-form-urlencoded + Content-Disposition: form-data; name=objects + + foo=bar + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddFormEncodedParts_WithContentTypeAndCharset() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddFormEncodedParts( + "objects", + [new { foo = "bar" }], + "application/x-www-form-urlencoded; charset=utf-8" + ); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: application/x-www-form-urlencoded; charset=utf-8 + Content-Disposition: form-data; name=objects + + foo=bar + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddExplodedFormEncodedParts_WithSimpleObject() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddExplodedFormEncodedPart("object", _simpleObject); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: application/x-www-form-urlencoded + Content-Disposition: form-data; name=object + + {EscapeFormEncodedString(_simpleExplodedFormEncoded)} + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddExplodedFormEncodedParts_WithSimpleObjectList() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddExplodedFormEncodedParts("objects", [_simpleObject, _simpleObject]); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: application/x-www-form-urlencoded + Content-Disposition: form-data; name=objects + + {EscapeFormEncodedString(_simpleExplodedFormEncoded)} + --{boundary} + Content-Type: application/x-www-form-urlencoded + Content-Disposition: form-data; name=objects + + {EscapeFormEncodedString(_simpleExplodedFormEncoded)} + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldNotAddExplodedFormEncodedParts_WithNull() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddExplodedFormEncodedPart("object", null); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldNotAddExplodedFormEncodedParts_WithNullsInList() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddExplodedFormEncodedParts("objects", [_simpleObject, null]); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: application/x-www-form-urlencoded + Content-Disposition: form-data; name=objects + + {EscapeFormEncodedString(_simpleExplodedFormEncoded)} + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddExplodedFormEncodedPart_WithContentType() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddExplodedFormEncodedPart( + "objects", + new { foo = "bar" }, + "application/x-www-form-urlencoded" + ); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: application/x-www-form-urlencoded + Content-Disposition: form-data; name=objects + + foo=bar + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddExplodedFormEncodedPart_WithContentTypeAndCharset() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddExplodedFormEncodedPart( + "objects", + new { foo = "bar" }, + "application/x-www-form-urlencoded; charset=utf-8" + ); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: application/x-www-form-urlencoded; charset=utf-8 + Content-Disposition: form-data; name=objects + + foo=bar + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddExplodedFormEncodedParts_WithContentType() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddExplodedFormEncodedParts( + "objects", + [new { foo = "bar" }], + "application/x-www-form-urlencoded" + ); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: application/x-www-form-urlencoded + Content-Disposition: form-data; name=objects + + foo=bar + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddExplodedFormEncodedParts_WithContentTypeAndCharset() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddExplodedFormEncodedParts( + "objects", + [new { foo = "bar" }], + "application/x-www-form-urlencoded; charset=utf-8" + ); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: application/x-www-form-urlencoded; charset=utf-8 + Content-Disposition: form-data; name=objects + + foo=bar + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + private static string EscapeFormEncodedString(string input) + { + return string.Join( + "&", + input + .Split('&') + .Select(x => x.Split('=')) + .Select(x => $"{Uri.EscapeDataString(x[0])}={Uri.EscapeDataString(x[1])}") + ); + } + + private static string GetBoundary(MultipartFormDataContent content) + { + return content + .Headers.ContentType?.Parameters.Single(p => + p.Name.Equals("boundary", StringComparison.OrdinalIgnoreCase) + ) + .Value?.Trim('"') ?? throw new global::System.Exception("Boundary not found"); + } + + private static SeedInferredAuthImplicitApiKey.Core.MultipartFormRequest CreateMultipartFormRequest() + { + return new SeedInferredAuthImplicitApiKey.Core.MultipartFormRequest + { + BaseUrl = "https://localhost", + Method = HttpMethod.Post, + Path = "", + }; + } + + private static (Stream partInput, string partExpectedString) GetFileParameterTestData() + { + const string partExpectedString = "file content"; + var partInput = new MemoryStream(Encoding.Default.GetBytes(partExpectedString)); + return (partInput, partExpectedString); + } + + private class SimpleObject + { + [JsonPropertyName("meta")] + public string Meta { get; set; } = "data"; + public DateOnly Date { get; set; } = DateOnly.Parse("2023-10-01"); + public TimeOnly Time { get; set; } = TimeOnly.Parse("12:00:00"); + public TimeSpan Duration { get; set; } = TimeSpan.FromHours(1); + public Guid Id { get; set; } = Guid.Parse("1a1bb98f-47c6-407b-9481-78476affe52a"); + public bool IsActive { get; set; } = true; + public int Count { get; set; } = 42; + public char Initial { get; set; } = 'A'; + public IEnumerable Values { get; set; } = + [ + "data", + DateOnly.Parse("2023-10-01"), + TimeOnly.Parse("12:00:00"), + TimeSpan.FromHours(1), + Guid.Parse("1a1bb98f-47c6-407b-9481-78476affe52a"), + true, + 42, + 'A', + ]; + } + + private class ComplexObject + { + [JsonPropertyName("meta")] + public string Meta { get; set; } = "data"; + + public object Nested { get; set; } = new { foo = "value" }; + + public Dictionary NestedDictionary { get; set; } = + new() { { "key", new { foo = "value" } } }; + + public IEnumerable ListOfObjects { get; set; } = + new List { new { foo = "value" }, new { foo = "value2" } }; + + public DateOnly Date { get; set; } = DateOnly.Parse("2023-10-01"); + public TimeOnly Time { get; set; } = TimeOnly.Parse("12:00:00"); + public TimeSpan Duration { get; set; } = TimeSpan.FromHours(1); + public Guid Id { get; set; } = Guid.Parse("1a1bb98f-47c6-407b-9481-78476affe52a"); + public bool IsActive { get; set; } = true; + public int Count { get; set; } = 42; + public char Initial { get; set; } = 'A'; + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/RawClientTests/QueryParameterTests.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/RawClientTests/QueryParameterTests.cs new file mode 100644 index 000000000000..93b6794eae69 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/RawClientTests/QueryParameterTests.cs @@ -0,0 +1,64 @@ +using NUnit.Framework; +using SeedInferredAuthImplicitApiKey.Core; +using WireMock.Matchers; +using WireMock.Server; +using SystemTask = global::System.Threading.Tasks.Task; +using WireMockRequest = WireMock.RequestBuilders.Request; +using WireMockResponse = WireMock.ResponseBuilders.Response; + +namespace SeedInferredAuthImplicitApiKey.Test.Core.RawClientTests; + +[TestFixture] +[Parallelizable(ParallelScope.Self)] +public class QueryParameterTests +{ + private WireMockServer _server; + private HttpClient _httpClient; + private RawClient _rawClient; + private string _baseUrl; + + [SetUp] + public void SetUp() + { + _server = WireMockServer.Start(); + _baseUrl = _server.Url ?? ""; + _httpClient = new HttpClient { BaseAddress = new Uri(_baseUrl) }; + _rawClient = new RawClient(new ClientOptions { HttpClient = _httpClient }); + } + + [Test] + public async SystemTask CreateRequest_QueryParametersEscaping() + { + _server + .Given(WireMockRequest.Create().WithPath("/test").WithParam("foo", "bar").UsingGet()) + .RespondWith(WireMockResponse.Create().WithStatusCode(200).WithBody("Success")); + + var request = new JsonRequest() + { + BaseUrl = _baseUrl, + Method = HttpMethod.Get, + Path = "/test", + Query = new Dictionary + { + { "sample", "value" }, + { "email", "bob+test@example.com" }, + { "%Complete", "100" }, + }, + Options = new RequestOptions(), + }; + + var httpRequest = await _rawClient.CreateHttpRequestAsync(request).ConfigureAwait(false); + var url = httpRequest.RequestUri!.AbsoluteUri; + + Assert.That(url, Does.Contain("sample=value")); + Assert.That(url, Does.Contain("email=bob%2Btest%40example.com")); + Assert.That(url, Does.Contain("%25Complete=100")); + } + + [TearDown] + public void TearDown() + { + _server.Dispose(); + _httpClient.Dispose(); + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/RawClientTests/RetriesTests.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/RawClientTests/RetriesTests.cs new file mode 100644 index 000000000000..95aca9bab8be --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Core/RawClientTests/RetriesTests.cs @@ -0,0 +1,327 @@ +using global::System.Net.Http; +using NUnit.Framework; +using SeedInferredAuthImplicitApiKey.Core; +using WireMock.Server; +using SystemTask = global::System.Threading.Tasks.Task; +using WireMockRequest = WireMock.RequestBuilders.Request; +using WireMockResponse = WireMock.ResponseBuilders.Response; + +namespace SeedInferredAuthImplicitApiKey.Test.Core.RawClientTests; + +[TestFixture] +[Parallelizable(ParallelScope.Self)] +public class RetriesTests +{ + private const int MaxRetries = 3; + private WireMockServer _server; + private HttpClient _httpClient; + private RawClient _rawClient; + private string _baseUrl; + + [SetUp] + public void SetUp() + { + _server = WireMockServer.Start(); + _baseUrl = _server.Url ?? ""; + _httpClient = new HttpClient { BaseAddress = new Uri(_baseUrl) }; + _rawClient = new RawClient( + new ClientOptions { HttpClient = _httpClient, MaxRetries = MaxRetries } + ) + { + BaseRetryDelay = 0, + }; + } + + [Test] + [TestCase(408)] + [TestCase(429)] + [TestCase(500)] + [TestCase(504)] + public async SystemTask SendRequestAsync_ShouldRetry_OnRetryableStatusCodes(int statusCode) + { + _server + .Given(WireMockRequest.Create().WithPath("/test").UsingGet()) + .InScenario("Retry") + .WillSetStateTo("Server Error") + .RespondWith(WireMockResponse.Create().WithStatusCode(statusCode)); + + _server + .Given(WireMockRequest.Create().WithPath("/test").UsingGet()) + .InScenario("Retry") + .WhenStateIs("Server Error") + .WillSetStateTo("Success") + .RespondWith(WireMockResponse.Create().WithStatusCode(statusCode)); + + _server + .Given(WireMockRequest.Create().WithPath("/test").UsingGet()) + .InScenario("Retry") + .WhenStateIs("Success") + .RespondWith(WireMockResponse.Create().WithStatusCode(200).WithBody("Success")); + + var request = new EmptyRequest + { + BaseUrl = _baseUrl, + Method = HttpMethod.Get, + Path = "/test", + }; + + var response = await _rawClient.SendRequestAsync(request); + Assert.That(response.StatusCode, Is.EqualTo(200)); + + var content = await response.Raw.Content.ReadAsStringAsync(); + using (Assert.EnterMultipleScope()) + { + Assert.That(content, Is.EqualTo("Success")); + + Assert.That(_server.LogEntries, Has.Count.EqualTo(MaxRetries)); + } + } + + [Test] + [TestCase(400)] + [TestCase(409)] + public async SystemTask SendRequestAsync_ShouldRetry_OnNonRetryableStatusCodes(int statusCode) + { + _server + .Given(WireMockRequest.Create().WithPath("/test").UsingGet()) + .InScenario("Retry") + .WillSetStateTo("Server Error") + .RespondWith(WireMockResponse.Create().WithStatusCode(statusCode).WithBody("Failure")); + + var request = new JsonRequest + { + BaseUrl = _baseUrl, + Method = HttpMethod.Get, + Path = "/test", + Body = new { }, + }; + + var response = await _rawClient.SendRequestAsync(request); + Assert.That(response.StatusCode, Is.EqualTo(statusCode)); + + var content = await response.Raw.Content.ReadAsStringAsync(); + Assert.Multiple(() => + { + Assert.That(content, Is.EqualTo("Failure")); + + Assert.That(_server.LogEntries, Has.Count.EqualTo(1)); + }); + } + + [Test] + public async SystemTask SendRequestAsync_ShouldNotRetry_WithStreamRequest() + { + _server + .Given(WireMockRequest.Create().WithPath("/test").UsingPost()) + .InScenario("Retry") + .WillSetStateTo("Server Error") + .RespondWith(WireMockResponse.Create().WithStatusCode(429).WithBody("Failure")); + + var request = new StreamRequest + { + BaseUrl = _baseUrl, + Method = HttpMethod.Post, + Path = "/test", + Body = new MemoryStream(), + }; + + var response = await _rawClient.SendRequestAsync(request); + Assert.That(response.StatusCode, Is.EqualTo(429)); + + var content = await response.Raw.Content.ReadAsStringAsync(); + Assert.Multiple(() => + { + Assert.That(content, Is.EqualTo("Failure")); + Assert.That(_server.LogEntries, Has.Count.EqualTo(1)); + }); + } + + [Test] + public async SystemTask SendRequestAsync_ShouldNotRetry_WithMultiPartFormRequest_WithStream() + { + _server + .Given(WireMockRequest.Create().WithPath("/test").UsingPost()) + .InScenario("Retry") + .WillSetStateTo("Server Error") + .RespondWith(WireMockResponse.Create().WithStatusCode(429).WithBody("Failure")); + + var request = new SeedInferredAuthImplicitApiKey.Core.MultipartFormRequest + { + BaseUrl = _baseUrl, + Method = HttpMethod.Post, + Path = "/test", + }; + request.AddFileParameterPart("file", new MemoryStream()); + + var response = await _rawClient.SendRequestAsync(request); + Assert.That(response.StatusCode, Is.EqualTo(429)); + + var content = await response.Raw.Content.ReadAsStringAsync(); + Assert.Multiple(() => + { + Assert.That(content, Is.EqualTo("Failure")); + Assert.That(_server.LogEntries, Has.Count.EqualTo(1)); + }); + } + + [Test] + public async SystemTask SendRequestAsync_ShouldRetry_WithMultiPartFormRequest_WithoutStream() + { + _server + .Given(WireMockRequest.Create().WithPath("/test").UsingPost()) + .InScenario("Retry") + .WillSetStateTo("Server Error") + .RespondWith(WireMockResponse.Create().WithStatusCode(429)); + + _server + .Given(WireMockRequest.Create().WithPath("/test").UsingPost()) + .InScenario("Retry") + .WhenStateIs("Server Error") + .WillSetStateTo("Success") + .RespondWith(WireMockResponse.Create().WithStatusCode(429)); + + _server + .Given(WireMockRequest.Create().WithPath("/test").UsingPost()) + .InScenario("Retry") + .WhenStateIs("Success") + .RespondWith(WireMockResponse.Create().WithStatusCode(200).WithBody("Success")); + + var request = new SeedInferredAuthImplicitApiKey.Core.MultipartFormRequest + { + BaseUrl = _baseUrl, + Method = HttpMethod.Post, + Path = "/test", + }; + request.AddJsonPart("object", new { }); + + var response = await _rawClient.SendRequestAsync(request); + Assert.That(response.StatusCode, Is.EqualTo(200)); + + var content = await response.Raw.Content.ReadAsStringAsync(); + Assert.Multiple(() => + { + Assert.That(content, Is.EqualTo("Success")); + Assert.That(_server.LogEntries, Has.Count.EqualTo(MaxRetries)); + }); + } + + [Test] + public async SystemTask SendRequestAsync_ShouldRespectRetryAfterHeader_WithSecondsValue() + { + _server + .Given(WireMockRequest.Create().WithPath("/test").UsingGet()) + .InScenario("RetryAfter") + .WillSetStateTo("Success") + .RespondWith( + WireMockResponse.Create().WithStatusCode(429).WithHeader("Retry-After", "1") + ); + + _server + .Given(WireMockRequest.Create().WithPath("/test").UsingGet()) + .InScenario("RetryAfter") + .WhenStateIs("Success") + .RespondWith(WireMockResponse.Create().WithStatusCode(200).WithBody("Success")); + + var request = new EmptyRequest + { + BaseUrl = _baseUrl, + Method = HttpMethod.Get, + Path = "/test", + }; + + var response = await _rawClient.SendRequestAsync(request); + Assert.That(response.StatusCode, Is.EqualTo(200)); + + var content = await response.Raw.Content.ReadAsStringAsync(); + Assert.Multiple(() => + { + Assert.That(content, Is.EqualTo("Success")); + Assert.That(_server.LogEntries, Has.Count.EqualTo(2)); + }); + } + + [Test] + public async SystemTask SendRequestAsync_ShouldRespectRetryAfterHeader_WithHttpDateValue() + { + var retryAfterDate = DateTimeOffset.UtcNow.AddSeconds(1).ToString("R"); + _server + .Given(WireMockRequest.Create().WithPath("/test").UsingGet()) + .InScenario("RetryAfterDate") + .WillSetStateTo("Success") + .RespondWith( + WireMockResponse + .Create() + .WithStatusCode(429) + .WithHeader("Retry-After", retryAfterDate) + ); + + _server + .Given(WireMockRequest.Create().WithPath("/test").UsingGet()) + .InScenario("RetryAfterDate") + .WhenStateIs("Success") + .RespondWith(WireMockResponse.Create().WithStatusCode(200).WithBody("Success")); + + var request = new EmptyRequest + { + BaseUrl = _baseUrl, + Method = HttpMethod.Get, + Path = "/test", + }; + + var response = await _rawClient.SendRequestAsync(request); + Assert.That(response.StatusCode, Is.EqualTo(200)); + + var content = await response.Raw.Content.ReadAsStringAsync(); + Assert.Multiple(() => + { + Assert.That(content, Is.EqualTo("Success")); + Assert.That(_server.LogEntries, Has.Count.EqualTo(2)); + }); + } + + [Test] + public async SystemTask SendRequestAsync_ShouldRespectXRateLimitResetHeader() + { + var resetTime = DateTimeOffset.UtcNow.AddSeconds(1).ToUnixTimeSeconds().ToString(); + _server + .Given(WireMockRequest.Create().WithPath("/test").UsingGet()) + .InScenario("RateLimitReset") + .WillSetStateTo("Success") + .RespondWith( + WireMockResponse + .Create() + .WithStatusCode(429) + .WithHeader("X-RateLimit-Reset", resetTime) + ); + + _server + .Given(WireMockRequest.Create().WithPath("/test").UsingGet()) + .InScenario("RateLimitReset") + .WhenStateIs("Success") + .RespondWith(WireMockResponse.Create().WithStatusCode(200).WithBody("Success")); + + var request = new EmptyRequest + { + BaseUrl = _baseUrl, + Method = HttpMethod.Get, + Path = "/test", + }; + + var response = await _rawClient.SendRequestAsync(request); + Assert.That(response.StatusCode, Is.EqualTo(200)); + + var content = await response.Raw.Content.ReadAsStringAsync(); + Assert.Multiple(() => + { + Assert.That(content, Is.EqualTo("Success")); + Assert.That(_server.LogEntries, Has.Count.EqualTo(2)); + }); + } + + [TearDown] + public void TearDown() + { + _server.Dispose(); + _httpClient.Dispose(); + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/SeedInferredAuthImplicitApiKey.Test.Custom.props b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/SeedInferredAuthImplicitApiKey.Test.Custom.props new file mode 100644 index 000000000000..aac9b5020d80 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/SeedInferredAuthImplicitApiKey.Test.Custom.props @@ -0,0 +1,6 @@ + + diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/SeedInferredAuthImplicitApiKey.Test.csproj b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/SeedInferredAuthImplicitApiKey.Test.csproj new file mode 100644 index 000000000000..ef25e188c123 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/SeedInferredAuthImplicitApiKey.Test.csproj @@ -0,0 +1,39 @@ + + + net8.0 + 12 + enable + enable + false + true + true + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/TestClient.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/TestClient.cs new file mode 100644 index 000000000000..0729417a3f6f --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/TestClient.cs @@ -0,0 +1,6 @@ +using NUnit.Framework; + +namespace SeedInferredAuthImplicitApiKey.Test; + +[TestFixture] +public class TestClient; diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Unit/MockServer/BaseMockServerTest.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Unit/MockServer/BaseMockServerTest.cs new file mode 100644 index 000000000000..9577dc6ba43a --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Unit/MockServer/BaseMockServerTest.cs @@ -0,0 +1,67 @@ +using NUnit.Framework; +using SeedInferredAuthImplicitApiKey; +using WireMock.Logging; +using WireMock.Server; +using WireMock.Settings; + +namespace SeedInferredAuthImplicitApiKey.Test.Unit.MockServer; + +[SetUpFixture] +public class BaseMockServerTest +{ + protected static WireMockServer Server { get; set; } = null!; + + protected static SeedInferredAuthImplicitApiKeyClient Client { get; set; } = null!; + + protected static RequestOptions RequestOptions { get; set; } = new(); + + private void MockInferredAuthEndpoint() + { + const string mockResponse = """ + { + "access_token": "access_token", + "token_type": "token_type", + "expires_in": 1, + "scope": "scope" + } + """; + + Server + .Given( + WireMock + .RequestBuilders.Request.Create() + .WithPath("/token") + .WithHeader("X-Api-Key", "X-Api-Key") + .UsingPost() + ) + .RespondWith( + WireMock + .ResponseBuilders.Response.Create() + .WithStatusCode(200) + .WithBody(mockResponse) + ); + } + + [OneTimeSetUp] + public void GlobalSetup() + { + // Start the WireMock server + Server = WireMockServer.Start( + new WireMockServerSettings { Logger = new WireMockConsoleLogger() } + ); + + // Initialize the Client + Client = new SeedInferredAuthImplicitApiKeyClient( + "X-Api-Key", + clientOptions: new ClientOptions { BaseUrl = Server.Urls[0], MaxRetries = 0 } + ); + MockInferredAuthEndpoint(); + } + + [OneTimeTearDown] + public void GlobalTeardown() + { + Server.Stop(); + Server.Dispose(); + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Unit/MockServer/GetSomethingTest.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Unit/MockServer/GetSomethingTest.cs new file mode 100644 index 000000000000..e68eede9d45f --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Unit/MockServer/GetSomethingTest.cs @@ -0,0 +1,17 @@ +using NUnit.Framework; + +namespace SeedInferredAuthImplicitApiKey.Test.Unit.MockServer; + +[TestFixture] +public class GetSomethingTest : BaseMockServerTest +{ + [NUnit.Framework.Test] + public void MockServerTest() + { + Server + .Given(WireMock.RequestBuilders.Request.Create().WithPath("/get-something").UsingGet()) + .RespondWith(WireMock.ResponseBuilders.Response.Create().WithStatusCode(200)); + + Assert.DoesNotThrowAsync(async () => await Client.Simple.GetSomethingAsync()); + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Unit/MockServer/GetTokenTest.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Unit/MockServer/GetTokenTest.cs new file mode 100644 index 000000000000..f5c5552fbef6 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Unit/MockServer/GetTokenTest.cs @@ -0,0 +1,43 @@ +using NUnit.Framework; +using SeedInferredAuthImplicitApiKey; +using SeedInferredAuthImplicitApiKey.Core; + +namespace SeedInferredAuthImplicitApiKey.Test.Unit.MockServer; + +[TestFixture] +public class GetTokenTest : BaseMockServerTest +{ + [NUnit.Framework.Test] + public async Task MockServerTest() + { + const string mockResponse = """ + { + "access_token": "access_token", + "token_type": "token_type", + "expires_in": 1, + "scope": "scope" + } + """; + + Server + .Given( + WireMock + .RequestBuilders.Request.Create() + .WithPath("/token") + .WithHeader("X-Api-Key", "api_key") + .UsingPost() + ) + .RespondWith( + WireMock + .ResponseBuilders.Response.Create() + .WithStatusCode(200) + .WithBody(mockResponse) + ); + + var response = await Client.Auth.GetTokenAsync(new GetTokenRequest { ApiKey = "api_key" }); + Assert.That( + response, + Is.EqualTo(JsonUtils.Deserialize(mockResponse)).UsingDefaults() + ); + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Unit/MockServer/Nested/GetSomethingTest.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Unit/MockServer/Nested/GetSomethingTest.cs new file mode 100644 index 000000000000..9ae18321613e --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Unit/MockServer/Nested/GetSomethingTest.cs @@ -0,0 +1,23 @@ +using NUnit.Framework; +using SeedInferredAuthImplicitApiKey.Test.Unit.MockServer; + +namespace SeedInferredAuthImplicitApiKey.Test.Unit.MockServer.Nested; + +[TestFixture] +public class GetSomethingTest : BaseMockServerTest +{ + [NUnit.Framework.Test] + public void MockServerTest() + { + Server + .Given( + WireMock + .RequestBuilders.Request.Create() + .WithPath("/nested/get-something") + .UsingGet() + ) + .RespondWith(WireMock.ResponseBuilders.Response.Create().WithStatusCode(200)); + + Assert.DoesNotThrowAsync(async () => await Client.Nested.Api.GetSomethingAsync()); + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Unit/MockServer/NestedNoAuth/GetSomethingTest.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Unit/MockServer/NestedNoAuth/GetSomethingTest.cs new file mode 100644 index 000000000000..00ca50f5e3a4 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Unit/MockServer/NestedNoAuth/GetSomethingTest.cs @@ -0,0 +1,23 @@ +using NUnit.Framework; +using SeedInferredAuthImplicitApiKey.Test.Unit.MockServer; + +namespace SeedInferredAuthImplicitApiKey.Test.Unit.MockServer.NestedNoAuth; + +[TestFixture] +public class GetSomethingTest : BaseMockServerTest +{ + [NUnit.Framework.Test] + public void MockServerTest() + { + Server + .Given( + WireMock + .RequestBuilders.Request.Create() + .WithPath("/nested-no-auth/get-something") + .UsingGet() + ) + .RespondWith(WireMock.ResponseBuilders.Response.Create().WithStatusCode(200)); + + Assert.DoesNotThrowAsync(async () => await Client.NestedNoAuth.Api.GetSomethingAsync()); + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Utils/JsonElementComparer.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Utils/JsonElementComparer.cs new file mode 100644 index 000000000000..1704c99af443 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Utils/JsonElementComparer.cs @@ -0,0 +1,236 @@ +using System.Text.Json; +using NUnit.Framework.Constraints; + +namespace NUnit.Framework; + +/// +/// Extensions for EqualConstraint to handle JsonElement objects. +/// +public static class JsonElementComparerExtensions +{ + /// + /// Extension method for comparing JsonElement objects in NUnit tests. + /// Property order doesn't matter, but array order does matter. + /// Includes special handling for DateTime string formats. + /// + /// The Is.EqualTo() constraint instance. + /// A constraint that can compare JsonElements with detailed diffs. + public static EqualConstraint UsingJsonElementComparer(this EqualConstraint constraint) + { + return constraint.Using(new JsonElementComparer()); + } +} + +/// +/// Equality comparer for JsonElement with detailed reporting. +/// Property order doesn't matter, but array order does matter. +/// Now includes special handling for DateTime string formats with improved null handling. +/// +public class JsonElementComparer : IEqualityComparer +{ + private string _failurePath = string.Empty; + + /// + public bool Equals(JsonElement x, JsonElement y) + { + _failurePath = string.Empty; + return CompareJsonElements(x, y, string.Empty); + } + + /// + public int GetHashCode(JsonElement obj) + { + return JsonSerializer.Serialize(obj).GetHashCode(); + } + + private bool CompareJsonElements(JsonElement x, JsonElement y, string path) + { + // If value kinds don't match, they're not equivalent + if (x.ValueKind != y.ValueKind) + { + _failurePath = $"{path}: Expected {x.ValueKind} but got {y.ValueKind}"; + return false; + } + + switch (x.ValueKind) + { + case JsonValueKind.Object: + return CompareJsonObjects(x, y, path); + + case JsonValueKind.Array: + return CompareJsonArraysInOrder(x, y, path); + + case JsonValueKind.String: + string? xStr = x.GetString(); + string? yStr = y.GetString(); + + // Handle null strings + if (xStr is null && yStr is null) + return true; + + if (xStr is null || yStr is null) + { + _failurePath = + $"{path}: Expected {(xStr is null ? "null" : $"\"{xStr}\"")} but got {(yStr is null ? "null" : $"\"{yStr}\"")}"; + return false; + } + + // Check if they are identical strings + if (xStr == yStr) + return true; + + // Try to handle DateTime strings + if (IsLikelyDateTimeString(xStr) && IsLikelyDateTimeString(yStr)) + { + if (AreEquivalentDateTimeStrings(xStr, yStr)) + return true; + } + + _failurePath = $"{path}: Expected \"{xStr}\" but got \"{yStr}\""; + return false; + + case JsonValueKind.Number: + if (x.GetDecimal() != y.GetDecimal()) + { + _failurePath = $"{path}: Expected {x.GetDecimal()} but got {y.GetDecimal()}"; + return false; + } + + return true; + + case JsonValueKind.True: + case JsonValueKind.False: + if (x.GetBoolean() != y.GetBoolean()) + { + _failurePath = $"{path}: Expected {x.GetBoolean()} but got {y.GetBoolean()}"; + return false; + } + + return true; + + case JsonValueKind.Null: + return true; + + default: + _failurePath = $"{path}: Unsupported JsonValueKind {x.ValueKind}"; + return false; + } + } + + private bool IsLikelyDateTimeString(string? str) + { + // Simple heuristic to identify likely ISO date time strings + return str != null + && (str.Contains("T") && (str.EndsWith("Z") || str.Contains("+") || str.Contains("-"))); + } + + private bool AreEquivalentDateTimeStrings(string str1, string str2) + { + // Try to parse both as DateTime + if (DateTime.TryParse(str1, out DateTime dt1) && DateTime.TryParse(str2, out DateTime dt2)) + { + return dt1 == dt2; + } + + return false; + } + + private bool CompareJsonObjects(JsonElement x, JsonElement y, string path) + { + // Create dictionaries for both JSON objects + var xProps = new Dictionary(); + var yProps = new Dictionary(); + + foreach (var prop in x.EnumerateObject()) + xProps[prop.Name] = prop.Value; + + foreach (var prop in y.EnumerateObject()) + yProps[prop.Name] = prop.Value; + + // Check if all properties in x exist in y + foreach (var key in xProps.Keys) + { + if (!yProps.ContainsKey(key)) + { + _failurePath = $"{path}: Missing property '{key}'"; + return false; + } + } + + // Check if y has extra properties + foreach (var key in yProps.Keys) + { + if (!xProps.ContainsKey(key)) + { + _failurePath = $"{path}: Unexpected property '{key}'"; + return false; + } + } + + // Compare each property value + foreach (var key in xProps.Keys) + { + var propPath = string.IsNullOrEmpty(path) ? key : $"{path}.{key}"; + if (!CompareJsonElements(xProps[key], yProps[key], propPath)) + { + return false; + } + } + + return true; + } + + private bool CompareJsonArraysInOrder(JsonElement x, JsonElement y, string path) + { + var xArray = x.EnumerateArray(); + var yArray = y.EnumerateArray(); + + // Count x elements + var xCount = 0; + var xElements = new List(); + foreach (var item in xArray) + { + xElements.Add(item); + xCount++; + } + + // Count y elements + var yCount = 0; + var yElements = new List(); + foreach (var item in yArray) + { + yElements.Add(item); + yCount++; + } + + // Check if counts match + if (xCount != yCount) + { + _failurePath = $"{path}: Expected {xCount} items but found {yCount}"; + return false; + } + + // Compare elements in order + for (var i = 0; i < xCount; i++) + { + var itemPath = $"{path}[{i}]"; + if (!CompareJsonElements(xElements[i], yElements[i], itemPath)) + { + return false; + } + } + + return true; + } + + /// + public override string ToString() + { + if (!string.IsNullOrEmpty(_failurePath)) + { + return $"JSON comparison failed at {_failurePath}"; + } + + return "JsonElementEqualityComparer"; + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Utils/NUnitExtensions.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Utils/NUnitExtensions.cs new file mode 100644 index 000000000000..426df1245388 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Utils/NUnitExtensions.cs @@ -0,0 +1,28 @@ +using NUnit.Framework.Constraints; + +namespace NUnit.Framework; + +/// +/// Extensions for NUnit constraints. +/// +public static class NUnitExtensions +{ + /// + /// Modifies the EqualConstraint to use our own set of default comparers. + /// + /// + /// + public static EqualConstraint UsingDefaults(this EqualConstraint constraint) => + constraint + .UsingPropertiesComparer() + .UsingReadOnlyMemoryComparer() + .UsingReadOnlyMemoryComparer() + .UsingReadOnlyMemoryComparer() + .UsingReadOnlyMemoryComparer() + .UsingReadOnlyMemoryComparer() + .UsingReadOnlyMemoryComparer() + .UsingReadOnlyMemoryComparer() + .UsingReadOnlyMemoryComparer() + .UsingOneOfComparer() + .UsingJsonElementComparer(); +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Utils/OneOfComparer.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Utils/OneOfComparer.cs new file mode 100644 index 000000000000..0c975b471ff3 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Utils/OneOfComparer.cs @@ -0,0 +1,43 @@ +using NUnit.Framework.Constraints; +using OneOf; + +namespace NUnit.Framework; + +/// +/// Extensions for EqualConstraint to handle OneOf values. +/// +public static class EqualConstraintExtensions +{ + /// + /// Modifies the EqualConstraint to handle OneOf instances by comparing their inner values. + /// This works alongside other comparison modifiers like UsingPropertiesComparer. + /// + /// The EqualConstraint to modify. + /// The same constraint instance for method chaining. + public static EqualConstraint UsingOneOfComparer(this EqualConstraint constraint) + { + // Register a comparer factory for IOneOf types + constraint.Using( + (x, y) => + { + // ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract + if (x.Value is null && y.Value is null) + { + return true; + } + + if (x.Value is null) + { + return false; + } + + var propertiesComparer = new NUnitEqualityComparer(); + var tolerance = Tolerance.Default; + propertiesComparer.CompareProperties = true; + return propertiesComparer.AreEqual(x.Value, y.Value, ref tolerance); + } + ); + + return constraint; + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Utils/ReadOnlyMemoryComparer.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Utils/ReadOnlyMemoryComparer.cs new file mode 100644 index 000000000000..fc0b595a5e54 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey.Test/Utils/ReadOnlyMemoryComparer.cs @@ -0,0 +1,87 @@ +using NUnit.Framework.Constraints; + +namespace NUnit.Framework; + +/// +/// Extensions for NUnit constraints. +/// +public static class ReadOnlyMemoryComparerExtensions +{ + /// + /// Extension method for comparing ReadOnlyMemory<T> in NUnit tests. + /// + /// The type of elements in the ReadOnlyMemory. + /// The Is.EqualTo() constraint instance. + /// A constraint that can compare ReadOnlyMemory<T>. + public static EqualConstraint UsingReadOnlyMemoryComparer(this EqualConstraint constraint) + where T : IComparable + { + return constraint.Using(new ReadOnlyMemoryComparer()); + } +} + +/// +/// Comparer for ReadOnlyMemory<T>. Compares sequences by value. +/// +/// +/// The type of elements in the ReadOnlyMemory. +/// +public class ReadOnlyMemoryComparer : IComparer> + where T : IComparable +{ + /// + public int Compare(ReadOnlyMemory x, ReadOnlyMemory y) + { + // Check if sequences are equal + var xSpan = x.Span; + var ySpan = y.Span; + + // Optimized case for IEquatable implementations + if (typeof(IEquatable).IsAssignableFrom(typeof(T))) + { + var areEqual = xSpan.SequenceEqual(ySpan); + if (areEqual) + { + return 0; // Sequences are equal + } + } + else + { + // Manual equality check for non-IEquatable types + if (xSpan.Length == ySpan.Length) + { + var areEqual = true; + for (var i = 0; i < xSpan.Length; i++) + { + if (!EqualityComparer.Default.Equals(xSpan[i], ySpan[i])) + { + areEqual = false; + break; + } + } + + if (areEqual) + { + return 0; // Sequences are equal + } + } + } + + // For non-equal sequences, we need to return a consistent ordering + // First compare lengths + if (x.Length != y.Length) + return x.Length.CompareTo(y.Length); + + // Same length but different content - compare first differing element + for (var i = 0; i < x.Length; i++) + { + if (!EqualityComparer.Default.Equals(xSpan[i], ySpan[i])) + { + return xSpan[i].CompareTo(ySpan[i]); + } + } + + // Should never reach here if not equal + return 0; + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Auth/AuthClient.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Auth/AuthClient.cs new file mode 100644 index 000000000000..264da1150d2e --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Auth/AuthClient.cs @@ -0,0 +1,65 @@ +using System.Text.Json; +using SeedInferredAuthImplicitApiKey.Core; + +namespace SeedInferredAuthImplicitApiKey; + +public partial class AuthClient +{ + private RawClient _client; + + internal AuthClient(RawClient client) + { + _client = client; + } + + /// + /// await client.Auth.GetTokenAsync(new GetTokenRequest { ApiKey = "api_key" }); + /// + public async Task GetTokenAsync( + GetTokenRequest request, + RequestOptions? options = null, + CancellationToken cancellationToken = default + ) + { + var _headers = new Headers( + new Dictionary() { { "X-Api-Key", request.ApiKey } } + ); + var response = await _client + .SendRequestAsync( + new JsonRequest + { + BaseUrl = _client.Options.BaseUrl, + Method = HttpMethod.Post, + Path = "/token", + Headers = _headers, + Options = options, + }, + cancellationToken + ) + .ConfigureAwait(false); + if (response.StatusCode is >= 200 and < 400) + { + var responseBody = await response.Raw.Content.ReadAsStringAsync(); + try + { + return JsonUtils.Deserialize(responseBody)!; + } + catch (JsonException e) + { + throw new SeedInferredAuthImplicitApiKeyException( + "Failed to deserialize response", + e + ); + } + } + + { + var responseBody = await response.Raw.Content.ReadAsStringAsync(); + throw new SeedInferredAuthImplicitApiKeyApiException( + $"Error with status code {response.StatusCode}", + response.StatusCode, + responseBody + ); + } + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Auth/Requests/GetTokenRequest.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Auth/Requests/GetTokenRequest.cs new file mode 100644 index 000000000000..c75ccbf0922f --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Auth/Requests/GetTokenRequest.cs @@ -0,0 +1,17 @@ +using System.Text.Json.Serialization; +using SeedInferredAuthImplicitApiKey.Core; + +namespace SeedInferredAuthImplicitApiKey; + +[Serializable] +public record GetTokenRequest +{ + [JsonIgnore] + public required string ApiKey { get; set; } + + /// + public override string ToString() + { + return JsonUtils.Serialize(this); + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Auth/Types/TokenResponse.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Auth/Types/TokenResponse.cs new file mode 100644 index 000000000000..369a4e8a5cc4 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Auth/Types/TokenResponse.cs @@ -0,0 +1,40 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using SeedInferredAuthImplicitApiKey.Core; + +namespace SeedInferredAuthImplicitApiKey; + +/// +/// An auth token response. +/// +[Serializable] +public record TokenResponse : IJsonOnDeserialized +{ + [JsonExtensionData] + private readonly IDictionary _extensionData = + new Dictionary(); + + [JsonPropertyName("access_token")] + public required string AccessToken { get; set; } + + [JsonPropertyName("token_type")] + public required string TokenType { get; set; } + + [JsonPropertyName("expires_in")] + public required int ExpiresIn { get; set; } + + [JsonPropertyName("scope")] + public string? Scope { get; set; } + + [JsonIgnore] + public ReadOnlyAdditionalProperties AdditionalProperties { get; private set; } = new(); + + void IJsonOnDeserialized.OnDeserialized() => + AdditionalProperties.CopyFromExtensionData(_extensionData); + + /// + public override string ToString() + { + return JsonUtils.Serialize(this); + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/ApiResponse.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/ApiResponse.cs new file mode 100644 index 000000000000..210d7624d5d6 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/ApiResponse.cs @@ -0,0 +1,13 @@ +using System.Net.Http; + +namespace SeedInferredAuthImplicitApiKey.Core; + +/// +/// The response object returned from the API. +/// +internal record ApiResponse +{ + internal required int StatusCode { get; init; } + + internal required HttpResponseMessage Raw { get; init; } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/BaseRequest.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/BaseRequest.cs new file mode 100644 index 000000000000..86f50441ec06 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/BaseRequest.cs @@ -0,0 +1,63 @@ +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; + +namespace SeedInferredAuthImplicitApiKey.Core; + +internal abstract record BaseRequest +{ + internal required string BaseUrl { get; init; } + + internal required HttpMethod Method { get; init; } + + internal required string Path { get; init; } + + internal string? ContentType { get; init; } + + internal Dictionary Query { get; init; } = new(); + + internal Headers Headers { get; init; } = new(); + + internal IRequestOptions? Options { get; init; } + + internal abstract HttpContent? CreateContent(); + + protected static ( + Encoding encoding, + string? charset, + string mediaType + ) ParseContentTypeOrDefault( + string? contentType, + Encoding encodingFallback, + string mediaTypeFallback + ) + { + var encoding = encodingFallback; + var mediaType = mediaTypeFallback; + string? charset = null; + if (string.IsNullOrEmpty(contentType)) + { + return (encoding, charset, mediaType); + } + + if (!MediaTypeHeaderValue.TryParse(contentType, out var mediaTypeHeaderValue)) + { + return (encoding, charset, mediaType); + } + + if (!string.IsNullOrEmpty(mediaTypeHeaderValue.CharSet)) + { + charset = mediaTypeHeaderValue.CharSet; + encoding = Encoding.GetEncoding(mediaTypeHeaderValue.CharSet); + } + + if (!string.IsNullOrEmpty(mediaTypeHeaderValue.MediaType)) + { + mediaType = mediaTypeHeaderValue.MediaType; + } + + return (encoding, charset, mediaType); + } + + protected static Encoding Utf8NoBom => EncodingCache.Utf8NoBom; +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/CollectionItemSerializer.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/CollectionItemSerializer.cs new file mode 100644 index 000000000000..eb701f5d1910 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/CollectionItemSerializer.cs @@ -0,0 +1,89 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace SeedInferredAuthImplicitApiKey.Core; + +/// +/// Json collection converter. +/// +/// Type of item to convert. +/// Converter to use for individual items. +internal class CollectionItemSerializer + : JsonConverter> + where TConverterType : JsonConverter +{ + /// + /// Reads a json string and deserializes it into an object. + /// + /// Json reader. + /// Type to convert. + /// Serializer options. + /// Created object. + public override IEnumerable? Read( + ref Utf8JsonReader reader, + global::System.Type typeToConvert, + JsonSerializerOptions options + ) + { + if (reader.TokenType == JsonTokenType.Null) + { + return default; + } + + var jsonSerializerOptions = new JsonSerializerOptions(options); + jsonSerializerOptions.Converters.Clear(); + jsonSerializerOptions.Converters.Add(Activator.CreateInstance()); + + var returnValue = new List(); + + while (reader.TokenType != JsonTokenType.EndArray) + { + if (reader.TokenType != JsonTokenType.StartArray) + { + var item = (TDatatype)( + JsonSerializer.Deserialize(ref reader, typeof(TDatatype), jsonSerializerOptions) + ?? throw new global::System.Exception( + $"Failed to deserialize collection item of type {typeof(TDatatype)}" + ) + ); + returnValue.Add(item); + } + + reader.Read(); + } + + return returnValue; + } + + /// + /// Writes a json string. + /// + /// Json writer. + /// Value to write. + /// Serializer options. + public override void Write( + Utf8JsonWriter writer, + IEnumerable? value, + JsonSerializerOptions options + ) + { + if (value == null) + { + writer.WriteNullValue(); + return; + } + + var jsonSerializerOptions = new JsonSerializerOptions(options); + jsonSerializerOptions.Converters.Clear(); + jsonSerializerOptions.Converters.Add(Activator.CreateInstance()); + + writer.WriteStartArray(); + + foreach (var data in value) + { + JsonSerializer.Serialize(writer, data, jsonSerializerOptions); + } + + writer.WriteEndArray(); + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/Constants.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/Constants.cs new file mode 100644 index 000000000000..46133ca0c122 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/Constants.cs @@ -0,0 +1,7 @@ +namespace SeedInferredAuthImplicitApiKey.Core; + +internal static class Constants +{ + public const string DateTimeFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.fffK"; + public const string DateFormat = "yyyy-MM-dd"; +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/DateOnlyConverter.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/DateOnlyConverter.cs new file mode 100644 index 000000000000..6944ed44d437 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/DateOnlyConverter.cs @@ -0,0 +1,747 @@ +// ReSharper disable All +#pragma warning disable + +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using global::System.Diagnostics; +using global::System.Diagnostics.CodeAnalysis; +using global::System.Globalization; +using global::System.Runtime.CompilerServices; +using global::System.Runtime.InteropServices; +using global::System.Text.Json; +using global::System.Text.Json.Serialization; + +// ReSharper disable SuggestVarOrType_SimpleTypes +// ReSharper disable SuggestVarOrType_BuiltInTypes + +namespace SeedInferredAuthImplicitApiKey.Core +{ + /// + /// Custom converter for handling the data type with the System.Text.Json library. + /// + /// + /// This class backported from: + /// + /// System.Text.Json.Serialization.Converters.DateOnlyConverter + /// + public sealed class DateOnlyConverter : JsonConverter + { + private const int FormatLength = 10; // YYYY-MM-DD + + private const int MaxEscapedFormatLength = + FormatLength * JsonConstants.MaxExpansionFactorWhileEscaping; + + /// + public override DateOnly Read( + ref Utf8JsonReader reader, + global::System.Type typeToConvert, + JsonSerializerOptions options + ) + { + if (reader.TokenType != JsonTokenType.String) + { + ThrowHelper.ThrowInvalidOperationException_ExpectedString(reader.TokenType); + } + + return ReadCore(ref reader); + } + + /// + public override DateOnly ReadAsPropertyName( + ref Utf8JsonReader reader, + global::System.Type typeToConvert, + JsonSerializerOptions options + ) + { + Debug.Assert(reader.TokenType == JsonTokenType.PropertyName); + return ReadCore(ref reader); + } + + private static DateOnly ReadCore(ref Utf8JsonReader reader) + { + if ( + !JsonHelpers.IsInRangeInclusive( + reader.ValueLength(), + FormatLength, + MaxEscapedFormatLength + ) + ) + { + ThrowHelper.ThrowFormatException(DataType.DateOnly); + } + + scoped ReadOnlySpan source; + if (!reader.HasValueSequence && !reader.ValueIsEscaped) + { + source = reader.ValueSpan; + } + else + { + Span stackSpan = stackalloc byte[MaxEscapedFormatLength]; + int bytesWritten = reader.CopyString(stackSpan); + source = stackSpan.Slice(0, bytesWritten); + } + + if (!JsonHelpers.TryParseAsIso(source, out DateOnly value)) + { + ThrowHelper.ThrowFormatException(DataType.DateOnly); + } + + return value; + } + + /// + public override void Write( + Utf8JsonWriter writer, + DateOnly value, + JsonSerializerOptions options + ) + { +#if NET8_0_OR_GREATER + Span buffer = stackalloc byte[FormatLength]; +#else + Span buffer = stackalloc char[FormatLength]; +#endif + // ReSharper disable once RedundantAssignment + bool formattedSuccessfully = value.TryFormat( + buffer, + out int charsWritten, + "O".AsSpan(), + CultureInfo.InvariantCulture + ); + Debug.Assert(formattedSuccessfully && charsWritten == FormatLength); + writer.WriteStringValue(buffer); + } + + /// + public override void WriteAsPropertyName( + Utf8JsonWriter writer, + DateOnly value, + JsonSerializerOptions options + ) + { +#if NET8_0_OR_GREATER + Span buffer = stackalloc byte[FormatLength]; +#else + Span buffer = stackalloc char[FormatLength]; +#endif + // ReSharper disable once RedundantAssignment + bool formattedSuccessfully = value.TryFormat( + buffer, + out int charsWritten, + "O".AsSpan(), + CultureInfo.InvariantCulture + ); + Debug.Assert(formattedSuccessfully && charsWritten == FormatLength); + writer.WritePropertyName(buffer); + } + } + + internal static class JsonConstants + { + // The maximum number of fraction digits the Json DateTime parser allows + public const int DateTimeParseNumFractionDigits = 16; + + // In the worst case, an ASCII character represented as a single utf-8 byte could expand 6x when escaped. + public const int MaxExpansionFactorWhileEscaping = 6; + + // The largest fraction expressible by TimeSpan and DateTime formats + public const int MaxDateTimeFraction = 9_999_999; + + // TimeSpan and DateTime formats allow exactly up to many digits for specifying the fraction after the seconds. + public const int DateTimeNumFractionDigits = 7; + + public const byte UtcOffsetToken = (byte)'Z'; + + public const byte TimePrefix = (byte)'T'; + + public const byte Period = (byte)'.'; + + public const byte Hyphen = (byte)'-'; + + public const byte Colon = (byte)':'; + + public const byte Plus = (byte)'+'; + } + + // ReSharper disable SuggestVarOrType_Elsewhere + // ReSharper disable SuggestVarOrType_SimpleTypes + // ReSharper disable SuggestVarOrType_BuiltInTypes + + internal static class JsonHelpers + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsInRangeInclusive(int value, int lowerBound, int upperBound) => + (uint)(value - lowerBound) <= (uint)(upperBound - lowerBound); + + public static bool IsDigit(byte value) => (uint)(value - '0') <= '9' - '0'; + + [StructLayout(LayoutKind.Auto)] + private struct DateTimeParseData + { + public int Year; + public int Month; + public int Day; + public bool IsCalendarDateOnly; + public int Hour; + public int Minute; + public int Second; + public int Fraction; // This value should never be greater than 9_999_999. + public int OffsetHours; + public int OffsetMinutes; + + // ReSharper disable once NotAccessedField.Local + public byte OffsetToken; + } + + public static bool TryParseAsIso(ReadOnlySpan source, out DateOnly value) + { + if ( + TryParseDateTimeOffset(source, out DateTimeParseData parseData) + && parseData.IsCalendarDateOnly + && TryCreateDateTime(parseData, DateTimeKind.Unspecified, out DateTime dateTime) + ) + { + value = DateOnly.FromDateTime(dateTime); + return true; + } + + value = default; + return false; + } + + /// + /// ISO 8601 date time parser (ISO 8601-1:2019). + /// + /// The date/time to parse in UTF-8 format. + /// The parsed for the given . + /// + /// Supports extended calendar date (5.2.2.1) and complete (5.4.2.1) calendar date/time of day + /// representations with optional specification of seconds and fractional seconds. + /// + /// Times can be explicitly specified as UTC ("Z" - 5.3.3) or offsets from UTC ("+/-hh:mm" 5.3.4.2). + /// If unspecified they are considered to be local per spec. + /// + /// Examples: (TZD is either "Z" or hh:mm offset from UTC) + /// + /// YYYY-MM-DD (e.g. 1997-07-16) + /// YYYY-MM-DDThh:mm (e.g. 1997-07-16T19:20) + /// YYYY-MM-DDThh:mm:ss (e.g. 1997-07-16T19:20:30) + /// YYYY-MM-DDThh:mm:ss.s (e.g. 1997-07-16T19:20:30.45) + /// YYYY-MM-DDThh:mmTZD (e.g. 1997-07-16T19:20+01:00) + /// YYYY-MM-DDThh:mm:ssTZD (e.g. 1997-07-16T19:20:3001:00) + /// YYYY-MM-DDThh:mm:ss.sTZD (e.g. 1997-07-16T19:20:30.45Z) + /// + /// Generally speaking we always require the "extended" option when one exists (3.1.3.5). + /// The extended variants have separator characters between components ('-', ':', '.', etc.). + /// Spaces are not permitted. + /// + /// "true" if successfully parsed. + private static bool TryParseDateTimeOffset( + ReadOnlySpan source, + out DateTimeParseData parseData + ) + { + parseData = default; + + // too short datetime + Debug.Assert(source.Length >= 10); + + // Parse the calendar date + // ----------------------- + // ISO 8601-1:2019 5.2.2.1b "Calendar date complete extended format" + // [dateX] = [year]["-"][month]["-"][day] + // [year] = [YYYY] [0000 - 9999] (4.3.2) + // [month] = [MM] [01 - 12] (4.3.3) + // [day] = [DD] [01 - 28, 29, 30, 31] (4.3.4) + // + // Note: 5.2.2.2 "Representations with reduced precision" allows for + // just [year]["-"][month] (a) and just [year] (b), but we currently + // don't permit it. + + { + uint digit1 = source[0] - (uint)'0'; + uint digit2 = source[1] - (uint)'0'; + uint digit3 = source[2] - (uint)'0'; + uint digit4 = source[3] - (uint)'0'; + + if (digit1 > 9 || digit2 > 9 || digit3 > 9 || digit4 > 9) + { + return false; + } + + parseData.Year = (int)(digit1 * 1000 + digit2 * 100 + digit3 * 10 + digit4); + } + + if ( + source[4] != JsonConstants.Hyphen + || !TryGetNextTwoDigits(source.Slice(start: 5, length: 2), ref parseData.Month) + || source[7] != JsonConstants.Hyphen + || !TryGetNextTwoDigits(source.Slice(start: 8, length: 2), ref parseData.Day) + ) + { + return false; + } + + // We now have YYYY-MM-DD [dateX] + // ReSharper disable once ConvertIfStatementToSwitchStatement + if (source.Length == 10) + { + parseData.IsCalendarDateOnly = true; + return true; + } + + // Parse the time of day + // --------------------- + // + // ISO 8601-1:2019 5.3.1.2b "Local time of day complete extended format" + // [timeX] = ["T"][hour][":"][min][":"][sec] + // [hour] = [hh] [00 - 23] (4.3.8a) + // [minute] = [mm] [00 - 59] (4.3.9a) + // [sec] = [ss] [00 - 59, 60 with a leap second] (4.3.10a) + // + // ISO 8601-1:2019 5.3.3 "UTC of day" + // [timeX]["Z"] + // + // ISO 8601-1:2019 5.3.4.2 "Local time of day with the time shift between + // local timescale and UTC" (Extended format) + // + // [shiftX] = ["+"|"-"][hour][":"][min] + // + // Notes: + // + // "T" is optional per spec, but _only_ when times are used alone. In our + // case, we're reading out a complete date & time and as such require "T". + // (5.4.2.1b). + // + // For [timeX] We allow seconds to be omitted per 5.3.1.3a "Representations + // with reduced precision". 5.3.1.3b allows just specifying the hour, but + // we currently don't permit this. + // + // Decimal fractions are allowed for hours, minutes and seconds (5.3.14). + // We only allow fractions for seconds currently. Lower order components + // can't follow, i.e. you can have T23.3, but not T23.3:04. There must be + // one digit, but the max number of digits is implementation defined. We + // currently allow up to 16 digits of fractional seconds only. While we + // support 16 fractional digits we only parse the first seven, anything + // past that is considered a zero. This is to stay compatible with the + // DateTime implementation which is limited to this resolution. + + if (source.Length < 16) + { + // Source does not have enough characters for YYYY-MM-DDThh:mm + return false; + } + + // Parse THH:MM (e.g. "T10:32") + if ( + source[10] != JsonConstants.TimePrefix + || source[13] != JsonConstants.Colon + || !TryGetNextTwoDigits(source.Slice(start: 11, length: 2), ref parseData.Hour) + || !TryGetNextTwoDigits(source.Slice(start: 14, length: 2), ref parseData.Minute) + ) + { + return false; + } + + // We now have YYYY-MM-DDThh:mm + Debug.Assert(source.Length >= 16); + if (source.Length == 16) + { + return true; + } + + byte curByte = source[16]; + int sourceIndex = 17; + + // Either a TZD ['Z'|'+'|'-'] or a seconds separator [':'] is valid at this point + switch (curByte) + { + case JsonConstants.UtcOffsetToken: + parseData.OffsetToken = JsonConstants.UtcOffsetToken; + return sourceIndex == source.Length; + case JsonConstants.Plus: + case JsonConstants.Hyphen: + parseData.OffsetToken = curByte; + return ParseOffset(ref parseData, source.Slice(sourceIndex)); + case JsonConstants.Colon: + break; + default: + return false; + } + + // Try reading the seconds + if ( + source.Length < 19 + || !TryGetNextTwoDigits(source.Slice(start: 17, length: 2), ref parseData.Second) + ) + { + return false; + } + + // We now have YYYY-MM-DDThh:mm:ss + Debug.Assert(source.Length >= 19); + if (source.Length == 19) + { + return true; + } + + curByte = source[19]; + sourceIndex = 20; + + // Either a TZD ['Z'|'+'|'-'] or a seconds decimal fraction separator ['.'] is valid at this point + switch (curByte) + { + case JsonConstants.UtcOffsetToken: + parseData.OffsetToken = JsonConstants.UtcOffsetToken; + return sourceIndex == source.Length; + case JsonConstants.Plus: + case JsonConstants.Hyphen: + parseData.OffsetToken = curByte; + return ParseOffset(ref parseData, source.Slice(sourceIndex)); + case JsonConstants.Period: + break; + default: + return false; + } + + // Source does not have enough characters for second fractions (i.e. ".s") + // YYYY-MM-DDThh:mm:ss.s + if (source.Length < 21) + { + return false; + } + + // Parse fraction. This value should never be greater than 9_999_999 + int numDigitsRead = 0; + int fractionEnd = Math.Min( + sourceIndex + JsonConstants.DateTimeParseNumFractionDigits, + source.Length + ); + + while (sourceIndex < fractionEnd && IsDigit(curByte = source[sourceIndex])) + { + if (numDigitsRead < JsonConstants.DateTimeNumFractionDigits) + { + parseData.Fraction = parseData.Fraction * 10 + (int)(curByte - (uint)'0'); + numDigitsRead++; + } + + sourceIndex++; + } + + if (parseData.Fraction != 0) + { + while (numDigitsRead < JsonConstants.DateTimeNumFractionDigits) + { + parseData.Fraction *= 10; + numDigitsRead++; + } + } + + // We now have YYYY-MM-DDThh:mm:ss.s + Debug.Assert(sourceIndex <= source.Length); + if (sourceIndex == source.Length) + { + return true; + } + + curByte = source[sourceIndex++]; + + // TZD ['Z'|'+'|'-'] is valid at this point + switch (curByte) + { + case JsonConstants.UtcOffsetToken: + parseData.OffsetToken = JsonConstants.UtcOffsetToken; + return sourceIndex == source.Length; + case JsonConstants.Plus: + case JsonConstants.Hyphen: + parseData.OffsetToken = curByte; + return ParseOffset(ref parseData, source.Slice(sourceIndex)); + default: + return false; + } + + static bool ParseOffset(ref DateTimeParseData parseData, ReadOnlySpan offsetData) + { + // Parse the hours for the offset + if ( + offsetData.Length < 2 + || !TryGetNextTwoDigits(offsetData.Slice(0, 2), ref parseData.OffsetHours) + ) + { + return false; + } + + // We now have YYYY-MM-DDThh:mm:ss.s+|-hh + + if (offsetData.Length == 2) + { + // Just hours offset specified + return true; + } + + // Ensure we have enough for ":mm" + return offsetData.Length == 5 + && offsetData[2] == JsonConstants.Colon + && TryGetNextTwoDigits(offsetData.Slice(3), ref parseData.OffsetMinutes); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + // ReSharper disable once RedundantAssignment + private static bool TryGetNextTwoDigits(ReadOnlySpan source, ref int value) + { + Debug.Assert(source.Length == 2); + + uint digit1 = source[0] - (uint)'0'; + uint digit2 = source[1] - (uint)'0'; + + if (digit1 > 9 || digit2 > 9) + { + value = 0; + return false; + } + + value = (int)(digit1 * 10 + digit2); + return true; + } + + // The following methods are borrowed verbatim from src/Common/src/CoreLib/System/Buffers/Text/Utf8Parser/Utf8Parser.Date.Helpers.cs + + /// + /// Overflow-safe DateTime factory. + /// + private static bool TryCreateDateTime( + DateTimeParseData parseData, + DateTimeKind kind, + out DateTime value + ) + { + if (parseData.Year == 0) + { + value = default; + return false; + } + + Debug.Assert(parseData.Year <= 9999); // All of our callers to date parse the year from fixed 4-digit fields so this value is trusted. + + if ((uint)parseData.Month - 1 >= 12) + { + value = default; + return false; + } + + uint dayMinusOne = (uint)parseData.Day - 1; + if ( + dayMinusOne >= 28 + && dayMinusOne >= DateTime.DaysInMonth(parseData.Year, parseData.Month) + ) + { + value = default; + return false; + } + + if ((uint)parseData.Hour > 23) + { + value = default; + return false; + } + + if ((uint)parseData.Minute > 59) + { + value = default; + return false; + } + + // This needs to allow leap seconds when appropriate. + // See https://github.com/dotnet/runtime/issues/30135. + if ((uint)parseData.Second > 59) + { + value = default; + return false; + } + + Debug.Assert(parseData.Fraction is >= 0 and <= JsonConstants.MaxDateTimeFraction); // All of our callers to date parse the fraction from fixed 7-digit fields so this value is trusted. + + ReadOnlySpan days = DateTime.IsLeapYear(parseData.Year) + ? DaysToMonth366 + : DaysToMonth365; + int yearMinusOne = parseData.Year - 1; + int totalDays = + yearMinusOne * 365 + + yearMinusOne / 4 + - yearMinusOne / 100 + + yearMinusOne / 400 + + days[parseData.Month - 1] + + parseData.Day + - 1; + long ticks = totalDays * TimeSpan.TicksPerDay; + int totalSeconds = parseData.Hour * 3600 + parseData.Minute * 60 + parseData.Second; + ticks += totalSeconds * TimeSpan.TicksPerSecond; + ticks += parseData.Fraction; + value = new DateTime(ticks: ticks, kind: kind); + return true; + } + + private static ReadOnlySpan DaysToMonth365 => + [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365]; + private static ReadOnlySpan DaysToMonth366 => + [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366]; + } + + internal static class ThrowHelper + { + private const string ExceptionSourceValueToRethrowAsJsonException = + "System.Text.Json.Rethrowable"; + + [DoesNotReturn] + public static void ThrowInvalidOperationException_ExpectedString(JsonTokenType tokenType) + { + throw GetInvalidOperationException("string", tokenType); + } + + public static void ThrowFormatException(DataType dataType) + { + throw new FormatException(SR.Format(SR.UnsupportedFormat, dataType)) + { + Source = ExceptionSourceValueToRethrowAsJsonException, + }; + } + + private static global::System.Exception GetInvalidOperationException( + string message, + JsonTokenType tokenType + ) + { + return GetInvalidOperationException(SR.Format(SR.InvalidCast, tokenType, message)); + } + + private static InvalidOperationException GetInvalidOperationException(string message) + { + return new InvalidOperationException(message) + { + Source = ExceptionSourceValueToRethrowAsJsonException, + }; + } + } + + internal static class Utf8JsonReaderExtensions + { + internal static int ValueLength(this Utf8JsonReader reader) => + reader.HasValueSequence + ? checked((int)reader.ValueSequence.Length) + : reader.ValueSpan.Length; + } + + internal enum DataType + { + TimeOnly, + DateOnly, + } + + [SuppressMessage("ReSharper", "InconsistentNaming")] + internal static class SR + { + private static readonly bool s_usingResourceKeys = + AppContext.TryGetSwitch( + "System.Resources.UseSystemResourceKeys", + out bool usingResourceKeys + ) && usingResourceKeys; + + public static string UnsupportedFormat => Strings.UnsupportedFormat; + + public static string InvalidCast => Strings.InvalidCast; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static string Format(string resourceFormat, object? p1) => + s_usingResourceKeys + ? string.Join(", ", resourceFormat, p1) + : string.Format(resourceFormat, p1); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static string Format(string resourceFormat, object? p1, object? p2) => + s_usingResourceKeys + ? string.Join(", ", resourceFormat, p1, p2) + : string.Format(resourceFormat, p1, p2); + } + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute( + "System.Resources.Tools.StronglyTypedResourceBuilder", + "17.0.0.0" + )] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Strings + { + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute( + "Microsoft.Performance", + "CA1811:AvoidUncalledPrivateCode" + )] + internal Strings() { } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute( + global::System.ComponentModel.EditorBrowsableState.Advanced + )] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if (object.ReferenceEquals(resourceMan, null)) + { + global::System.Resources.ResourceManager temp = + new global::System.Resources.ResourceManager( + "System.Text.Json.Resources.Strings", + typeof(Strings).Assembly + ); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute( + global::System.ComponentModel.EditorBrowsableState.Advanced + )] + internal static global::System.Globalization.CultureInfo Culture + { + get { return resourceCulture; } + set { resourceCulture = value; } + } + + /// + /// Looks up a localized string similar to Cannot get the value of a token type '{0}' as a {1}.. + /// + internal static string InvalidCast + { + get { return ResourceManager.GetString("InvalidCast", resourceCulture); } + } + + /// + /// Looks up a localized string similar to The JSON value is not in a supported {0} format.. + /// + internal static string UnsupportedFormat + { + get { return ResourceManager.GetString("UnsupportedFormat", resourceCulture); } + } + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/DateTimeSerializer.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/DateTimeSerializer.cs new file mode 100644 index 000000000000..da2b193d24af --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/DateTimeSerializer.cs @@ -0,0 +1,22 @@ +using System.Globalization; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace SeedInferredAuthImplicitApiKey.Core; + +internal class DateTimeSerializer : JsonConverter +{ + public override DateTime Read( + ref Utf8JsonReader reader, + global::System.Type typeToConvert, + JsonSerializerOptions options + ) + { + return DateTime.Parse(reader.GetString()!, null, DateTimeStyles.RoundtripKind); + } + + public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.ToString(Constants.DateTimeFormat)); + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/EmptyRequest.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/EmptyRequest.cs new file mode 100644 index 000000000000..a1613e03c9b5 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/EmptyRequest.cs @@ -0,0 +1,11 @@ +using System.Net.Http; + +namespace SeedInferredAuthImplicitApiKey.Core; + +/// +/// The request object to send without a request body. +/// +internal record EmptyRequest : BaseRequest +{ + internal override HttpContent? CreateContent() => null; +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/EncodingCache.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/EncodingCache.cs new file mode 100644 index 000000000000..8d9a3084c862 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/EncodingCache.cs @@ -0,0 +1,11 @@ +using System.Text; + +namespace SeedInferredAuthImplicitApiKey.Core; + +internal static class EncodingCache +{ + internal static readonly Encoding Utf8NoBom = new UTF8Encoding( + encoderShouldEmitUTF8Identifier: false, + throwOnInvalidBytes: true + ); +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/Extensions.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/Extensions.cs new file mode 100644 index 000000000000..25db313710a1 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/Extensions.cs @@ -0,0 +1,55 @@ +using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization; + +namespace SeedInferredAuthImplicitApiKey.Core; + +internal static class Extensions +{ + public static string Stringify(this Enum value) + { + var field = value.GetType().GetField(value.ToString()); + if (field != null) + { + var attribute = (EnumMemberAttribute?) + global::System.Attribute.GetCustomAttribute(field, typeof(EnumMemberAttribute)); + return attribute?.Value ?? value.ToString(); + } + return value.ToString(); + } + + /// + /// Asserts that a condition is true, throwing an exception with the specified message if it is false. + /// + /// The condition to assert. + /// The exception message if the assertion fails. + /// Thrown when the condition is false. + internal static void Assert(this object value, bool condition, string message) + { + if (!condition) + { + throw new global::System.Exception(message); + } + } + + /// + /// Asserts that a value is not null, throwing an exception with the specified message if it is null. + /// + /// The type of the value to assert. + /// The value to assert is not null. + /// The exception message if the assertion fails. + /// The non-null value. + /// Thrown when the value is null. + internal static TValue Assert( + this object _unused, + [NotNull] TValue? value, + string message + ) + where TValue : class + { + if (value == null) + { + throw new global::System.Exception(message); + } + return value; + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/FormUrlEncoder.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/FormUrlEncoder.cs new file mode 100644 index 000000000000..30f57aeb7931 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/FormUrlEncoder.cs @@ -0,0 +1,33 @@ +using global::System.Net.Http; + +namespace SeedInferredAuthImplicitApiKey.Core; + +/// +/// Encodes an object into a form URL-encoded content. +/// +public static class FormUrlEncoder +{ + /// + /// Encodes an object into a form URL-encoded content using Deep Object notation. + /// + /// Object to form URL-encode. You can pass in an object or dictionary, but not lists, strings, or primitives. + /// Throws when passing in a list, a string, or a primitive value. + internal static FormUrlEncodedContent EncodeAsDeepObject(object value) => + new(QueryStringConverter.ToDeepObject(value)); + + /// + /// Encodes an object into a form URL-encoded content using Exploded Form notation. + /// + /// Object to form URL-encode. You can pass in an object or dictionary, but not lists, strings, or primitives. + /// Throws when passing in a list, a string, or a primitive value. + internal static FormUrlEncodedContent EncodeAsExplodedForm(object value) => + new(QueryStringConverter.ToExplodedForm(value)); + + /// + /// Encodes an object into a form URL-encoded content using Form notation without exploding parameters. + /// + /// Object to form URL-encode. You can pass in an object or dictionary, but not lists, strings, or primitives. + /// Throws when passing in a list, a string, or a primitive value. + internal static FormUrlEncodedContent EncodeAsForm(object value) => + new(QueryStringConverter.ToForm(value)); +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/HeaderValue.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/HeaderValue.cs new file mode 100644 index 000000000000..ae9c5b7526ed --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/HeaderValue.cs @@ -0,0 +1,41 @@ +using OneOf; + +namespace SeedInferredAuthImplicitApiKey.Core; + +internal sealed class HeaderValue( + OneOf< + string, + Func, + Func>, + Func> + > value +) + : OneOfBase< + string, + Func, + Func>, + Func> + >(value) +{ + public static implicit operator HeaderValue(string value) => new(value); + + public static implicit operator HeaderValue(Func value) => new(value); + + public static implicit operator HeaderValue( + Func> value + ) => new(value); + + public static implicit operator HeaderValue( + Func> value + ) => new(value); + + internal global::System.Threading.Tasks.ValueTask ResolveAsync() + { + return Match( + str => new global::System.Threading.Tasks.ValueTask(str), + syncFunc => new global::System.Threading.Tasks.ValueTask(syncFunc()), + valueTaskFunc => valueTaskFunc(), + taskFunc => new global::System.Threading.Tasks.ValueTask(taskFunc()) + ); + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/Headers.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/Headers.cs new file mode 100644 index 000000000000..5680d1ccd2d1 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/Headers.cs @@ -0,0 +1,28 @@ +namespace SeedInferredAuthImplicitApiKey.Core; + +/// +/// Represents the headers sent with the request. +/// +internal sealed class Headers : Dictionary +{ + internal Headers() { } + + /// + /// Initializes a new instance of the Headers class with the specified value. + /// + /// + internal Headers(Dictionary value) + { + foreach (var kvp in value) + { + this[kvp.Key] = new HeaderValue(kvp.Value); + } + } + + /// + /// Initializes a new instance of the Headers class with the specified value. + /// + /// + internal Headers(IEnumerable> value) + : base(value.ToDictionary(e => e.Key, e => e.Value)) { } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/HttpMethodExtensions.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/HttpMethodExtensions.cs new file mode 100644 index 000000000000..a0ba09dc598b --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/HttpMethodExtensions.cs @@ -0,0 +1,8 @@ +using System.Net.Http; + +namespace SeedInferredAuthImplicitApiKey.Core; + +internal static class HttpMethodExtensions +{ + public static readonly HttpMethod Patch = new("PATCH"); +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/IIsRetryableContent.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/IIsRetryableContent.cs new file mode 100644 index 000000000000..e8eb595e27ff --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/IIsRetryableContent.cs @@ -0,0 +1,6 @@ +namespace SeedInferredAuthImplicitApiKey.Core; + +public interface IIsRetryableContent +{ + public bool IsRetryable { get; } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/IRequestOptions.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/IRequestOptions.cs new file mode 100644 index 000000000000..b99700035cdd --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/IRequestOptions.cs @@ -0,0 +1,88 @@ +namespace SeedInferredAuthImplicitApiKey.Core; + +internal interface IRequestOptions +{ + /// + /// The Base URL for the API. + /// + public string? BaseUrl { get; +#if NET5_0_OR_GREATER + init; +#else + set; +#endif + } + + /// + /// The http client used to make requests. + /// + public HttpClient? HttpClient { get; +#if NET5_0_OR_GREATER + init; +#else + set; +#endif + } + + /// + /// The http headers sent with the request. + /// + internal Headers Headers { get; init; } + + /// + /// Additional headers to be sent with the request. + /// Headers previously set with matching keys will be overwritten. + /// + public IEnumerable> AdditionalHeaders { get; +#if NET5_0_OR_GREATER + init; +#else + set; +#endif + } + + /// + /// The http client used to make requests. + /// + public int? MaxRetries { get; +#if NET5_0_OR_GREATER + init; +#else + set; +#endif + } + + /// + /// The timeout for the request. + /// + public TimeSpan? Timeout { get; +#if NET5_0_OR_GREATER + init; +#else + set; +#endif + } + + /// + /// Additional query parameters sent with the request. + /// + public IEnumerable> AdditionalQueryParameters { get; +#if NET5_0_OR_GREATER + init; +#else + set; +#endif + } + + /// + /// Additional body properties sent with the request. + /// This is only applied to JSON requests. + /// + public object? AdditionalBodyProperties { get; +#if NET5_0_OR_GREATER + init; +#else + set; +#endif + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/InferredAuthTokenProvider.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/InferredAuthTokenProvider.cs new file mode 100644 index 000000000000..96fa16eaa4dc --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/InferredAuthTokenProvider.cs @@ -0,0 +1,60 @@ +using SeedInferredAuthImplicitApiKey; + +namespace SeedInferredAuthImplicitApiKey.Core; + +internal partial class InferredAuthTokenProvider +{ + private const double BufferInMinutes = 2; + + private AuthClient _client; + + private IDictionary? _cachedHeaders; + + private DateTime? _expiresAt; + + private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1); + + private string _apiKey; + + internal InferredAuthTokenProvider(string apiKey, AuthClient client) + { + _apiKey = apiKey; + _client = client; + } + + internal async Task> GetAuthHeadersAsync() + { + if (_cachedHeaders == null || DateTime.UtcNow >= _expiresAt) + { + await _lock.WaitAsync().ConfigureAwait(false); + try + { + if (_cachedHeaders == null || DateTime.UtcNow >= _expiresAt) + { + try + { + var tokenResponse = await _client + .GetTokenAsync(new GetTokenRequest { ApiKey = _apiKey }) + .ConfigureAwait(false); + _cachedHeaders = new Dictionary(); + _cachedHeaders["Authorization"] = $"Bearer {tokenResponse.AccessToken}"; + _expiresAt = DateTime + .UtcNow.AddSeconds(tokenResponse.ExpiresIn) + .AddMinutes(-BufferInMinutes); + } + catch + { + _cachedHeaders = null; + _expiresAt = null; + throw; + } + } + } + finally + { + _lock.Release(); + } + } + return _cachedHeaders; + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/JsonAccessAttribute.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/JsonAccessAttribute.cs new file mode 100644 index 000000000000..5b368983daa3 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/JsonAccessAttribute.cs @@ -0,0 +1,15 @@ +namespace SeedInferredAuthImplicitApiKey.Core; + +[global::System.AttributeUsage( + global::System.AttributeTargets.Property | global::System.AttributeTargets.Field +)] +internal class JsonAccessAttribute(JsonAccessType accessType) : global::System.Attribute +{ + internal JsonAccessType AccessType { get; init; } = accessType; +} + +internal enum JsonAccessType +{ + ReadOnly, + WriteOnly, +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/JsonConfiguration.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/JsonConfiguration.cs new file mode 100644 index 000000000000..8a773147e702 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/JsonConfiguration.cs @@ -0,0 +1,180 @@ +using global::System.Reflection; +using global::System.Text.Json; +using global::System.Text.Json.Nodes; +using global::System.Text.Json.Serialization; +using global::System.Text.Json.Serialization.Metadata; + +namespace SeedInferredAuthImplicitApiKey.Core; + +internal static partial class JsonOptions +{ + internal static readonly JsonSerializerOptions JsonSerializerOptions; + + static JsonOptions() + { + var options = new JsonSerializerOptions + { + Converters = { new DateTimeSerializer(), +#if USE_PORTABLE_DATE_ONLY + new DateOnlyConverter(), +#endif + new OneOfSerializer() }, +#if DEBUG + WriteIndented = true, +#endif + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + TypeInfoResolver = new DefaultJsonTypeInfoResolver + { + Modifiers = + { + static typeInfo => + { + if (typeInfo.Kind != JsonTypeInfoKind.Object) + return; + + foreach (var propertyInfo in typeInfo.Properties) + { + var jsonAccessAttribute = propertyInfo + .AttributeProvider?.GetCustomAttributes( + typeof(JsonAccessAttribute), + true + ) + .OfType() + .FirstOrDefault(); + + if (jsonAccessAttribute != null) + { + propertyInfo.IsRequired = false; + switch (jsonAccessAttribute.AccessType) + { + case JsonAccessType.ReadOnly: + propertyInfo.ShouldSerialize = (_, _) => false; + break; + case JsonAccessType.WriteOnly: + propertyInfo.Set = null; + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + + var jsonIgnoreAttribute = propertyInfo + .AttributeProvider?.GetCustomAttributes( + typeof(JsonIgnoreAttribute), + true + ) + .OfType() + .FirstOrDefault(); + + if (jsonIgnoreAttribute is not null) + { + propertyInfo.IsRequired = false; + } + } + + if ( + typeInfo.Kind == JsonTypeInfoKind.Object + && typeInfo.Properties.All(prop => !prop.IsExtensionData) + ) + { + var extensionProp = typeInfo + .Type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic) + .FirstOrDefault(prop => + prop.GetCustomAttribute() != null + ); + + if (extensionProp is not null) + { + var jsonPropertyInfo = typeInfo.CreateJsonPropertyInfo( + extensionProp.FieldType, + extensionProp.Name + ); + jsonPropertyInfo.Get = extensionProp.GetValue; + jsonPropertyInfo.Set = extensionProp.SetValue; + jsonPropertyInfo.IsExtensionData = true; + typeInfo.Properties.Add(jsonPropertyInfo); + } + } + }, + }, + }, + }; + ConfigureJsonSerializerOptions(options); + JsonSerializerOptions = options; + } + + static partial void ConfigureJsonSerializerOptions(JsonSerializerOptions defaultOptions); +} + +internal static class JsonUtils +{ + internal static string Serialize(T obj) => + JsonSerializer.Serialize(obj, JsonOptions.JsonSerializerOptions); + + internal static JsonElement SerializeToElement(T obj) => + JsonSerializer.SerializeToElement(obj, JsonOptions.JsonSerializerOptions); + + internal static JsonDocument SerializeToDocument(T obj) => + JsonSerializer.SerializeToDocument(obj, JsonOptions.JsonSerializerOptions); + + internal static JsonNode? SerializeToNode(T obj) => + JsonSerializer.SerializeToNode(obj, JsonOptions.JsonSerializerOptions); + + internal static byte[] SerializeToUtf8Bytes(T obj) => + JsonSerializer.SerializeToUtf8Bytes(obj, JsonOptions.JsonSerializerOptions); + + internal static string SerializeWithAdditionalProperties( + T obj, + object? additionalProperties = null + ) + { + if (additionalProperties == null) + { + return Serialize(obj); + } + var additionalPropertiesJsonNode = SerializeToNode(additionalProperties); + if (additionalPropertiesJsonNode is not JsonObject additionalPropertiesJsonObject) + { + throw new InvalidOperationException( + "The additional properties must serialize to a JSON object." + ); + } + var jsonNode = SerializeToNode(obj); + if (jsonNode is not JsonObject jsonObject) + { + throw new InvalidOperationException( + "The serialized object must be a JSON object to add properties." + ); + } + MergeJsonObjects(jsonObject, additionalPropertiesJsonObject); + return jsonObject.ToJsonString(JsonOptions.JsonSerializerOptions); + } + + private static void MergeJsonObjects(JsonObject baseObject, JsonObject overrideObject) + { + foreach (var property in overrideObject) + { + if (!baseObject.TryGetPropertyValue(property.Key, out JsonNode? existingValue)) + { + baseObject[property.Key] = + property.Value != null ? JsonNode.Parse(property.Value.ToJsonString()) : null; + continue; + } + if ( + existingValue is JsonObject nestedBaseObject + && property.Value is JsonObject nestedOverrideObject + ) + { + // If both values are objects, recursively merge them. + MergeJsonObjects(nestedBaseObject, nestedOverrideObject); + continue; + } + // Otherwise, the overrideObject takes precedence. + baseObject[property.Key] = + property.Value != null ? JsonNode.Parse(property.Value.ToJsonString()) : null; + } + } + + internal static T Deserialize(string json) => + JsonSerializer.Deserialize(json, JsonOptions.JsonSerializerOptions)!; +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/JsonRequest.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/JsonRequest.cs new file mode 100644 index 000000000000..fce43a98728b --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/JsonRequest.cs @@ -0,0 +1,36 @@ +using System.Net.Http; + +namespace SeedInferredAuthImplicitApiKey.Core; + +/// +/// The request object to be sent for JSON APIs. +/// +internal record JsonRequest : BaseRequest +{ + internal object? Body { get; init; } + + internal override HttpContent? CreateContent() + { + if (Body is null && Options?.AdditionalBodyProperties is null) + { + return null; + } + + var (encoding, charset, mediaType) = ParseContentTypeOrDefault( + ContentType, + Utf8NoBom, + "application/json" + ); + var content = new StringContent( + JsonUtils.SerializeWithAdditionalProperties(Body, Options?.AdditionalBodyProperties), + encoding, + mediaType + ); + if (string.IsNullOrEmpty(charset) && content.Headers.ContentType is not null) + { + content.Headers.ContentType.CharSet = ""; + } + + return content; + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/MultipartFormRequest.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/MultipartFormRequest.cs new file mode 100644 index 000000000000..3ab9f0252e50 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/MultipartFormRequest.cs @@ -0,0 +1,294 @@ +using System.Net.Http; +using System.Net.Http.Headers; + +namespace SeedInferredAuthImplicitApiKey.Core; + +/// +/// The request object to be sent for multipart form data. +/// +internal record MultipartFormRequest : BaseRequest +{ + private readonly List> _partAdders = []; + + internal void AddJsonPart(string name, object? value) => AddJsonPart(name, value, null); + + internal void AddJsonPart(string name, object? value, string? contentType) + { + if (value is null) + { + return; + } + + _partAdders.Add(form => + { + var (encoding, charset, mediaType) = ParseContentTypeOrDefault( + contentType, + Utf8NoBom, + "application/json" + ); + var content = new StringContent(JsonUtils.Serialize(value), encoding, mediaType); + if (string.IsNullOrEmpty(charset) && content.Headers.ContentType is not null) + { + content.Headers.ContentType.CharSet = ""; + } + + form.Add(content, name); + }); + } + + internal void AddJsonParts(string name, IEnumerable? value) => + AddJsonParts(name, value, null); + + internal void AddJsonParts(string name, IEnumerable? value, string? contentType) + { + if (value is null) + { + return; + } + + foreach (var item in value) + { + AddJsonPart(name, item, contentType); + } + } + + internal void AddJsonParts(string name, IEnumerable? value) => + AddJsonParts(name, value, null); + + internal void AddJsonParts(string name, IEnumerable? value, string? contentType) + { + if (value is null) + { + return; + } + + foreach (var item in value) + { + AddJsonPart(name, item, contentType); + } + } + + internal void AddStringPart(string name, object? value) => AddStringPart(name, value, null); + + internal void AddStringPart(string name, object? value, string? contentType) + { + if (value is null) + { + return; + } + + AddStringPart(name, ValueConvert.ToString(value), contentType); + } + + internal void AddStringPart(string name, string? value) => AddStringPart(name, value, null); + + internal void AddStringPart(string name, string? value, string? contentType) + { + if (value is null) + { + return; + } + + _partAdders.Add(form => + { + var (encoding, charset, mediaType) = ParseContentTypeOrDefault( + contentType, + Utf8NoBom, + "text/plain" + ); + var content = new StringContent(value, encoding, mediaType); + if (string.IsNullOrEmpty(charset) && content.Headers.ContentType is not null) + { + content.Headers.ContentType.CharSet = ""; + } + + form.Add(content, name); + }); + } + + internal void AddStringParts(string name, IEnumerable? value) => + AddStringParts(name, value, null); + + internal void AddStringParts(string name, IEnumerable? value, string? contentType) + { + if (value is null) + { + return; + } + + AddStringPart(name, ValueConvert.ToString(value), contentType); + } + + internal void AddStringParts(string name, IEnumerable? value) => + AddStringParts(name, value, null); + + internal void AddStringParts(string name, IEnumerable? value, string? contentType) + { + if (value is null) + { + return; + } + + foreach (var item in value) + { + AddStringPart(name, item, contentType); + } + } + + internal void AddStreamPart(string name, Stream? stream, string? fileName) => + AddStreamPart(name, stream, fileName, null); + + internal void AddStreamPart(string name, Stream? stream, string? fileName, string? contentType) + { + if (stream is null) + { + return; + } + + _partAdders.Add(form => + { + var content = new StreamContent(stream) + { + Headers = + { + ContentType = MediaTypeHeaderValue.Parse( + contentType ?? "application/octet-stream" + ), + }, + }; + + if (fileName is not null) + { + form.Add(content, name, fileName); + } + else + { + form.Add(content, name); + } + }); + } + + internal void AddFileParameterPart(string name, Stream? stream) => + AddStreamPart(name, stream, null, null); + + internal void AddFileParameterPart(string name, FileParameter? file) => + AddFileParameterPart(name, file, null); + + internal void AddFileParameterPart( + string name, + FileParameter? file, + string? fallbackContentType + ) => + AddStreamPart(name, file?.Stream, file?.FileName, file?.ContentType ?? fallbackContentType); + + internal void AddFileParameterParts(string name, IEnumerable? files) => + AddFileParameterParts(name, files, null); + + internal void AddFileParameterParts( + string name, + IEnumerable? files, + string? fallbackContentType + ) + { + if (files is null) + { + return; + } + + foreach (var file in files) + { + AddFileParameterPart(name, file, fallbackContentType); + } + } + + internal void AddFormEncodedPart(string name, object? value) => + AddFormEncodedPart(name, value, null); + + internal void AddFormEncodedPart(string name, object? value, string? contentType) + { + if (value is null) + { + return; + } + + _partAdders.Add(form => + { + var content = FormUrlEncoder.EncodeAsForm(value); + if (!string.IsNullOrEmpty(contentType)) + { + content.Headers.ContentType = MediaTypeHeaderValue.Parse(contentType); + } + + form.Add(content, name); + }); + } + + internal void AddFormEncodedParts(string name, IEnumerable? value) => + AddFormEncodedParts(name, value, null); + + internal void AddFormEncodedParts(string name, IEnumerable? value, string? contentType) + { + if (value is null) + { + return; + } + + foreach (var item in value) + { + AddFormEncodedPart(name, item, contentType); + } + } + + internal void AddExplodedFormEncodedPart(string name, object? value) => + AddExplodedFormEncodedPart(name, value, null); + + internal void AddExplodedFormEncodedPart(string name, object? value, string? contentType) + { + if (value is null) + { + return; + } + + _partAdders.Add(form => + { + var content = FormUrlEncoder.EncodeAsExplodedForm(value); + if (!string.IsNullOrEmpty(contentType)) + { + content.Headers.ContentType = MediaTypeHeaderValue.Parse(contentType); + } + + form.Add(content, name); + }); + } + + internal void AddExplodedFormEncodedParts(string name, IEnumerable? value) => + AddExplodedFormEncodedParts(name, value, null); + + internal void AddExplodedFormEncodedParts( + string name, + IEnumerable? value, + string? contentType + ) + { + if (value is null) + { + return; + } + + foreach (var item in value) + { + AddExplodedFormEncodedPart(name, item, contentType); + } + } + + internal override HttpContent CreateContent() + { + var form = new MultipartFormDataContent(); + foreach (var adder in _partAdders) + { + adder(form); + } + + return form; + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/OneOfSerializer.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/OneOfSerializer.cs new file mode 100644 index 000000000000..e96243b11e3e --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/OneOfSerializer.cs @@ -0,0 +1,91 @@ +using System.Reflection; +using System.Text.Json; +using System.Text.Json.Serialization; +using OneOf; + +namespace SeedInferredAuthImplicitApiKey.Core; + +internal class OneOfSerializer : JsonConverter +{ + public override IOneOf? Read( + ref Utf8JsonReader reader, + global::System.Type typeToConvert, + JsonSerializerOptions options + ) + { + if (reader.TokenType is JsonTokenType.Null) + return default; + + foreach (var (type, cast) in GetOneOfTypes(typeToConvert)) + { + try + { + var readerCopy = reader; + var result = JsonSerializer.Deserialize(ref readerCopy, type, options); + reader.Skip(); + return (IOneOf)cast.Invoke(null, [result])!; + } + catch (JsonException) { } + } + + throw new JsonException( + $"Cannot deserialize into one of the supported types for {typeToConvert}" + ); + } + + public override void Write(Utf8JsonWriter writer, IOneOf value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.Value, options); + } + + private static (global::System.Type type, MethodInfo cast)[] GetOneOfTypes( + global::System.Type typeToConvert + ) + { + var type = typeToConvert; + if (Nullable.GetUnderlyingType(type) is { } underlyingType) + { + type = underlyingType; + } + + var casts = type.GetRuntimeMethods() + .Where(m => m.IsSpecialName && m.Name == "op_Implicit") + .ToArray(); + while (type != null) + { + if ( + type.IsGenericType + && (type.Name.StartsWith("OneOf`") || type.Name.StartsWith("OneOfBase`")) + ) + { + var genericArguments = type.GetGenericArguments(); + if (genericArguments.Length == 1) + { + return [(genericArguments[0], casts[0])]; + } + + // if object type is present, make sure it is last + var indexOfObjectType = Array.IndexOf(genericArguments, typeof(object)); + if (indexOfObjectType != -1 && genericArguments.Length - 1 != indexOfObjectType) + { + genericArguments = genericArguments + .OrderBy(t => t == typeof(object) ? 1 : 0) + .ToArray(); + } + + return genericArguments + .Select(t => (t, casts.First(c => c.GetParameters()[0].ParameterType == t))) + .ToArray(); + } + + type = type.BaseType; + } + + throw new InvalidOperationException($"{type} isn't OneOf or OneOfBase"); + } + + public override bool CanConvert(global::System.Type typeToConvert) + { + return typeof(IOneOf).IsAssignableFrom(typeToConvert); + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/Public/AdditionalProperties.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/Public/AdditionalProperties.cs new file mode 100644 index 000000000000..af33ded80d3a --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/Public/AdditionalProperties.cs @@ -0,0 +1,353 @@ +using global::System.Collections; +using global::System.Collections.ObjectModel; +using global::System.Text.Json; +using global::System.Text.Json.Nodes; +using SeedInferredAuthImplicitApiKey.Core; + +namespace SeedInferredAuthImplicitApiKey; + +public record ReadOnlyAdditionalProperties : ReadOnlyAdditionalProperties +{ + internal ReadOnlyAdditionalProperties() { } + + internal ReadOnlyAdditionalProperties(IDictionary properties) + : base(properties) { } +} + +public record ReadOnlyAdditionalProperties : IReadOnlyDictionary +{ + private readonly Dictionary _extensionData = new(); + private readonly Dictionary _convertedCache = new(); + + internal ReadOnlyAdditionalProperties() + { + _extensionData = new Dictionary(); + _convertedCache = new Dictionary(); + } + + internal ReadOnlyAdditionalProperties(IDictionary properties) + { + _extensionData = new Dictionary(properties.Count); + _convertedCache = new Dictionary(properties.Count); + foreach (var kvp in properties) + { + if (kvp.Value is JsonElement element) + { + _extensionData.Add(kvp.Key, element); + } + else + { + _extensionData[kvp.Key] = JsonUtils.SerializeToElement(kvp.Value); + } + + _convertedCache[kvp.Key] = kvp.Value; + } + } + + private static T ConvertToT(JsonElement value) + { + if (typeof(T) == typeof(JsonElement)) + { + return (T)(object)value; + } + + return value.Deserialize(JsonOptions.JsonSerializerOptions)!; + } + + internal void CopyFromExtensionData(IDictionary extensionData) + { + _extensionData.Clear(); + _convertedCache.Clear(); + foreach (var kvp in extensionData) + { + _extensionData[kvp.Key] = kvp.Value; + if (kvp.Value is T value) + { + _convertedCache[kvp.Key] = value; + } + } + } + + private T GetCached(string key) + { + if (_convertedCache.TryGetValue(key, out var cached)) + { + return cached; + } + + var value = ConvertToT(_extensionData[key]); + _convertedCache[key] = value; + return value; + } + + public IEnumerator> GetEnumerator() + { + return _extensionData + .Select(kvp => new KeyValuePair(kvp.Key, GetCached(kvp.Key))) + .GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public int Count => _extensionData.Count; + + public bool ContainsKey(string key) => _extensionData.ContainsKey(key); + + public bool TryGetValue(string key, out T value) + { + if (_convertedCache.TryGetValue(key, out value!)) + { + return true; + } + + if (_extensionData.TryGetValue(key, out var element)) + { + value = ConvertToT(element); + _convertedCache[key] = value; + return true; + } + + return false; + } + + public T this[string key] => GetCached(key); + + public IEnumerable Keys => _extensionData.Keys; + + public IEnumerable Values => Keys.Select(GetCached); +} + +public record AdditionalProperties : AdditionalProperties +{ + public AdditionalProperties() { } + + public AdditionalProperties(IDictionary properties) + : base(properties) { } +} + +public record AdditionalProperties : IDictionary +{ + private readonly Dictionary _extensionData; + private readonly Dictionary _convertedCache; + + public AdditionalProperties() + { + _extensionData = new Dictionary(); + _convertedCache = new Dictionary(); + } + + public AdditionalProperties(IDictionary properties) + { + _extensionData = new Dictionary(properties.Count); + _convertedCache = new Dictionary(properties.Count); + foreach (var kvp in properties) + { + _extensionData[kvp.Key] = kvp.Value; + _convertedCache[kvp.Key] = kvp.Value; + } + } + + private static T ConvertToT(object? extensionDataValue) + { + return extensionDataValue switch + { + T value => value, + JsonElement jsonElement => jsonElement.Deserialize( + JsonOptions.JsonSerializerOptions + )!, + JsonNode jsonNode => jsonNode.Deserialize(JsonOptions.JsonSerializerOptions)!, + _ => JsonUtils + .SerializeToElement(extensionDataValue) + .Deserialize(JsonOptions.JsonSerializerOptions)!, + }; + } + + internal void CopyFromExtensionData(IDictionary extensionData) + { + _extensionData.Clear(); + _convertedCache.Clear(); + foreach (var kvp in extensionData) + { + _extensionData[kvp.Key] = kvp.Value; + if (kvp.Value is T value) + { + _convertedCache[kvp.Key] = value; + } + } + } + + internal void CopyToExtensionData(IDictionary extensionData) + { + extensionData.Clear(); + foreach (var kvp in _extensionData) + { + extensionData[kvp.Key] = kvp.Value; + } + } + + public JsonObject ToJsonObject() => + ( + JsonUtils.SerializeToNode(_extensionData) + ?? throw new InvalidOperationException( + "Failed to serialize AdditionalProperties to JSON Node." + ) + ).AsObject(); + + public JsonNode ToJsonNode() => + JsonUtils.SerializeToNode(_extensionData) + ?? throw new InvalidOperationException( + "Failed to serialize AdditionalProperties to JSON Node." + ); + + public JsonElement ToJsonElement() => JsonUtils.SerializeToElement(_extensionData); + + public JsonDocument ToJsonDocument() => JsonUtils.SerializeToDocument(_extensionData); + + public IReadOnlyDictionary ToJsonElementDictionary() + { + return new ReadOnlyDictionary( + _extensionData.ToDictionary( + kvp => kvp.Key, + kvp => + { + if (kvp.Value is JsonElement jsonElement) + { + return jsonElement; + } + + return JsonUtils.SerializeToElement(kvp.Value); + } + ) + ); + } + + public ICollection Keys => _extensionData.Keys; + + public ICollection Values + { + get + { + var values = new T[_extensionData.Count]; + var i = 0; + foreach (var key in Keys) + { + values[i++] = GetCached(key); + } + + return values; + } + } + + private T GetCached(string key) + { + if (_convertedCache.TryGetValue(key, out var value)) + { + return value; + } + + value = ConvertToT(_extensionData[key]); + _convertedCache.Add(key, value); + return value; + } + + private void SetCached(string key, T value) + { + _extensionData[key] = value; + _convertedCache[key] = value; + } + + private void AddCached(string key, T value) + { + _extensionData.Add(key, value); + _convertedCache.Add(key, value); + } + + private bool RemoveCached(string key) + { + var isRemoved = _extensionData.Remove(key); + _convertedCache.Remove(key); + return isRemoved; + } + + public int Count => _extensionData.Count; + public bool IsReadOnly => false; + + public T this[string key] + { + get => GetCached(key); + set => SetCached(key, value); + } + + public void Add(string key, T value) => AddCached(key, value); + + public void Add(KeyValuePair item) => AddCached(item.Key, item.Value); + + public bool Remove(string key) => RemoveCached(key); + + public bool Remove(KeyValuePair item) => RemoveCached(item.Key); + + public bool ContainsKey(string key) => _extensionData.ContainsKey(key); + + public bool Contains(KeyValuePair item) + { + return _extensionData.ContainsKey(item.Key) + && EqualityComparer.Default.Equals(GetCached(item.Key), item.Value); + } + + public bool TryGetValue(string key, out T value) + { + if (_convertedCache.TryGetValue(key, out value!)) + { + return true; + } + + if (_extensionData.TryGetValue(key, out var extensionDataValue)) + { + value = ConvertToT(extensionDataValue); + _convertedCache[key] = value; + return true; + } + + return false; + } + + public void Clear() + { + _extensionData.Clear(); + _convertedCache.Clear(); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + if (array is null) + { + throw new ArgumentNullException(nameof(array)); + } + + if (arrayIndex < 0 || arrayIndex > array.Length) + { + throw new ArgumentOutOfRangeException(nameof(arrayIndex)); + } + + if (array.Length - arrayIndex < _extensionData.Count) + { + throw new ArgumentException( + "The array does not have enough space to copy the elements." + ); + } + + foreach (var kvp in _extensionData) + { + array[arrayIndex++] = new KeyValuePair(kvp.Key, GetCached(kvp.Key)); + } + } + + public IEnumerator> GetEnumerator() + { + return _extensionData + .Select(kvp => new KeyValuePair(kvp.Key, GetCached(kvp.Key))) + .GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/Public/ClientOptions.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/Public/ClientOptions.cs new file mode 100644 index 000000000000..9c4d8f99e50b --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/Public/ClientOptions.cs @@ -0,0 +1,83 @@ +using SeedInferredAuthImplicitApiKey.Core; + +namespace SeedInferredAuthImplicitApiKey; + +[Serializable] +public partial class ClientOptions +{ + /// + /// The http headers sent with the request. + /// + internal Headers Headers { get; init; } = new(); + + /// + /// The Base URL for the API. + /// + public string BaseUrl { get; +#if NET5_0_OR_GREATER + init; +#else + set; +#endif + } = ""; + + /// + /// The http client used to make requests. + /// + public HttpClient HttpClient { get; +#if NET5_0_OR_GREATER + init; +#else + set; +#endif + } = new HttpClient(); + + /// + /// Additional headers to be sent with HTTP requests. + /// Headers with matching keys will be overwritten by headers set on the request. + /// + public IEnumerable> AdditionalHeaders { get; +#if NET5_0_OR_GREATER + init; +#else + set; +#endif + } = []; + + /// + /// The http client used to make requests. + /// + public int MaxRetries { get; +#if NET5_0_OR_GREATER + init; +#else + set; +#endif + } = 2; + + /// + /// The timeout for the request. + /// + public TimeSpan Timeout { get; +#if NET5_0_OR_GREATER + init; +#else + set; +#endif + } = TimeSpan.FromSeconds(30); + + /// + /// Clones this and returns a new instance + /// + internal ClientOptions Clone() + { + return new ClientOptions + { + BaseUrl = BaseUrl, + HttpClient = HttpClient, + MaxRetries = MaxRetries, + Timeout = Timeout, + Headers = new Headers(new Dictionary(Headers)), + }; + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/Public/FileParameter.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/Public/FileParameter.cs new file mode 100644 index 000000000000..5bceeb9e95a6 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/Public/FileParameter.cs @@ -0,0 +1,63 @@ +namespace SeedInferredAuthImplicitApiKey; + +/// +/// File parameter for uploading files. +/// +public record FileParameter : IDisposable +#if NET6_0_OR_GREATER + , IAsyncDisposable +#endif +{ + private bool _disposed; + + /// + /// The name of the file to be uploaded. + /// + public string? FileName { get; set; } + + /// + /// The content type of the file to be uploaded. + /// + public string? ContentType { get; set; } + + /// + /// The content of the file to be uploaded. + /// + public required Stream Stream { get; set; } + + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + protected virtual void Dispose(bool disposing) + { + if (_disposed) + return; + if (disposing) + { + Stream.Dispose(); + } + + _disposed = true; + } + +#if NET6_0_OR_GREATER + /// + public async ValueTask DisposeAsync() + { + if (!_disposed) + { + await Stream.DisposeAsync().ConfigureAwait(false); + _disposed = true; + } + + GC.SuppressFinalize(this); + } +#endif + + public static implicit operator FileParameter(Stream stream) => new() { Stream = stream }; +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/Public/RequestOptions.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/Public/RequestOptions.cs new file mode 100644 index 000000000000..09ec58928206 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/Public/RequestOptions.cs @@ -0,0 +1,91 @@ +using SeedInferredAuthImplicitApiKey.Core; + +namespace SeedInferredAuthImplicitApiKey; + +[Serializable] +public partial class RequestOptions : IRequestOptions +{ + /// + /// The http headers sent with the request. + /// + Headers IRequestOptions.Headers { get; init; } = new(); + + /// + /// The Base URL for the API. + /// + public string? BaseUrl { get; +#if NET5_0_OR_GREATER + init; +#else + set; +#endif + } + + /// + /// The http client used to make requests. + /// + public HttpClient? HttpClient { get; +#if NET5_0_OR_GREATER + init; +#else + set; +#endif + } + + /// + /// Additional headers to be sent with the request. + /// Headers previously set with matching keys will be overwritten. + /// + public IEnumerable> AdditionalHeaders { get; +#if NET5_0_OR_GREATER + init; +#else + set; +#endif + } = []; + + /// + /// The http client used to make requests. + /// + public int? MaxRetries { get; +#if NET5_0_OR_GREATER + init; +#else + set; +#endif + } + + /// + /// The timeout for the request. + /// + public TimeSpan? Timeout { get; +#if NET5_0_OR_GREATER + init; +#else + set; +#endif + } + + /// + /// Additional query parameters sent with the request. + /// + public IEnumerable> AdditionalQueryParameters { get; +#if NET5_0_OR_GREATER + init; +#else + set; +#endif + } = Enumerable.Empty>(); + + /// + /// Additional body properties sent with the request. + /// This is only applied to JSON requests. + /// + public object? AdditionalBodyProperties { get; +#if NET5_0_OR_GREATER + init; +#else + set; +#endif + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/Public/SeedInferredAuthImplicitApiKeyApiException.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/Public/SeedInferredAuthImplicitApiKeyApiException.cs new file mode 100644 index 000000000000..41f52a142f07 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/Public/SeedInferredAuthImplicitApiKeyApiException.cs @@ -0,0 +1,18 @@ +namespace SeedInferredAuthImplicitApiKey; + +/// +/// This exception type will be thrown for any non-2XX API responses. +/// +public class SeedInferredAuthImplicitApiKeyApiException(string message, int statusCode, object body) + : SeedInferredAuthImplicitApiKeyException(message) +{ + /// + /// The error code of the response that triggered the exception. + /// + public int StatusCode => statusCode; + + /// + /// The body of the response that triggered the exception. + /// + public object Body => body; +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/Public/SeedInferredAuthImplicitApiKeyException.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/Public/SeedInferredAuthImplicitApiKeyException.cs new file mode 100644 index 000000000000..c8ab644636c0 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/Public/SeedInferredAuthImplicitApiKeyException.cs @@ -0,0 +1,9 @@ +namespace SeedInferredAuthImplicitApiKey; + +/// +/// Base exception class for all exceptions thrown by the SDK. +/// +public class SeedInferredAuthImplicitApiKeyException( + string message, + Exception? innerException = null +) : Exception(message, innerException); diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/Public/Version.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/Public/Version.cs new file mode 100644 index 000000000000..67937e4e7c97 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/Public/Version.cs @@ -0,0 +1,7 @@ +namespace SeedInferredAuthImplicitApiKey; + +[Serializable] +internal class Version +{ + public const string Current = "0.0.1"; +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/QueryStringConverter.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/QueryStringConverter.cs new file mode 100644 index 000000000000..8d0bf20b881a --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/QueryStringConverter.cs @@ -0,0 +1,229 @@ +using global::System.Text.Json; + +namespace SeedInferredAuthImplicitApiKey.Core; + +/// +/// Converts an object into a query string collection. +/// +internal static class QueryStringConverter +{ + /// + /// Converts an object into a query string collection using Deep Object notation. + /// + /// Object to form URL-encode. You can pass in an object or dictionary, but not lists, strings, or primitives. + /// Throws when passing in a list, a string, or a primitive value. + /// A collection of key value pairs. The keys and values are not URL encoded. + internal static IEnumerable> ToDeepObject(object value) + { + var queryCollection = new List>(); + var json = JsonUtils.SerializeToElement(value); + AssertRootJson(json); + JsonToDeepObject(json, "", queryCollection); + return queryCollection; + } + + /// + /// Converts an object into a query string collection using Exploded Form notation. + /// + /// Object to form URL-encode. You can pass in an object or dictionary, but not lists, strings, or primitives. + /// Throws when passing in a list, a string, or a primitive value. + /// A collection of key value pairs. The keys and values are not URL encoded. + internal static IEnumerable> ToExplodedForm(object value) + { + var queryCollection = new List>(); + var json = JsonUtils.SerializeToElement(value); + AssertRootJson(json); + JsonToFormExploded(json, "", queryCollection); + return queryCollection; + } + + /// + /// Converts an object into a query string collection using Form notation without exploding parameters. + /// + /// Object to form URL-encode. You can pass in an object or dictionary, but not lists, strings, or primitives. + /// Throws when passing in a list, a string, or a primitive value. + /// A collection of key value pairs. The keys and values are not URL encoded. + internal static IEnumerable> ToForm(object value) + { + var queryCollection = new List>(); + var json = JsonUtils.SerializeToElement(value); + AssertRootJson(json); + JsonToForm(json, "", queryCollection); + return queryCollection; + } + + private static void AssertRootJson(JsonElement json) + { + switch (json.ValueKind) + { + case JsonValueKind.Object: + break; + case JsonValueKind.Array: + case JsonValueKind.Undefined: + case JsonValueKind.String: + case JsonValueKind.Number: + case JsonValueKind.True: + case JsonValueKind.False: + case JsonValueKind.Null: + default: + throw new global::System.Exception( + $"Only objects can be converted to query string collections. Given type is {json.ValueKind}." + ); + } + } + + private static void JsonToForm( + JsonElement element, + string prefix, + List> parameters + ) + { + switch (element.ValueKind) + { + case JsonValueKind.Object: + foreach (var property in element.EnumerateObject()) + { + var newPrefix = string.IsNullOrEmpty(prefix) + ? property.Name + : $"{prefix}[{property.Name}]"; + + JsonToForm(property.Value, newPrefix, parameters); + } + break; + case JsonValueKind.Array: + var arrayValues = element.EnumerateArray().Select(ValueToString).ToArray(); + parameters.Add( + new KeyValuePair(prefix, string.Join(",", arrayValues)) + ); + break; + case JsonValueKind.Null: + break; + case JsonValueKind.Undefined: + case JsonValueKind.String: + case JsonValueKind.Number: + case JsonValueKind.True: + case JsonValueKind.False: + default: + parameters.Add(new KeyValuePair(prefix, ValueToString(element))); + break; + } + } + + private static void JsonToFormExploded( + JsonElement element, + string prefix, + List> parameters + ) + { + switch (element.ValueKind) + { + case JsonValueKind.Object: + foreach (var property in element.EnumerateObject()) + { + var newPrefix = string.IsNullOrEmpty(prefix) + ? property.Name + : $"{prefix}[{property.Name}]"; + + JsonToFormExploded(property.Value, newPrefix, parameters); + } + + break; + case JsonValueKind.Array: + foreach (var item in element.EnumerateArray()) + { + if ( + item.ValueKind != JsonValueKind.Object + && item.ValueKind != JsonValueKind.Array + ) + { + parameters.Add( + new KeyValuePair(prefix, ValueToString(item)) + ); + } + else + { + JsonToFormExploded(item, prefix, parameters); + } + } + + break; + case JsonValueKind.Null: + break; + case JsonValueKind.Undefined: + case JsonValueKind.String: + case JsonValueKind.Number: + case JsonValueKind.True: + case JsonValueKind.False: + default: + parameters.Add(new KeyValuePair(prefix, ValueToString(element))); + break; + } + } + + private static void JsonToDeepObject( + JsonElement element, + string prefix, + List> parameters + ) + { + switch (element.ValueKind) + { + case JsonValueKind.Object: + foreach (var property in element.EnumerateObject()) + { + var newPrefix = string.IsNullOrEmpty(prefix) + ? property.Name + : $"{prefix}[{property.Name}]"; + + JsonToDeepObject(property.Value, newPrefix, parameters); + } + + break; + case JsonValueKind.Array: + var index = 0; + foreach (var item in element.EnumerateArray()) + { + var newPrefix = $"{prefix}[{index++}]"; + + if ( + item.ValueKind != JsonValueKind.Object + && item.ValueKind != JsonValueKind.Array + ) + { + parameters.Add( + new KeyValuePair(newPrefix, ValueToString(item)) + ); + } + else + { + JsonToDeepObject(item, newPrefix, parameters); + } + } + + break; + case JsonValueKind.Null: + break; + case JsonValueKind.Undefined: + case JsonValueKind.String: + case JsonValueKind.Number: + case JsonValueKind.True: + case JsonValueKind.False: + default: + parameters.Add(new KeyValuePair(prefix, ValueToString(element))); + break; + } + } + + private static string ValueToString(JsonElement element) + { + return element.ValueKind switch + { + JsonValueKind.String => element.GetString() ?? "", + JsonValueKind.Number => element.GetRawText(), + JsonValueKind.True => "true", + JsonValueKind.False => "false", + JsonValueKind.Null => "", + _ => element.GetRawText(), + }; + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/RawClient.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/RawClient.cs new file mode 100644 index 000000000000..6ee8ba86c0c0 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/RawClient.cs @@ -0,0 +1,508 @@ +using global::System.Net.Http; +using global::System.Net.Http.Headers; +using global::System.Text; +using SystemTask = global::System.Threading.Tasks.Task; + +namespace SeedInferredAuthImplicitApiKey.Core; + +/// +/// Utility class for making raw HTTP requests to the API. +/// +internal partial class RawClient(ClientOptions clientOptions) +{ + private const int MaxRetryDelayMs = 60000; + private const double JitterFactor = 0.2; +#if NET6_0_OR_GREATER + // Use Random.Shared for thread-safe random number generation on .NET 6+ +#else + private static readonly object JitterLock = new(); + private static readonly Random JitterRandom = new(); +#endif + internal int BaseRetryDelay { get; set; } = 1000; + + /// + /// The client options applied on every request. + /// + internal readonly ClientOptions Options = clientOptions; + + [Obsolete("Use SendRequestAsync instead.")] + internal global::System.Threading.Tasks.Task MakeRequestAsync( + global::SeedInferredAuthImplicitApiKey.Core.BaseRequest request, + CancellationToken cancellationToken = default + ) + { + return SendRequestAsync(request, cancellationToken); + } + + internal async global::System.Threading.Tasks.Task SendRequestAsync( + global::SeedInferredAuthImplicitApiKey.Core.BaseRequest request, + CancellationToken cancellationToken = default + ) + { + // Apply the request timeout. + using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + var timeout = request.Options?.Timeout ?? Options.Timeout; + cts.CancelAfter(timeout); + + var httpRequest = await CreateHttpRequestAsync(request).ConfigureAwait(false); + // Send the request. + return await SendWithRetriesAsync(httpRequest, request.Options, cts.Token) + .ConfigureAwait(false); + } + + internal async global::System.Threading.Tasks.Task SendRequestAsync( + HttpRequestMessage request, + IRequestOptions? options, + CancellationToken cancellationToken = default + ) + { + // Apply the request timeout. + using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + var timeout = options?.Timeout ?? Options.Timeout; + cts.CancelAfter(timeout); + + // Send the request. + return await SendWithRetriesAsync(request, options, cts.Token).ConfigureAwait(false); + } + + private static async global::System.Threading.Tasks.Task CloneRequestAsync( + HttpRequestMessage request + ) + { + var clonedRequest = new HttpRequestMessage(request.Method, request.RequestUri); + clonedRequest.Version = request.Version; + switch (request.Content) + { + case MultipartContent oldMultipartFormContent: + var originalBoundary = + oldMultipartFormContent + .Headers.ContentType?.Parameters.First(p => + p.Name.Equals("boundary", StringComparison.OrdinalIgnoreCase) + ) + .Value?.Trim('"') ?? Guid.NewGuid().ToString(); + var newMultipartContent = oldMultipartFormContent switch + { + MultipartFormDataContent => new MultipartFormDataContent(originalBoundary), + _ => new MultipartContent(), + }; + foreach (var content in oldMultipartFormContent) + { + var ms = new MemoryStream(); + await content.CopyToAsync(ms).ConfigureAwait(false); + ms.Position = 0; + var newPart = new StreamContent(ms); + foreach (var header in oldMultipartFormContent.Headers) + { + newPart.Headers.TryAddWithoutValidation(header.Key, header.Value); + } + + newMultipartContent.Add(newPart); + } + + clonedRequest.Content = newMultipartContent; + break; + default: + clonedRequest.Content = request.Content; + break; + } + + foreach (var header in request.Headers) + { + clonedRequest.Headers.TryAddWithoutValidation(header.Key, header.Value); + } + + return clonedRequest; + } + + /// + /// Sends the request with retries, unless the request content is not retryable, + /// such as stream requests and multipart form data with stream content. + /// + private async global::System.Threading.Tasks.Task SendWithRetriesAsync( + HttpRequestMessage request, + IRequestOptions? options, + CancellationToken cancellationToken + ) + { + var httpClient = options?.HttpClient ?? Options.HttpClient; + var maxRetries = options?.MaxRetries ?? Options.MaxRetries; + var response = await httpClient + .SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken) + .ConfigureAwait(false); + var isRetryableContent = IsRetryableContent(request); + + if (!isRetryableContent) + { + return new global::SeedInferredAuthImplicitApiKey.Core.ApiResponse + { + StatusCode = (int)response.StatusCode, + Raw = response, + }; + } + + for (var i = 0; i < maxRetries; i++) + { + if (!ShouldRetry(response)) + { + break; + } + + var delayMs = GetRetryDelayFromHeaders(response, i); + await SystemTask.Delay(delayMs, cancellationToken).ConfigureAwait(false); + using var retryRequest = await CloneRequestAsync(request).ConfigureAwait(false); + response = await httpClient + .SendAsync( + retryRequest, + HttpCompletionOption.ResponseHeadersRead, + cancellationToken + ) + .ConfigureAwait(false); + } + + return new global::SeedInferredAuthImplicitApiKey.Core.ApiResponse + { + StatusCode = (int)response.StatusCode, + Raw = response, + }; + } + + private static bool ShouldRetry(HttpResponseMessage response) + { + var statusCode = (int)response.StatusCode; + return statusCode is 408 or 429 or >= 500; + } + + private static int AddPositiveJitter(int delayMs) + { +#if NET6_0_OR_GREATER + var random = Random.Shared.NextDouble(); +#else + double random; + lock (JitterLock) + { + random = JitterRandom.NextDouble(); + } +#endif + var jitterMultiplier = 1 + random * JitterFactor; + return (int)(delayMs * jitterMultiplier); + } + + private static int AddSymmetricJitter(int delayMs) + { +#if NET6_0_OR_GREATER + var random = Random.Shared.NextDouble(); +#else + double random; + lock (JitterLock) + { + random = JitterRandom.NextDouble(); + } +#endif + var jitterMultiplier = 1 + (random - 0.5) * JitterFactor; + return (int)(delayMs * jitterMultiplier); + } + + private int GetRetryDelayFromHeaders(HttpResponseMessage response, int retryAttempt) + { + if (response.Headers.TryGetValues("Retry-After", out var retryAfterValues)) + { + var retryAfter = retryAfterValues.FirstOrDefault(); + if (!string.IsNullOrEmpty(retryAfter)) + { + if (int.TryParse(retryAfter, out var retryAfterSeconds) && retryAfterSeconds > 0) + { + return Math.Min(retryAfterSeconds * 1000, MaxRetryDelayMs); + } + + if (DateTimeOffset.TryParse(retryAfter, out var retryAfterDate)) + { + var delay = (int)(retryAfterDate - DateTimeOffset.UtcNow).TotalMilliseconds; + if (delay > 0) + { + return Math.Min(delay, MaxRetryDelayMs); + } + } + } + } + + if (response.Headers.TryGetValues("X-RateLimit-Reset", out var rateLimitResetValues)) + { + var rateLimitReset = rateLimitResetValues.FirstOrDefault(); + if ( + !string.IsNullOrEmpty(rateLimitReset) + && long.TryParse(rateLimitReset, out var resetTime) + ) + { + var resetDateTime = DateTimeOffset.FromUnixTimeSeconds(resetTime); + var delay = (int)(resetDateTime - DateTimeOffset.UtcNow).TotalMilliseconds; + if (delay > 0) + { + return AddPositiveJitter(Math.Min(delay, MaxRetryDelayMs)); + } + } + } + + var exponentialDelay = Math.Min(BaseRetryDelay * (1 << retryAttempt), MaxRetryDelayMs); + return AddSymmetricJitter(exponentialDelay); + } + + private static bool IsRetryableContent(HttpRequestMessage request) + { + return request.Content switch + { + IIsRetryableContent c => c.IsRetryable, + StreamContent => false, + MultipartContent content => !content.Any(c => c is StreamContent), + _ => true, + }; + } + + internal async global::System.Threading.Tasks.Task CreateHttpRequestAsync( + global::SeedInferredAuthImplicitApiKey.Core.BaseRequest request + ) + { + var url = BuildUrl(request); + var httpRequest = new HttpRequestMessage(request.Method, url); + httpRequest.Content = request.CreateContent(); + var mergedHeaders = new Dictionary>(); + await MergeHeadersAsync(mergedHeaders, Options.Headers).ConfigureAwait(false); + MergeAdditionalHeaders(mergedHeaders, Options.AdditionalHeaders); + await MergeHeadersAsync(mergedHeaders, request.Headers).ConfigureAwait(false); + await MergeHeadersAsync(mergedHeaders, request.Options?.Headers).ConfigureAwait(false); + + MergeAdditionalHeaders(mergedHeaders, request.Options?.AdditionalHeaders ?? []); + SetHeaders(httpRequest, mergedHeaders); + return httpRequest; + } + + private static string BuildUrl(global::SeedInferredAuthImplicitApiKey.Core.BaseRequest request) + { + var baseUrl = request.Options?.BaseUrl ?? request.BaseUrl; + var trimmedBaseUrl = baseUrl.TrimEnd('/'); + var trimmedBasePath = request.Path.TrimStart('/'); + var url = $"{trimmedBaseUrl}/{trimmedBasePath}"; + + var queryParameters = GetQueryParameters(request); + if (!queryParameters.Any()) + return url; + + url += "?"; + url = queryParameters.Aggregate( + url, + (current, queryItem) => + { + if ( + queryItem.Value + is global::System.Collections.IEnumerable collection + and not string + ) + { + var items = collection + .Cast() + .Select(value => + $"{Uri.EscapeDataString(queryItem.Key)}={Uri.EscapeDataString(value?.ToString() ?? "")}" + ) + .ToList(); + if (items.Any()) + { + current += string.Join("&", items) + "&"; + } + } + else + { + current += + $"{Uri.EscapeDataString(queryItem.Key)}={Uri.EscapeDataString(queryItem.Value)}&"; + } + + return current; + } + ); + url = url[..^1]; + return url; + } + + private static List> GetQueryParameters( + global::SeedInferredAuthImplicitApiKey.Core.BaseRequest request + ) + { + var result = TransformToKeyValuePairs(request.Query); + if ( + request.Options?.AdditionalQueryParameters is null + || !request.Options.AdditionalQueryParameters.Any() + ) + { + return result; + } + + var additionalKeys = request + .Options.AdditionalQueryParameters.Select(p => p.Key) + .Distinct(); + foreach (var key in additionalKeys) + { + result.RemoveAll(kv => kv.Key == key); + } + + result.AddRange(request.Options.AdditionalQueryParameters); + return result; + } + + private static List> TransformToKeyValuePairs( + Dictionary inputDict + ) + { + var result = new List>(); + foreach (var kvp in inputDict) + { + switch (kvp.Value) + { + case string str: + result.Add(new KeyValuePair(kvp.Key, str)); + break; + case IEnumerable strList: + { + foreach (var value in strList) + { + result.Add(new KeyValuePair(kvp.Key, value)); + } + + break; + } + } + } + + return result; + } + + private static async SystemTask MergeHeadersAsync( + Dictionary> mergedHeaders, + Headers? headers + ) + { + if (headers is null) + { + return; + } + + foreach (var header in headers) + { + var value = await header.Value.ResolveAsync().ConfigureAwait(false); + if (value is not null) + { + mergedHeaders[header.Key] = [value]; + } + } + } + + private static void MergeAdditionalHeaders( + Dictionary> mergedHeaders, + IEnumerable>? headers + ) + { + if (headers is null) + { + return; + } + + var usedKeys = new HashSet(); + foreach (var header in headers) + { + if (header.Value is null) + { + mergedHeaders.Remove(header.Key); + usedKeys.Remove(header.Key); + continue; + } + + if (usedKeys.Contains(header.Key)) + { + mergedHeaders[header.Key].Add(header.Value); + } + else + { + mergedHeaders[header.Key] = [header.Value]; + usedKeys.Add(header.Key); + } + } + } + + private void SetHeaders( + HttpRequestMessage httpRequest, + Dictionary> mergedHeaders + ) + { + foreach (var kv in mergedHeaders) + { + foreach (var header in kv.Value) + { + if (header is null) + { + continue; + } + + httpRequest.Headers.TryAddWithoutValidation(kv.Key, header); + } + } + } + + private static (Encoding encoding, string? charset, string mediaType) ParseContentTypeOrDefault( + string? contentType, + Encoding encodingFallback, + string mediaTypeFallback + ) + { + var encoding = encodingFallback; + var mediaType = mediaTypeFallback; + string? charset = null; + if (string.IsNullOrEmpty(contentType)) + { + return (encoding, charset, mediaType); + } + + if (!MediaTypeHeaderValue.TryParse(contentType, out var mediaTypeHeaderValue)) + { + return (encoding, charset, mediaType); + } + + if (!string.IsNullOrEmpty(mediaTypeHeaderValue.CharSet)) + { + charset = mediaTypeHeaderValue.CharSet; + encoding = Encoding.GetEncoding(mediaTypeHeaderValue.CharSet); + } + + if (!string.IsNullOrEmpty(mediaTypeHeaderValue.MediaType)) + { + mediaType = mediaTypeHeaderValue.MediaType; + } + + return (encoding, charset, mediaType); + } + + /// + [Obsolete("Use global::SeedInferredAuthImplicitApiKey.Core.ApiResponse instead.")] + internal record ApiResponse : global::SeedInferredAuthImplicitApiKey.Core.ApiResponse; + + /// + [Obsolete("Use global::SeedInferredAuthImplicitApiKey.Core.BaseRequest instead.")] + internal abstract record BaseApiRequest + : global::SeedInferredAuthImplicitApiKey.Core.BaseRequest; + + /// + [Obsolete("Use global::SeedInferredAuthImplicitApiKey.Core.EmptyRequest instead.")] + internal abstract record EmptyApiRequest + : global::SeedInferredAuthImplicitApiKey.Core.EmptyRequest; + + /// + [Obsolete("Use global::SeedInferredAuthImplicitApiKey.Core.JsonRequest instead.")] + internal abstract record JsonApiRequest + : global::SeedInferredAuthImplicitApiKey.Core.JsonRequest; + + /// + [Obsolete("Use global::SeedInferredAuthImplicitApiKey.Core.MultipartFormRequest instead.")] + internal abstract record MultipartFormRequest + : global::SeedInferredAuthImplicitApiKey.Core.MultipartFormRequest; + + /// + [Obsolete("Use global::SeedInferredAuthImplicitApiKey.Core.StreamRequest instead.")] + internal abstract record StreamApiRequest + : global::SeedInferredAuthImplicitApiKey.Core.StreamRequest; +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/StreamRequest.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/StreamRequest.cs new file mode 100644 index 000000000000..f6f3979d646f --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/StreamRequest.cs @@ -0,0 +1,29 @@ +using System.Net.Http; +using System.Net.Http.Headers; + +namespace SeedInferredAuthImplicitApiKey.Core; + +/// +/// The request object to be sent for streaming uploads. +/// +internal record StreamRequest : BaseRequest +{ + internal Stream? Body { get; init; } + + internal override HttpContent? CreateContent() + { + if (Body is null) + { + return null; + } + + var content = new StreamContent(Body) + { + Headers = + { + ContentType = MediaTypeHeaderValue.Parse(ContentType ?? "application/octet-stream"), + }, + }; + return content; + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/StringEnum.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/StringEnum.cs new file mode 100644 index 000000000000..5aedd1ab0281 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/StringEnum.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace SeedInferredAuthImplicitApiKey.Core; + +public interface IStringEnum : IEquatable +{ + public string Value { get; } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/StringEnumExtensions.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/StringEnumExtensions.cs new file mode 100644 index 000000000000..761c43c6a797 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/StringEnumExtensions.cs @@ -0,0 +1,6 @@ +namespace SeedInferredAuthImplicitApiKey.Core; + +internal static class StringEnumExtensions +{ + public static string Stringify(this IStringEnum stringEnum) => stringEnum.Value; +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/StringEnumSerializer.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/StringEnumSerializer.cs new file mode 100644 index 000000000000..87e14d75a623 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/StringEnumSerializer.cs @@ -0,0 +1,25 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace SeedInferredAuthImplicitApiKey.Core; + +internal class StringEnumSerializer : JsonConverter + where T : IStringEnum +{ + public override T? Read( + ref Utf8JsonReader reader, + global::System.Type typeToConvert, + JsonSerializerOptions options + ) + { + var stringValue = + reader.GetString() + ?? throw new global::System.Exception("The JSON value could not be read as a string."); + return (T?)Activator.CreateInstance(typeToConvert, stringValue); + } + + public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.Value); + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/ValueConvert.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/ValueConvert.cs new file mode 100644 index 000000000000..d00dab911e54 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Core/ValueConvert.cs @@ -0,0 +1,115 @@ +using global::System.Globalization; + +namespace SeedInferredAuthImplicitApiKey.Core; + +/// +/// Convert values to string for path and query parameters. +/// +public static class ValueConvert +{ + internal static string ToPathParameterString(T value) => ToString(value); + + internal static string ToPathParameterString(bool v) => ToString(v); + + internal static string ToPathParameterString(int v) => ToString(v); + + internal static string ToPathParameterString(long v) => ToString(v); + + internal static string ToPathParameterString(float v) => ToString(v); + + internal static string ToPathParameterString(double v) => ToString(v); + + internal static string ToPathParameterString(decimal v) => ToString(v); + + internal static string ToPathParameterString(short v) => ToString(v); + + internal static string ToPathParameterString(ushort v) => ToString(v); + + internal static string ToPathParameterString(uint v) => ToString(v); + + internal static string ToPathParameterString(ulong v) => ToString(v); + + internal static string ToPathParameterString(string v) => ToString(v); + + internal static string ToPathParameterString(char v) => ToString(v); + + internal static string ToPathParameterString(Guid v) => ToString(v); + + internal static string ToQueryStringValue(T value) => value is null ? "" : ToString(value); + + internal static string ToQueryStringValue(bool v) => ToString(v); + + internal static string ToQueryStringValue(int v) => ToString(v); + + internal static string ToQueryStringValue(long v) => ToString(v); + + internal static string ToQueryStringValue(float v) => ToString(v); + + internal static string ToQueryStringValue(double v) => ToString(v); + + internal static string ToQueryStringValue(decimal v) => ToString(v); + + internal static string ToQueryStringValue(short v) => ToString(v); + + internal static string ToQueryStringValue(ushort v) => ToString(v); + + internal static string ToQueryStringValue(uint v) => ToString(v); + + internal static string ToQueryStringValue(ulong v) => ToString(v); + + internal static string ToQueryStringValue(string v) => v is null ? "" : v; + + internal static string ToQueryStringValue(char v) => ToString(v); + + internal static string ToQueryStringValue(Guid v) => ToString(v); + + internal static string ToString(T value) + { + return value switch + { + null => "null", + string str => str, + true => "true", + false => "false", + int i => ToString(i), + long l => ToString(l), + float f => ToString(f), + double d => ToString(d), + decimal dec => ToString(dec), + short s => ToString(s), + ushort u => ToString(u), + uint u => ToString(u), + ulong u => ToString(u), + char c => ToString(c), + Guid guid => ToString(guid), + Enum e => JsonUtils.Serialize(e).Trim('"'), + _ => JsonUtils.Serialize(value).Trim('"'), + }; + } + + internal static string ToString(bool v) => v ? "true" : "false"; + + internal static string ToString(int v) => v.ToString(CultureInfo.InvariantCulture); + + internal static string ToString(long v) => v.ToString(CultureInfo.InvariantCulture); + + internal static string ToString(float v) => v.ToString(CultureInfo.InvariantCulture); + + internal static string ToString(double v) => v.ToString(CultureInfo.InvariantCulture); + + internal static string ToString(decimal v) => v.ToString(CultureInfo.InvariantCulture); + + internal static string ToString(short v) => v.ToString(CultureInfo.InvariantCulture); + + internal static string ToString(ushort v) => v.ToString(CultureInfo.InvariantCulture); + + internal static string ToString(uint v) => v.ToString(CultureInfo.InvariantCulture); + + internal static string ToString(ulong v) => v.ToString(CultureInfo.InvariantCulture); + + internal static string ToString(char v) => v.ToString(CultureInfo.InvariantCulture); + + internal static string ToString(string v) => v; + + internal static string ToString(Guid v) => v.ToString("D"); +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Nested/Api/ApiClient.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Nested/Api/ApiClient.cs new file mode 100644 index 000000000000..ea92e974b87c --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Nested/Api/ApiClient.cs @@ -0,0 +1,48 @@ +using SeedInferredAuthImplicitApiKey; +using SeedInferredAuthImplicitApiKey.Core; + +namespace SeedInferredAuthImplicitApiKey.Nested; + +public partial class ApiClient +{ + private RawClient _client; + + internal ApiClient(RawClient client) + { + _client = client; + } + + /// + /// await client.Nested.Api.GetSomethingAsync(); + /// + public async Task GetSomethingAsync( + RequestOptions? options = null, + CancellationToken cancellationToken = default + ) + { + var response = await _client + .SendRequestAsync( + new JsonRequest + { + BaseUrl = _client.Options.BaseUrl, + Method = HttpMethod.Get, + Path = "/nested/get-something", + Options = options, + }, + cancellationToken + ) + .ConfigureAwait(false); + if (response.StatusCode is >= 200 and < 400) + { + return; + } + { + var responseBody = await response.Raw.Content.ReadAsStringAsync(); + throw new SeedInferredAuthImplicitApiKeyApiException( + $"Error with status code {response.StatusCode}", + response.StatusCode, + responseBody + ); + } + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Nested/NestedClient.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Nested/NestedClient.cs new file mode 100644 index 000000000000..8831dc18098b --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Nested/NestedClient.cs @@ -0,0 +1,16 @@ +using SeedInferredAuthImplicitApiKey.Core; + +namespace SeedInferredAuthImplicitApiKey.Nested; + +public partial class NestedClient +{ + private RawClient _client; + + internal NestedClient(RawClient client) + { + _client = client; + Api = new ApiClient(_client); + } + + public ApiClient Api { get; } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/NestedNoAuth/Api/ApiClient.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/NestedNoAuth/Api/ApiClient.cs new file mode 100644 index 000000000000..a80f881e0334 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/NestedNoAuth/Api/ApiClient.cs @@ -0,0 +1,48 @@ +using SeedInferredAuthImplicitApiKey; +using SeedInferredAuthImplicitApiKey.Core; + +namespace SeedInferredAuthImplicitApiKey.NestedNoAuth; + +public partial class ApiClient +{ + private RawClient _client; + + internal ApiClient(RawClient client) + { + _client = client; + } + + /// + /// await client.NestedNoAuth.Api.GetSomethingAsync(); + /// + public async Task GetSomethingAsync( + RequestOptions? options = null, + CancellationToken cancellationToken = default + ) + { + var response = await _client + .SendRequestAsync( + new JsonRequest + { + BaseUrl = _client.Options.BaseUrl, + Method = HttpMethod.Get, + Path = "/nested-no-auth/get-something", + Options = options, + }, + cancellationToken + ) + .ConfigureAwait(false); + if (response.StatusCode is >= 200 and < 400) + { + return; + } + { + var responseBody = await response.Raw.Content.ReadAsStringAsync(); + throw new SeedInferredAuthImplicitApiKeyApiException( + $"Error with status code {response.StatusCode}", + response.StatusCode, + responseBody + ); + } + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/NestedNoAuth/NestedNoAuthClient.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/NestedNoAuth/NestedNoAuthClient.cs new file mode 100644 index 000000000000..dede40b50c2a --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/NestedNoAuth/NestedNoAuthClient.cs @@ -0,0 +1,16 @@ +using SeedInferredAuthImplicitApiKey.Core; + +namespace SeedInferredAuthImplicitApiKey.NestedNoAuth; + +public partial class NestedNoAuthClient +{ + private RawClient _client; + + internal NestedNoAuthClient(RawClient client) + { + _client = client; + Api = new ApiClient(_client); + } + + public ApiClient Api { get; } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/SeedInferredAuthImplicitApiKey.Custom.props b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/SeedInferredAuthImplicitApiKey.Custom.props new file mode 100644 index 000000000000..17a84cada530 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/SeedInferredAuthImplicitApiKey.Custom.props @@ -0,0 +1,20 @@ + + + + diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/SeedInferredAuthImplicitApiKey.csproj b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/SeedInferredAuthImplicitApiKey.csproj new file mode 100644 index 000000000000..0a96b5dc6c42 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/SeedInferredAuthImplicitApiKey.csproj @@ -0,0 +1,61 @@ + + + net462;net8.0;net7.0;net6.0;netstandard2.0 + enable + 12 + enable + 0.0.1 + $(Version) + $(Version) + README.md + https://github.com/inferred-auth-implicit-api-key/fern + true + + + + false + + + $(DefineConstants);USE_PORTABLE_DATE_ONLY + true + + + + + + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + + + + + <_Parameter1>SeedInferredAuthImplicitApiKey.Test + + + + + diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/SeedInferredAuthImplicitApiKeyClient.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/SeedInferredAuthImplicitApiKeyClient.cs new file mode 100644 index 000000000000..a92bb399eea8 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/SeedInferredAuthImplicitApiKeyClient.cs @@ -0,0 +1,54 @@ +using SeedInferredAuthImplicitApiKey.Core; +using SeedInferredAuthImplicitApiKey.Nested; +using SeedInferredAuthImplicitApiKey.NestedNoAuth; + +namespace SeedInferredAuthImplicitApiKey; + +public partial class SeedInferredAuthImplicitApiKeyClient +{ + private readonly RawClient _client; + + public SeedInferredAuthImplicitApiKeyClient(string apiKey, ClientOptions? clientOptions = null) + { + var defaultHeaders = new Headers( + new Dictionary() + { + { "X-Fern-Language", "C#" }, + { "X-Fern-SDK-Name", "SeedInferredAuthImplicitApiKey" }, + { "X-Fern-SDK-Version", Version.Current }, + { "User-Agent", "Ferninferred-auth-implicit-api-key/0.0.1" }, + } + ); + clientOptions ??= new ClientOptions(); + foreach (var header in defaultHeaders) + { + if (!clientOptions.Headers.ContainsKey(header.Key)) + { + clientOptions.Headers[header.Key] = header.Value; + } + } + var inferredAuthProvider = new InferredAuthTokenProvider( + apiKey, + new AuthClient(new RawClient(clientOptions.Clone())) + ); + clientOptions.Headers["Authorization"] = + new Func>(async () => + (await inferredAuthProvider.GetAuthHeadersAsync().ConfigureAwait(false)) + .First() + .Value + ); + _client = new RawClient(clientOptions); + Auth = new AuthClient(_client); + NestedNoAuth = new NestedNoAuthClient(_client); + Nested = new NestedClient(_client); + Simple = new SimpleClient(_client); + } + + public AuthClient Auth { get; } + + public NestedNoAuthClient NestedNoAuth { get; } + + public NestedClient Nested { get; } + + public SimpleClient Simple { get; } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Simple/SimpleClient.cs b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Simple/SimpleClient.cs new file mode 100644 index 000000000000..7b968276879a --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-api-key/src/SeedInferredAuthImplicitApiKey/Simple/SimpleClient.cs @@ -0,0 +1,47 @@ +using SeedInferredAuthImplicitApiKey.Core; + +namespace SeedInferredAuthImplicitApiKey; + +public partial class SimpleClient +{ + private RawClient _client; + + internal SimpleClient(RawClient client) + { + _client = client; + } + + /// + /// await client.Simple.GetSomethingAsync(); + /// + public async Task GetSomethingAsync( + RequestOptions? options = null, + CancellationToken cancellationToken = default + ) + { + var response = await _client + .SendRequestAsync( + new JsonRequest + { + BaseUrl = _client.Options.BaseUrl, + Method = HttpMethod.Get, + Path = "/get-something", + Options = options, + }, + cancellationToken + ) + .ConfigureAwait(false); + if (response.StatusCode is >= 200 and < 400) + { + return; + } + { + var responseBody = await response.Raw.Content.ReadAsStringAsync(); + throw new SeedInferredAuthImplicitApiKeyApiException( + $"Error with status code {response.StatusCode}", + response.StatusCode, + responseBody + ); + } + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-no-expiry/README.md b/seed/csharp-sdk/inferred-auth-implicit-no-expiry/README.md index 1f7cc2e70023..6d2511f00a29 100644 --- a/seed/csharp-sdk/inferred-auth-implicit-no-expiry/README.md +++ b/seed/csharp-sdk/inferred-auth-implicit-no-expiry/README.md @@ -38,7 +38,7 @@ Instantiate and use the client with the following: ```csharp using SeedInferredAuthImplicitNoExpiry; -var client = new SeedInferredAuthImplicitNoExpiryClient(); +var client = new SeedInferredAuthImplicitNoExpiryClient("X-Api-Key", "client_id", "client_secret", "scope"); await client.Auth.GetTokenWithClientCredentialsAsync( new GetTokenRequest { diff --git a/seed/csharp-sdk/inferred-auth-implicit-no-expiry/snippet.json b/seed/csharp-sdk/inferred-auth-implicit-no-expiry/snippet.json index 9f849f6a938a..00c62c342925 100644 --- a/seed/csharp-sdk/inferred-auth-implicit-no-expiry/snippet.json +++ b/seed/csharp-sdk/inferred-auth-implicit-no-expiry/snippet.json @@ -10,7 +10,7 @@ }, "snippet": { "type": "csharp", - "client": "using SeedInferredAuthImplicitNoExpiry;\n\nvar client = new SeedInferredAuthImplicitNoExpiryClient();\nawait client.Auth.GetTokenWithClientCredentialsAsync(\n new GetTokenRequest\n {\n XApiKey = \"X-Api-Key\",\n ClientId = \"client_id\",\n ClientSecret = \"client_secret\",\n Audience = \"https://api.example.com\",\n GrantType = \"client_credentials\",\n Scope = \"scope\",\n }\n);\n" + "client": "using SeedInferredAuthImplicitNoExpiry;\n\nvar client = new SeedInferredAuthImplicitNoExpiryClient(\"X-Api-Key\", \"client_id\", \"client_secret\", \"scope\");\nawait client.Auth.GetTokenWithClientCredentialsAsync(\n new GetTokenRequest\n {\n XApiKey = \"X-Api-Key\",\n ClientId = \"client_id\",\n ClientSecret = \"client_secret\",\n Audience = \"https://api.example.com\",\n GrantType = \"client_credentials\",\n Scope = \"scope\",\n }\n);\n" } }, { @@ -22,7 +22,7 @@ }, "snippet": { "type": "csharp", - "client": "using SeedInferredAuthImplicitNoExpiry;\n\nvar client = new SeedInferredAuthImplicitNoExpiryClient();\nawait client.Auth.RefreshTokenAsync(\n new RefreshTokenRequest\n {\n XApiKey = \"X-Api-Key\",\n ClientId = \"client_id\",\n ClientSecret = \"client_secret\",\n RefreshToken = \"refresh_token\",\n Audience = \"https://api.example.com\",\n GrantType = \"refresh_token\",\n Scope = \"scope\",\n }\n);\n" + "client": "using SeedInferredAuthImplicitNoExpiry;\n\nvar client = new SeedInferredAuthImplicitNoExpiryClient(\"X-Api-Key\", \"client_id\", \"client_secret\", \"scope\");\nawait client.Auth.RefreshTokenAsync(\n new RefreshTokenRequest\n {\n XApiKey = \"X-Api-Key\",\n ClientId = \"client_id\",\n ClientSecret = \"client_secret\",\n RefreshToken = \"refresh_token\",\n Audience = \"https://api.example.com\",\n GrantType = \"refresh_token\",\n Scope = \"scope\",\n }\n);\n" } }, { @@ -34,7 +34,7 @@ }, "snippet": { "type": "csharp", - "client": "using SeedInferredAuthImplicitNoExpiry;\n\nvar client = new SeedInferredAuthImplicitNoExpiryClient();\nawait client.NestedNoAuth.Api.GetSomethingAsync();\n" + "client": "using SeedInferredAuthImplicitNoExpiry;\n\nvar client = new SeedInferredAuthImplicitNoExpiryClient(\"X-Api-Key\", \"client_id\", \"client_secret\", \"scope\");\nawait client.NestedNoAuth.Api.GetSomethingAsync();\n" } }, { @@ -46,7 +46,7 @@ }, "snippet": { "type": "csharp", - "client": "using SeedInferredAuthImplicitNoExpiry;\n\nvar client = new SeedInferredAuthImplicitNoExpiryClient();\nawait client.Nested.Api.GetSomethingAsync();\n" + "client": "using SeedInferredAuthImplicitNoExpiry;\n\nvar client = new SeedInferredAuthImplicitNoExpiryClient(\"X-Api-Key\", \"client_id\", \"client_secret\", \"scope\");\nawait client.Nested.Api.GetSomethingAsync();\n" } }, { @@ -58,7 +58,7 @@ }, "snippet": { "type": "csharp", - "client": "using SeedInferredAuthImplicitNoExpiry;\n\nvar client = new SeedInferredAuthImplicitNoExpiryClient();\nawait client.Simple.GetSomethingAsync();\n" + "client": "using SeedInferredAuthImplicitNoExpiry;\n\nvar client = new SeedInferredAuthImplicitNoExpiryClient(\"X-Api-Key\", \"client_id\", \"client_secret\", \"scope\");\nawait client.Simple.GetSomethingAsync();\n" } } ] diff --git a/seed/csharp-sdk/inferred-auth-implicit-no-expiry/src/SeedApi.DynamicSnippets/Example0.cs b/seed/csharp-sdk/inferred-auth-implicit-no-expiry/src/SeedApi.DynamicSnippets/Example0.cs index 07f6ecc85611..7260a3c9e0dd 100644 --- a/seed/csharp-sdk/inferred-auth-implicit-no-expiry/src/SeedApi.DynamicSnippets/Example0.cs +++ b/seed/csharp-sdk/inferred-auth-implicit-no-expiry/src/SeedApi.DynamicSnippets/Example0.cs @@ -6,6 +6,10 @@ public class Example0 { public async Task Do() { var client = new SeedInferredAuthImplicitNoExpiryClient( + xApiKey: "X-Api-Key", + clientId: "client_id", + clientSecret: "client_secret", + scope: "scope", clientOptions: new ClientOptions { BaseUrl = "https://api.fern.com" } diff --git a/seed/csharp-sdk/inferred-auth-implicit-no-expiry/src/SeedApi.DynamicSnippets/Example1.cs b/seed/csharp-sdk/inferred-auth-implicit-no-expiry/src/SeedApi.DynamicSnippets/Example1.cs index 781ba2120b2f..82b5eee7ebf2 100644 --- a/seed/csharp-sdk/inferred-auth-implicit-no-expiry/src/SeedApi.DynamicSnippets/Example1.cs +++ b/seed/csharp-sdk/inferred-auth-implicit-no-expiry/src/SeedApi.DynamicSnippets/Example1.cs @@ -6,6 +6,10 @@ public class Example1 { public async Task Do() { var client = new SeedInferredAuthImplicitNoExpiryClient( + xApiKey: "X-Api-Key", + clientId: "client_id", + clientSecret: "client_secret", + scope: "scope", clientOptions: new ClientOptions { BaseUrl = "https://api.fern.com" } diff --git a/seed/csharp-sdk/inferred-auth-implicit-no-expiry/src/SeedApi.DynamicSnippets/Example2.cs b/seed/csharp-sdk/inferred-auth-implicit-no-expiry/src/SeedApi.DynamicSnippets/Example2.cs index 2b5865c01011..407b466917be 100644 --- a/seed/csharp-sdk/inferred-auth-implicit-no-expiry/src/SeedApi.DynamicSnippets/Example2.cs +++ b/seed/csharp-sdk/inferred-auth-implicit-no-expiry/src/SeedApi.DynamicSnippets/Example2.cs @@ -6,6 +6,10 @@ public class Example2 { public async Task Do() { var client = new SeedInferredAuthImplicitNoExpiryClient( + xApiKey: "X-Api-Key", + clientId: "client_id", + clientSecret: "client_secret", + scope: "scope", clientOptions: new ClientOptions { BaseUrl = "https://api.fern.com" } diff --git a/seed/csharp-sdk/inferred-auth-implicit-no-expiry/src/SeedApi.DynamicSnippets/Example3.cs b/seed/csharp-sdk/inferred-auth-implicit-no-expiry/src/SeedApi.DynamicSnippets/Example3.cs index bb213dd46410..5ff4e6a08dc0 100644 --- a/seed/csharp-sdk/inferred-auth-implicit-no-expiry/src/SeedApi.DynamicSnippets/Example3.cs +++ b/seed/csharp-sdk/inferred-auth-implicit-no-expiry/src/SeedApi.DynamicSnippets/Example3.cs @@ -6,6 +6,10 @@ public class Example3 { public async Task Do() { var client = new SeedInferredAuthImplicitNoExpiryClient( + xApiKey: "X-Api-Key", + clientId: "client_id", + clientSecret: "client_secret", + scope: "scope", clientOptions: new ClientOptions { BaseUrl = "https://api.fern.com" } diff --git a/seed/csharp-sdk/inferred-auth-implicit-no-expiry/src/SeedApi.DynamicSnippets/Example4.cs b/seed/csharp-sdk/inferred-auth-implicit-no-expiry/src/SeedApi.DynamicSnippets/Example4.cs index baac6a523198..575b4b347000 100644 --- a/seed/csharp-sdk/inferred-auth-implicit-no-expiry/src/SeedApi.DynamicSnippets/Example4.cs +++ b/seed/csharp-sdk/inferred-auth-implicit-no-expiry/src/SeedApi.DynamicSnippets/Example4.cs @@ -6,6 +6,10 @@ public class Example4 { public async Task Do() { var client = new SeedInferredAuthImplicitNoExpiryClient( + xApiKey: "X-Api-Key", + clientId: "client_id", + clientSecret: "client_secret", + scope: "scope", clientOptions: new ClientOptions { BaseUrl = "https://api.fern.com" } diff --git a/seed/csharp-sdk/inferred-auth-implicit-no-expiry/src/SeedInferredAuthImplicitNoExpiry.Test/Unit/MockServer/BaseMockServerTest.cs b/seed/csharp-sdk/inferred-auth-implicit-no-expiry/src/SeedInferredAuthImplicitNoExpiry.Test/Unit/MockServer/BaseMockServerTest.cs index 84c7625dc8a8..3574110bc63b 100644 --- a/seed/csharp-sdk/inferred-auth-implicit-no-expiry/src/SeedInferredAuthImplicitNoExpiry.Test/Unit/MockServer/BaseMockServerTest.cs +++ b/seed/csharp-sdk/inferred-auth-implicit-no-expiry/src/SeedInferredAuthImplicitNoExpiry.Test/Unit/MockServer/BaseMockServerTest.cs @@ -15,6 +15,42 @@ public class BaseMockServerTest protected static RequestOptions RequestOptions { get; set; } = new(); + private void MockInferredAuthEndpoint() + { + const string requestJson = """ + { + "client_id": "client_id", + "client_secret": "client_secret", + "audience": "https://api.example.com", + "grant_type": "client_credentials", + "scope": "scope" + } + """; + + const string mockResponse = """ + { + "access_token": "access_token", + "refresh_token": "refresh_token" + } + """; + + Server + .Given( + WireMock + .RequestBuilders.Request.Create() + .WithPath("/token") + .WithHeader("X-Api-Key", "X-Api-Key") + .UsingPost() + .WithBodyAsJson(requestJson) + ) + .RespondWith( + WireMock + .ResponseBuilders.Response.Create() + .WithStatusCode(200) + .WithBody(mockResponse) + ); + } + [OneTimeSetUp] public void GlobalSetup() { @@ -25,8 +61,13 @@ public void GlobalSetup() // Initialize the Client Client = new SeedInferredAuthImplicitNoExpiryClient( + "X-Api-Key", + "client_id", + "client_secret", + "scope", clientOptions: new ClientOptions { BaseUrl = Server.Urls[0], MaxRetries = 0 } ); + MockInferredAuthEndpoint(); } [OneTimeTearDown] diff --git a/seed/csharp-sdk/inferred-auth-implicit-no-expiry/src/SeedInferredAuthImplicitNoExpiry/Core/InferredAuthTokenProvider.cs b/seed/csharp-sdk/inferred-auth-implicit-no-expiry/src/SeedInferredAuthImplicitNoExpiry/Core/InferredAuthTokenProvider.cs new file mode 100644 index 000000000000..0e7f4e53fdf8 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-no-expiry/src/SeedInferredAuthImplicitNoExpiry/Core/InferredAuthTokenProvider.cs @@ -0,0 +1,49 @@ +using SeedInferredAuthImplicitNoExpiry; + +namespace SeedInferredAuthImplicitNoExpiry.Core; + +internal partial class InferredAuthTokenProvider +{ + private AuthClient _client; + + private string _xApiKey; + + private string _clientId; + + private string _clientSecret; + + private string? _scope; + + internal InferredAuthTokenProvider( + string xApiKey, + string clientId, + string clientSecret, + string? scope, + AuthClient client + ) + { + _xApiKey = xApiKey; + _clientId = clientId; + _clientSecret = clientSecret; + _scope = scope; + _client = client; + } + + internal async Task> GetAuthHeadersAsync() + { + var tokenResponse = await _client + .GetTokenWithClientCredentialsAsync( + new GetTokenRequest + { + XApiKey = _xApiKey, + ClientId = _clientId, + ClientSecret = _clientSecret, + Scope = _scope, + } + ) + .ConfigureAwait(false); + var headers = new Dictionary(); + headers["Authorization"] = $"Bearer {tokenResponse.AccessToken}"; + return headers; + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-no-expiry/src/SeedInferredAuthImplicitNoExpiry/SeedInferredAuthImplicitNoExpiryClient.cs b/seed/csharp-sdk/inferred-auth-implicit-no-expiry/src/SeedInferredAuthImplicitNoExpiry/SeedInferredAuthImplicitNoExpiryClient.cs index b32a5d83df93..56bc26e13e9c 100644 --- a/seed/csharp-sdk/inferred-auth-implicit-no-expiry/src/SeedInferredAuthImplicitNoExpiry/SeedInferredAuthImplicitNoExpiryClient.cs +++ b/seed/csharp-sdk/inferred-auth-implicit-no-expiry/src/SeedInferredAuthImplicitNoExpiry/SeedInferredAuthImplicitNoExpiryClient.cs @@ -8,7 +8,13 @@ public partial class SeedInferredAuthImplicitNoExpiryClient { private readonly RawClient _client; - public SeedInferredAuthImplicitNoExpiryClient(ClientOptions? clientOptions = null) + public SeedInferredAuthImplicitNoExpiryClient( + string xApiKey, + string clientId, + string clientSecret, + string? scope = null, + ClientOptions? clientOptions = null + ) { var defaultHeaders = new Headers( new Dictionary() @@ -27,6 +33,19 @@ public SeedInferredAuthImplicitNoExpiryClient(ClientOptions? clientOptions = nul clientOptions.Headers[header.Key] = header.Value; } } + var inferredAuthProvider = new InferredAuthTokenProvider( + xApiKey, + clientId, + clientSecret, + scope, + new AuthClient(new RawClient(clientOptions.Clone())) + ); + clientOptions.Headers["Authorization"] = + new Func>(async () => + (await inferredAuthProvider.GetAuthHeadersAsync().ConfigureAwait(false)) + .First() + .Value + ); _client = new RawClient(clientOptions); Auth = new AuthClient(_client); NestedNoAuth = new NestedNoAuthClient(_client); diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/.editorconfig b/seed/csharp-sdk/inferred-auth-implicit-reference/.editorconfig new file mode 100644 index 000000000000..1e7a0adbac80 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/.editorconfig @@ -0,0 +1,35 @@ +root = true + +[*.cs] +resharper_arrange_object_creation_when_type_evident_highlighting = hint +resharper_auto_property_can_be_made_get_only_global_highlighting = hint +resharper_check_namespace_highlighting = hint +resharper_class_never_instantiated_global_highlighting = hint +resharper_class_never_instantiated_local_highlighting = hint +resharper_collection_never_updated_global_highlighting = hint +resharper_convert_type_check_pattern_to_null_check_highlighting = hint +resharper_inconsistent_naming_highlighting = hint +resharper_member_can_be_private_global_highlighting = hint +resharper_member_hides_static_from_outer_class_highlighting = hint +resharper_not_accessed_field_local_highlighting = hint +resharper_nullable_warning_suppression_is_used_highlighting = suggestion +resharper_partial_type_with_single_part_highlighting = hint +resharper_prefer_concrete_value_over_default_highlighting = none +resharper_private_field_can_be_converted_to_local_variable_highlighting = hint +resharper_property_can_be_made_init_only_global_highlighting = hint +resharper_property_can_be_made_init_only_local_highlighting = hint +resharper_redundant_name_qualifier_highlighting = none +resharper_redundant_using_directive_highlighting = hint +resharper_replace_slice_with_range_indexer_highlighting = none +resharper_unused_auto_property_accessor_global_highlighting = hint +resharper_unused_auto_property_accessor_local_highlighting = hint +resharper_unused_member_global_highlighting = hint +resharper_unused_type_global_highlighting = hint +resharper_use_string_interpolation_highlighting = hint +dotnet_diagnostic.CS1591.severity = suggestion + +[src/**/Types/*.cs] +resharper_check_namespace_highlighting = none + +[src/**/Core/Public/*.cs] +resharper_check_namespace_highlighting = none \ No newline at end of file diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/.fern/metadata.json b/seed/csharp-sdk/inferred-auth-implicit-reference/.fern/metadata.json new file mode 100644 index 000000000000..c48ce7a5a66a --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/.fern/metadata.json @@ -0,0 +1,6 @@ +{ + "cliVersion": "DUMMY", + "generatorName": "fernapi/fern-csharp-sdk", + "generatorVersion": "latest", + "generatorConfig": {} +} \ No newline at end of file diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/.github/workflows/ci.yml b/seed/csharp-sdk/inferred-auth-implicit-reference/.github/workflows/ci.yml new file mode 100644 index 000000000000..7b850e54db3c --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/.github/workflows/ci.yml @@ -0,0 +1,97 @@ +name: ci + +on: [push] + +env: + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true + DOTNET_NOLOGO: true + +jobs: + compile: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v5 + + - uses: actions/checkout@master + + - name: Setup .NET + uses: actions/setup-dotnet@v5 + with: + dotnet-version: 10.x + + - name: Print .NET info + run: dotnet --info + + - name: Install tools + run: dotnet tool restore + + - name: Restore dependencies + run: dotnet restore src/SeedInferredAuthImplicit/SeedInferredAuthImplicit.csproj + + - name: Build Release + run: dotnet build src/SeedInferredAuthImplicit/SeedInferredAuthImplicit.csproj -c Release --no-restore + + unit-tests: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v5 + + - name: Setup .NET + uses: actions/setup-dotnet@v5 + with: + dotnet-version: 10.x + + - name: Print .NET info + run: dotnet --info + + - name: Install tools + run: | + dotnet tool restore + + - name: Restore dependencies + run: dotnet restore src/SeedInferredAuthImplicit.Test/SeedInferredAuthImplicit.Test.csproj + + - name: Build Release + run: dotnet build src/SeedInferredAuthImplicit.Test/SeedInferredAuthImplicit.Test.csproj -c Release --no-restore + + - name: Run Tests + run: dotnet test src/SeedInferredAuthImplicit.Test/SeedInferredAuthImplicit.Test.csproj -c Release --no-build --no-restore + + + publish: + needs: [compile] + if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v5 + + - name: Setup .NET + uses: actions/setup-dotnet@v5 + with: + dotnet-version: 10.x + + - name: Print .NET info + run: dotnet --info + + - name: Install tools + run: dotnet tool restore + + - name: Restore dependencies + run: dotnet restore src/SeedInferredAuthImplicit/SeedInferredAuthImplicit.csproj + + - name: Build Release + run: dotnet build src/SeedInferredAuthImplicit/SeedInferredAuthImplicit.csproj -c Release --no-restore + + - name: Publish + env: + NUGET_API_KEY: ${{ secrets.NUGET_API_TOKEN }} + run: | + dotnet pack src/SeedInferredAuthImplicit/SeedInferredAuthImplicit.csproj -c Release --no-build --no-restore + dotnet nuget push src/SeedInferredAuthImplicit/bin/Release/*.nupkg --api-key $NUGET_API_KEY --source "nuget.org" + diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/.gitignore b/seed/csharp-sdk/inferred-auth-implicit-reference/.gitignore new file mode 100644 index 000000000000..11014f2b33d7 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/.gitignore @@ -0,0 +1,484 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +## This is based on `dotnet new gitignore` and customized by Fern + +# dotenv files +.env + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +# [Rr]elease/ (Ignored by Fern) +# [Rr]eleases/ (Ignored by Fern) +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +# [Ll]og/ (Ignored by Fern) +# [Ll]ogs/ (Ignored by Fern) + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET +project.lock.json +project.fragment.lock.json +artifacts/ + +# Tye +.tye/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml +.idea + +## +## Visual studio for Mac +## + + +# globs +Makefile.in +*.userprefs +*.usertasks +config.make +config.status +aclocal.m4 +install-sh +autom4te.cache/ +*.tar.gz +tarballs/ +test-results/ + +# Mac bundle stuff +*.dmg +*.app + +# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# Vim temporary swap files +*.swp diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/README.md b/seed/csharp-sdk/inferred-auth-implicit-reference/README.md new file mode 100644 index 000000000000..239b7924539b --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/README.md @@ -0,0 +1,116 @@ +# Seed C# Library + +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FC%23) +[![nuget shield](https://img.shields.io/nuget/v/SeedInferredAuthImplicit)](https://nuget.org/packages/SeedInferredAuthImplicit) + +The Seed C# library provides convenient access to the Seed APIs from C#. + +## Table of Contents + +- [Requirements](#requirements) +- [Installation](#installation) +- [Reference](#reference) +- [Usage](#usage) +- [Exception Handling](#exception-handling) +- [Advanced](#advanced) + - [Retries](#retries) + - [Timeouts](#timeouts) +- [Contributing](#contributing) + +## Requirements + +This SDK requires: + +## Installation + +```sh +dotnet add package SeedInferredAuthImplicit +``` + +## Reference + +A full reference for this library is available [here](./reference.md). + +## Usage + +Instantiate and use the client with the following: + +```csharp +using SeedInferredAuthImplicit; + +var client = new SeedInferredAuthImplicitClient("client_id", "client_secret", "scope"); +await client.Auth.GetTokenWithClientCredentialsAsync( + new GetTokenRequest + { + ClientId = "client_id", + ClientSecret = "client_secret", + Audience = "https://api.example.com", + GrantType = "client_credentials", + Scope = "scope", + } +); +``` + +## Exception Handling + +When the API returns a non-success status code (4xx or 5xx response), a subclass of the following error +will be thrown. + +```csharp +using SeedInferredAuthImplicit; + +try { + var response = await client.Auth.GetTokenWithClientCredentialsAsync(...); +} catch (SeedInferredAuthImplicitApiException e) { + System.Console.WriteLine(e.Body); + System.Console.WriteLine(e.StatusCode); +} +``` + +## Advanced + +### Retries + +The SDK is instrumented with automatic retries with exponential backoff. A request will be retried as long +as the request is deemed retryable and the number of retry attempts has not grown larger than the configured +retry limit (default: 2). + +A request is deemed retryable when any of the following HTTP status codes is returned: + +- [408](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/408) (Timeout) +- [429](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429) (Too Many Requests) +- [5XX](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500) (Internal Server Errors) + +Use the `MaxRetries` request option to configure this behavior. + +```csharp +var response = await client.Auth.GetTokenWithClientCredentialsAsync( + ..., + new RequestOptions { + MaxRetries: 0 // Override MaxRetries at the request level + } +); +``` + +### Timeouts + +The SDK defaults to a 30 second timeout. Use the `Timeout` option to configure this behavior. + +```csharp +var response = await client.Auth.GetTokenWithClientCredentialsAsync( + ..., + new RequestOptions { + Timeout: TimeSpan.FromSeconds(3) // Override timeout to 3s + } +); +``` + +## Contributing + +While we value open-source contributions to this SDK, this library is generated programmatically. +Additions made directly to this library would have to be moved over to our generation code, +otherwise they would be overwritten upon the next generated release. Feel free to open a PR as +a proof of concept, but know that we will not be able to merge it as-is. We suggest opening +an issue first to discuss with us! + +On the other hand, contributions to the README are always very welcome! \ No newline at end of file diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/SeedInferredAuthImplicit.slnx b/seed/csharp-sdk/inferred-auth-implicit-reference/SeedInferredAuthImplicit.slnx new file mode 100644 index 000000000000..a036be008daa --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/SeedInferredAuthImplicit.slnx @@ -0,0 +1,4 @@ + + + + diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/reference.md b/seed/csharp-sdk/inferred-auth-implicit-reference/reference.md new file mode 100644 index 000000000000..b35abc85e74e --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/reference.md @@ -0,0 +1,178 @@ +# Reference +## Auth +
client.Auth.GetTokenWithClientCredentialsAsync(GetTokenRequest { ... }) -> TokenResponse +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```csharp +await client.Auth.GetTokenWithClientCredentialsAsync( + new GetTokenRequest + { + ClientId = "client_id", + ClientSecret = "client_secret", + Audience = "https://api.example.com", + GrantType = "client_credentials", + Scope = "scope", + } +); +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `GetTokenRequest` + +
+
+
+
+ + +
+
+
+ +
client.Auth.RefreshTokenAsync(RefreshTokenRequest { ... }) -> TokenResponse +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```csharp +await client.Auth.RefreshTokenAsync( + new RefreshTokenRequest + { + ClientId = "client_id", + ClientSecret = "client_secret", + RefreshToken = "refresh_token", + Audience = "https://api.example.com", + GrantType = "refresh_token", + Scope = "scope", + } +); +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `RefreshTokenRequest` + +
+
+
+
+ + +
+
+
+ +## NestedNoAuth Api +
client.NestedNoAuth.Api.GetSomethingAsync() +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```csharp +await client.NestedNoAuth.Api.GetSomethingAsync(); +``` +
+
+
+
+ + +
+
+
+ +## Nested Api +
client.Nested.Api.GetSomethingAsync() +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```csharp +await client.Nested.Api.GetSomethingAsync(); +``` +
+
+
+
+ + +
+
+
+ +## Simple +
client.Simple.GetSomethingAsync() +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```csharp +await client.Simple.GetSomethingAsync(); +``` +
+
+
+
+ + +
+
+
diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/snippet.json b/seed/csharp-sdk/inferred-auth-implicit-reference/snippet.json new file mode 100644 index 000000000000..2c2641174dad --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/snippet.json @@ -0,0 +1,65 @@ +{ + "types": {}, + "endpoints": [ + { + "example_identifier": null, + "id": { + "path": "/token", + "method": "POST", + "identifier_override": "endpoint_auth.getTokenWithClientCredentials" + }, + "snippet": { + "type": "csharp", + "client": "using SeedInferredAuthImplicit;\n\nvar client = new SeedInferredAuthImplicitClient(\"client_id\", \"client_secret\", \"scope\");\nawait client.Auth.GetTokenWithClientCredentialsAsync(\n new GetTokenRequest\n {\n ClientId = \"client_id\",\n ClientSecret = \"client_secret\",\n Audience = \"https://api.example.com\",\n GrantType = \"client_credentials\",\n Scope = \"scope\",\n }\n);\n" + } + }, + { + "example_identifier": null, + "id": { + "path": "/token/refresh", + "method": "POST", + "identifier_override": "endpoint_auth.refreshToken" + }, + "snippet": { + "type": "csharp", + "client": "using SeedInferredAuthImplicit;\n\nvar client = new SeedInferredAuthImplicitClient(\"client_id\", \"client_secret\", \"scope\");\nawait client.Auth.RefreshTokenAsync(\n new RefreshTokenRequest\n {\n ClientId = \"client_id\",\n ClientSecret = \"client_secret\",\n RefreshToken = \"refresh_token\",\n Audience = \"https://api.example.com\",\n GrantType = \"refresh_token\",\n Scope = \"scope\",\n }\n);\n" + } + }, + { + "example_identifier": null, + "id": { + "path": "/nested-no-auth/get-something", + "method": "GET", + "identifier_override": "endpoint_nested-no-auth/api.getSomething" + }, + "snippet": { + "type": "csharp", + "client": "using SeedInferredAuthImplicit;\n\nvar client = new SeedInferredAuthImplicitClient(\"client_id\", \"client_secret\", \"scope\");\nawait client.NestedNoAuth.Api.GetSomethingAsync();\n" + } + }, + { + "example_identifier": null, + "id": { + "path": "/nested/get-something", + "method": "GET", + "identifier_override": "endpoint_nested/api.getSomething" + }, + "snippet": { + "type": "csharp", + "client": "using SeedInferredAuthImplicit;\n\nvar client = new SeedInferredAuthImplicitClient(\"client_id\", \"client_secret\", \"scope\");\nawait client.Nested.Api.GetSomethingAsync();\n" + } + }, + { + "example_identifier": null, + "id": { + "path": "/get-something", + "method": "GET", + "identifier_override": "endpoint_simple.getSomething" + }, + "snippet": { + "type": "csharp", + "client": "using SeedInferredAuthImplicit;\n\nvar client = new SeedInferredAuthImplicitClient(\"client_id\", \"client_secret\", \"scope\");\nawait client.Simple.GetSomethingAsync();\n" + } + } + ] +} \ No newline at end of file diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedApi.DynamicSnippets/Example0.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedApi.DynamicSnippets/Example0.cs new file mode 100644 index 000000000000..7f15ef01d31b --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedApi.DynamicSnippets/Example0.cs @@ -0,0 +1,28 @@ +using SeedInferredAuthImplicit; + +namespace Usage; + +public class Example0 +{ + public async Task Do() { + var client = new SeedInferredAuthImplicitClient( + clientId: "client_id", + clientSecret: "client_secret", + scope: "scope", + clientOptions: new ClientOptions { + BaseUrl = "https://api.fern.com" + } + ); + + await client.Auth.GetTokenWithClientCredentialsAsync( + new GetTokenRequest { + ClientId = "client_id", + ClientSecret = "client_secret", + Audience = "https://api.example.com", + GrantType = "client_credentials", + Scope = "scope" + } + ); + } + +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedApi.DynamicSnippets/Example1.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedApi.DynamicSnippets/Example1.cs new file mode 100644 index 000000000000..0f66ac2576f2 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedApi.DynamicSnippets/Example1.cs @@ -0,0 +1,29 @@ +using SeedInferredAuthImplicit; + +namespace Usage; + +public class Example1 +{ + public async Task Do() { + var client = new SeedInferredAuthImplicitClient( + clientId: "client_id", + clientSecret: "client_secret", + scope: "scope", + clientOptions: new ClientOptions { + BaseUrl = "https://api.fern.com" + } + ); + + await client.Auth.RefreshTokenAsync( + new RefreshTokenRequest { + ClientId = "client_id", + ClientSecret = "client_secret", + RefreshToken = "refresh_token", + Audience = "https://api.example.com", + GrantType = "refresh_token", + Scope = "scope" + } + ); + } + +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedApi.DynamicSnippets/Example2.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedApi.DynamicSnippets/Example2.cs new file mode 100644 index 000000000000..040d7e97cb97 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedApi.DynamicSnippets/Example2.cs @@ -0,0 +1,20 @@ +using SeedInferredAuthImplicit; + +namespace Usage; + +public class Example2 +{ + public async Task Do() { + var client = new SeedInferredAuthImplicitClient( + clientId: "client_id", + clientSecret: "client_secret", + scope: "scope", + clientOptions: new ClientOptions { + BaseUrl = "https://api.fern.com" + } + ); + + await client.NestedNoAuth.Api.GetSomethingAsync(); + } + +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedApi.DynamicSnippets/Example3.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedApi.DynamicSnippets/Example3.cs new file mode 100644 index 000000000000..9489ed75d05b --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedApi.DynamicSnippets/Example3.cs @@ -0,0 +1,20 @@ +using SeedInferredAuthImplicit; + +namespace Usage; + +public class Example3 +{ + public async Task Do() { + var client = new SeedInferredAuthImplicitClient( + clientId: "client_id", + clientSecret: "client_secret", + scope: "scope", + clientOptions: new ClientOptions { + BaseUrl = "https://api.fern.com" + } + ); + + await client.Nested.Api.GetSomethingAsync(); + } + +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedApi.DynamicSnippets/Example4.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedApi.DynamicSnippets/Example4.cs new file mode 100644 index 000000000000..76b313d21a91 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedApi.DynamicSnippets/Example4.cs @@ -0,0 +1,20 @@ +using SeedInferredAuthImplicit; + +namespace Usage; + +public class Example4 +{ + public async Task Do() { + var client = new SeedInferredAuthImplicitClient( + clientId: "client_id", + clientSecret: "client_secret", + scope: "scope", + clientOptions: new ClientOptions { + BaseUrl = "https://api.fern.com" + } + ); + + await client.Simple.GetSomethingAsync(); + } + +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedApi.DynamicSnippets/SeedApi.DynamicSnippets.csproj b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedApi.DynamicSnippets/SeedApi.DynamicSnippets.csproj new file mode 100644 index 000000000000..3417db2e58e2 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedApi.DynamicSnippets/SeedApi.DynamicSnippets.csproj @@ -0,0 +1,13 @@ + + + + net8.0 + 12 + enable + enable + + + + + + \ No newline at end of file diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/Json/AdditionalPropertiesTests.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/Json/AdditionalPropertiesTests.cs new file mode 100644 index 000000000000..c0a032843861 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/Json/AdditionalPropertiesTests.cs @@ -0,0 +1,365 @@ +using global::System.Text.Json; +using global::System.Text.Json.Serialization; +using NUnit.Framework; +using SeedInferredAuthImplicit.Core; + +namespace SeedInferredAuthImplicit.Test.Core.Json; + +[TestFixture] +public class AdditionalPropertiesTests +{ + [Test] + public void Record_OnDeserialized_ShouldPopulateAdditionalProperties() + { + // Arrange + const string json = """ + { + "id": "1", + "category": "fiction", + "title": "The Hobbit" + } + """; + + // Act + var record = JsonUtils.Deserialize(json); + + // Assert + Assert.That(record, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(record.Id, Is.EqualTo("1")); + Assert.That(record.AdditionalProperties["category"].GetString(), Is.EqualTo("fiction")); + Assert.That(record.AdditionalProperties["title"].GetString(), Is.EqualTo("The Hobbit")); + }); + } + + [Test] + public void RecordWithWriteableAdditionalProperties_OnSerialization_ShouldIncludeAdditionalProperties() + { + // Arrange + var record = new WriteableRecord + { + Id = "1", + AdditionalProperties = { ["category"] = "fiction", ["title"] = "The Hobbit" }, + }; + + // Act + var json = JsonUtils.Serialize(record); + var deserializedRecord = JsonUtils.Deserialize(json); + + // Assert + Assert.That(deserializedRecord, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(deserializedRecord.Id, Is.EqualTo("1")); + Assert.That( + deserializedRecord.AdditionalProperties["category"], + Is.InstanceOf() + ); + Assert.That( + ((JsonElement)deserializedRecord.AdditionalProperties["category"]!).GetString(), + Is.EqualTo("fiction") + ); + Assert.That( + deserializedRecord.AdditionalProperties["title"], + Is.InstanceOf() + ); + Assert.That( + ((JsonElement)deserializedRecord.AdditionalProperties["title"]!).GetString(), + Is.EqualTo("The Hobbit") + ); + }); + } + + [Test] + public void ReadOnlyAdditionalProperties_ShouldRetrieveValuesCorrectly() + { + // Arrange + var extensionData = new Dictionary + { + ["key1"] = JsonUtils.SerializeToElement("value1"), + ["key2"] = JsonUtils.SerializeToElement(123), + }; + var readOnlyProps = new ReadOnlyAdditionalProperties(); + readOnlyProps.CopyFromExtensionData(extensionData); + + // Act & Assert + Assert.That(readOnlyProps["key1"].GetString(), Is.EqualTo("value1")); + Assert.That(readOnlyProps["key2"].GetInt32(), Is.EqualTo(123)); + } + + [Test] + public void AdditionalProperties_ShouldBehaveAsDictionary() + { + // Arrange + var additionalProps = new AdditionalProperties { ["key1"] = "value1", ["key2"] = 123 }; + + // Act + additionalProps["key3"] = true; + + // Assert + Assert.Multiple(() => + { + Assert.That(additionalProps["key1"], Is.EqualTo("value1")); + Assert.That(additionalProps["key2"], Is.EqualTo(123)); + Assert.That((bool)additionalProps["key3"]!, Is.True); + Assert.That(additionalProps.Count, Is.EqualTo(3)); + }); + } + + [Test] + public void AdditionalProperties_ToJsonObject_ShouldSerializeCorrectly() + { + // Arrange + var additionalProps = new AdditionalProperties { ["key1"] = "value1", ["key2"] = 123 }; + + // Act + var jsonObject = additionalProps.ToJsonObject(); + + Assert.Multiple(() => + { + // Assert + Assert.That(jsonObject["key1"]!.GetValue(), Is.EqualTo("value1")); + Assert.That(jsonObject["key2"]!.GetValue(), Is.EqualTo(123)); + }); + } + + [Test] + public void AdditionalProperties_MixReadAndWrite_ShouldOverwriteDeserializedProperty() + { + // Arrange + const string json = """ + { + "id": "1", + "category": "fiction", + "title": "The Hobbit" + } + """; + var record = JsonUtils.Deserialize(json); + + // Act + record.AdditionalProperties["category"] = "non-fiction"; + + // Assert + Assert.Multiple(() => + { + Assert.That(record, Is.Not.Null); + Assert.That(record.Id, Is.EqualTo("1")); + Assert.That(record.AdditionalProperties["category"], Is.EqualTo("non-fiction")); + Assert.That(record.AdditionalProperties["title"], Is.InstanceOf()); + Assert.That( + ((JsonElement)record.AdditionalProperties["title"]!).GetString(), + Is.EqualTo("The Hobbit") + ); + }); + } + + [Test] + public void RecordWithReadonlyAdditionalPropertiesInts_OnDeserialized_ShouldPopulateAdditionalProperties() + { + // Arrange + const string json = """ + { + "extra1": 42, + "extra2": 99 + } + """; + + // Act + var record = JsonUtils.Deserialize(json); + + // Assert + Assert.That(record, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(record.AdditionalProperties["extra1"], Is.EqualTo(42)); + Assert.That(record.AdditionalProperties["extra2"], Is.EqualTo(99)); + }); + } + + [Test] + public void RecordWithAdditionalPropertiesInts_OnSerialization_ShouldIncludeAdditionalProperties() + { + // Arrange + var record = new WriteableRecordWithInts + { + AdditionalProperties = { ["extra1"] = 42, ["extra2"] = 99 }, + }; + + // Act + var json = JsonUtils.Serialize(record); + var deserializedRecord = JsonUtils.Deserialize(json); + + // Assert + Assert.That(deserializedRecord, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(deserializedRecord.AdditionalProperties["extra1"], Is.EqualTo(42)); + Assert.That(deserializedRecord.AdditionalProperties["extra2"], Is.EqualTo(99)); + }); + } + + [Test] + public void RecordWithReadonlyAdditionalPropertiesDictionaries_OnDeserialized_ShouldPopulateAdditionalProperties() + { + // Arrange + const string json = """ + { + "extra1": { "key1": true, "key2": false }, + "extra2": { "key3": true } + } + """; + + // Act + var record = JsonUtils.Deserialize(json); + + // Assert + Assert.That(record, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(record.AdditionalProperties["extra1"]["key1"], Is.True); + Assert.That(record.AdditionalProperties["extra1"]["key2"], Is.False); + Assert.That(record.AdditionalProperties["extra2"]["key3"], Is.True); + }); + } + + [Test] + public void RecordWithAdditionalPropertiesDictionaries_OnSerialization_ShouldIncludeAdditionalProperties() + { + // Arrange + var record = new WriteableRecordWithDictionaries + { + AdditionalProperties = + { + ["extra1"] = new Dictionary { { "key1", true }, { "key2", false } }, + ["extra2"] = new Dictionary { { "key3", true } }, + }, + }; + + // Act + var json = JsonUtils.Serialize(record); + var deserializedRecord = JsonUtils.Deserialize(json); + + // Assert + Assert.That(deserializedRecord, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(deserializedRecord.AdditionalProperties["extra1"]["key1"], Is.True); + Assert.That(deserializedRecord.AdditionalProperties["extra1"]["key2"], Is.False); + Assert.That(deserializedRecord.AdditionalProperties["extra2"]["key3"], Is.True); + }); + } + + private record Record : IJsonOnDeserialized + { + [JsonPropertyName("id")] + public required string Id { get; set; } + + [JsonExtensionData] + private readonly IDictionary _extensionData = + new Dictionary(); + + [JsonIgnore] + public ReadOnlyAdditionalProperties AdditionalProperties { get; } = new(); + + void IJsonOnDeserialized.OnDeserialized() + { + AdditionalProperties.CopyFromExtensionData(_extensionData); + } + } + + private record WriteableRecord : IJsonOnDeserialized, IJsonOnSerializing + { + [JsonPropertyName("id")] + public required string Id { get; set; } + + [JsonExtensionData] + private readonly IDictionary _extensionData = + new Dictionary(); + + [JsonIgnore] + public AdditionalProperties AdditionalProperties { get; set; } = new(); + + void IJsonOnDeserialized.OnDeserialized() + { + AdditionalProperties.CopyFromExtensionData(_extensionData); + } + + void IJsonOnSerializing.OnSerializing() + { + AdditionalProperties.CopyToExtensionData(_extensionData); + } + } + + private record RecordWithInts : IJsonOnDeserialized + { + [JsonExtensionData] + private readonly IDictionary _extensionData = + new Dictionary(); + + [JsonIgnore] + public ReadOnlyAdditionalProperties AdditionalProperties { get; } = new(); + + void IJsonOnDeserialized.OnDeserialized() + { + AdditionalProperties.CopyFromExtensionData(_extensionData); + } + } + + private record WriteableRecordWithInts : IJsonOnDeserialized, IJsonOnSerializing + { + [JsonExtensionData] + private readonly IDictionary _extensionData = + new Dictionary(); + + [JsonIgnore] + public AdditionalProperties AdditionalProperties { get; } = new(); + + void IJsonOnDeserialized.OnDeserialized() + { + AdditionalProperties.CopyFromExtensionData(_extensionData); + } + + void IJsonOnSerializing.OnSerializing() + { + AdditionalProperties.CopyToExtensionData(_extensionData); + } + } + + private record RecordWithDictionaries : IJsonOnDeserialized + { + [JsonExtensionData] + private readonly IDictionary _extensionData = + new Dictionary(); + + [JsonIgnore] + public ReadOnlyAdditionalProperties< + Dictionary + > AdditionalProperties { get; } = new(); + + void IJsonOnDeserialized.OnDeserialized() + { + AdditionalProperties.CopyFromExtensionData(_extensionData); + } + } + + private record WriteableRecordWithDictionaries : IJsonOnDeserialized, IJsonOnSerializing + { + [JsonExtensionData] + private readonly IDictionary _extensionData = + new Dictionary(); + + [JsonIgnore] + public AdditionalProperties> AdditionalProperties { get; } = new(); + + void IJsonOnDeserialized.OnDeserialized() + { + AdditionalProperties.CopyFromExtensionData(_extensionData); + } + + void IJsonOnSerializing.OnSerializing() + { + AdditionalProperties.CopyToExtensionData(_extensionData); + } + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/Json/DateOnlyJsonTests.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/Json/DateOnlyJsonTests.cs new file mode 100644 index 000000000000..16c45d284f09 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/Json/DateOnlyJsonTests.cs @@ -0,0 +1,76 @@ +using NUnit.Framework; +using SeedInferredAuthImplicit.Core; + +namespace SeedInferredAuthImplicit.Test.Core.Json; + +[TestFixture] +public class DateOnlyJsonTests +{ + [Test] + public void SerializeDateOnly_ShouldMatchExpectedFormat() + { + (DateOnly dateOnly, string expected)[] testCases = + [ + (new DateOnly(2023, 10, 5), "\"2023-10-05\""), + (new DateOnly(2023, 1, 1), "\"2023-01-01\""), + (new DateOnly(2023, 12, 31), "\"2023-12-31\""), + (new DateOnly(2023, 6, 15), "\"2023-06-15\""), + (new DateOnly(2023, 3, 10), "\"2023-03-10\""), + ]; + foreach (var (dateOnly, expected) in testCases) + { + var json = JsonUtils.Serialize(dateOnly); + Assert.That(json, Is.EqualTo(expected)); + } + } + + [Test] + public void DeserializeDateOnly_ShouldMatchExpectedDateOnly() + { + (DateOnly expected, string json)[] testCases = + [ + (new DateOnly(2023, 10, 5), "\"2023-10-05\""), + (new DateOnly(2023, 1, 1), "\"2023-01-01\""), + (new DateOnly(2023, 12, 31), "\"2023-12-31\""), + (new DateOnly(2023, 6, 15), "\"2023-06-15\""), + (new DateOnly(2023, 3, 10), "\"2023-03-10\""), + ]; + + foreach (var (expected, json) in testCases) + { + var dateOnly = JsonUtils.Deserialize(json); + Assert.That(dateOnly, Is.EqualTo(expected)); + } + } + + [Test] + public void SerializeNullableDateOnly_ShouldMatchExpectedFormat() + { + (DateOnly? dateOnly, string expected)[] testCases = + [ + (new DateOnly(2023, 10, 5), "\"2023-10-05\""), + (null, "null"), + ]; + foreach (var (dateOnly, expected) in testCases) + { + var json = JsonUtils.Serialize(dateOnly); + Assert.That(json, Is.EqualTo(expected)); + } + } + + [Test] + public void DeserializeNullableDateOnly_ShouldMatchExpectedDateOnly() + { + (DateOnly? expected, string json)[] testCases = + [ + (new DateOnly(2023, 10, 5), "\"2023-10-05\""), + (null, "null"), + ]; + + foreach (var (expected, json) in testCases) + { + var dateOnly = JsonUtils.Deserialize(json); + Assert.That(dateOnly, Is.EqualTo(expected)); + } + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/Json/DateTimeJsonTests.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/Json/DateTimeJsonTests.cs new file mode 100644 index 000000000000..8bab90119d14 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/Json/DateTimeJsonTests.cs @@ -0,0 +1,110 @@ +using NUnit.Framework; +using SeedInferredAuthImplicit.Core; + +namespace SeedInferredAuthImplicit.Test.Core.Json; + +[TestFixture] +public class DateTimeJsonTests +{ + [Test] + public void SerializeDateTime_ShouldMatchExpectedFormat() + { + (DateTime dateTime, string expected)[] testCases = + [ + ( + new DateTime(2023, 10, 5, 14, 30, 0, DateTimeKind.Utc), + "\"2023-10-05T14:30:00.000Z\"" + ), + (new DateTime(2023, 1, 1, 0, 0, 0, DateTimeKind.Utc), "\"2023-01-01T00:00:00.000Z\""), + ( + new DateTime(2023, 12, 31, 23, 59, 59, DateTimeKind.Utc), + "\"2023-12-31T23:59:59.000Z\"" + ), + (new DateTime(2023, 6, 15, 12, 0, 0, DateTimeKind.Utc), "\"2023-06-15T12:00:00.000Z\""), + ( + new DateTime(2023, 3, 10, 8, 45, 30, DateTimeKind.Utc), + "\"2023-03-10T08:45:30.000Z\"" + ), + ( + new DateTime(2023, 3, 10, 8, 45, 30, 123, DateTimeKind.Utc), + "\"2023-03-10T08:45:30.123Z\"" + ), + ]; + foreach (var (dateTime, expected) in testCases) + { + var json = JsonUtils.Serialize(dateTime); + Assert.That(json, Is.EqualTo(expected)); + } + } + + [Test] + public void DeserializeDateTime_ShouldMatchExpectedDateTime() + { + (DateTime expected, string json)[] testCases = + [ + ( + new DateTime(2023, 10, 5, 14, 30, 0, DateTimeKind.Utc), + "\"2023-10-05T14:30:00.000Z\"" + ), + (new DateTime(2023, 1, 1, 0, 0, 0, DateTimeKind.Utc), "\"2023-01-01T00:00:00.000Z\""), + ( + new DateTime(2023, 12, 31, 23, 59, 59, DateTimeKind.Utc), + "\"2023-12-31T23:59:59.000Z\"" + ), + (new DateTime(2023, 6, 15, 12, 0, 0, DateTimeKind.Utc), "\"2023-06-15T12:00:00.000Z\""), + ( + new DateTime(2023, 3, 10, 8, 45, 30, DateTimeKind.Utc), + "\"2023-03-10T08:45:30.000Z\"" + ), + (new DateTime(2023, 3, 10, 8, 45, 30, DateTimeKind.Utc), "\"2023-03-10T08:45:30Z\""), + ( + new DateTime(2023, 3, 10, 8, 45, 30, 123, DateTimeKind.Utc), + "\"2023-03-10T08:45:30.123Z\"" + ), + ]; + + foreach (var (expected, json) in testCases) + { + var dateTime = JsonUtils.Deserialize(json); + Assert.That(dateTime, Is.EqualTo(expected)); + } + } + + [Test] + public void SerializeNullableDateTime_ShouldMatchExpectedFormat() + { + (DateTime? expected, string json)[] testCases = + [ + ( + new DateTime(2023, 10, 5, 14, 30, 0, DateTimeKind.Utc), + "\"2023-10-05T14:30:00.000Z\"" + ), + (null, "null"), + ]; + + foreach (var (expected, json) in testCases) + { + var dateTime = JsonUtils.Deserialize(json); + Assert.That(dateTime, Is.EqualTo(expected)); + } + } + + [Test] + public void DeserializeNullableDateTime_ShouldMatchExpectedDateTime() + { + (DateTime? expected, string json)[] testCases = + [ + ( + new DateTime(2023, 10, 5, 14, 30, 0, DateTimeKind.Utc), + "\"2023-10-05T14:30:00.000Z\"" + ), + (null, "null"), + ]; + + foreach (var (expected, json) in testCases) + { + var dateTime = JsonUtils.Deserialize(json); + Assert.That(dateTime, Is.EqualTo(expected)); + } + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/Json/JsonAccessAttributeTests.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/Json/JsonAccessAttributeTests.cs new file mode 100644 index 000000000000..6eef3d3e1bc0 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/Json/JsonAccessAttributeTests.cs @@ -0,0 +1,160 @@ +using global::System.Text.Json.Serialization; +using NUnit.Framework; +using SeedInferredAuthImplicit.Core; + +namespace SeedInferredAuthImplicit.Test.Core.Json; + +[TestFixture] +public class JsonAccessAttributeTests +{ + private class MyClass + { + [JsonPropertyName("read_only_prop")] + [JsonAccess(JsonAccessType.ReadOnly)] + public string? ReadOnlyProp { get; set; } + + [JsonPropertyName("write_only_prop")] + [JsonAccess(JsonAccessType.WriteOnly)] + public string? WriteOnlyProp { get; set; } + + [JsonPropertyName("normal_prop")] + public string? NormalProp { get; set; } + + [JsonPropertyName("read_only_nullable_list")] + [JsonAccess(JsonAccessType.ReadOnly)] + public IEnumerable? ReadOnlyNullableList { get; set; } + + [JsonPropertyName("read_only_list")] + [JsonAccess(JsonAccessType.ReadOnly)] + public IEnumerable ReadOnlyList { get; set; } = []; + + [JsonPropertyName("write_only_nullable_list")] + [JsonAccess(JsonAccessType.WriteOnly)] + public IEnumerable? WriteOnlyNullableList { get; set; } + + [JsonPropertyName("write_only_list")] + [JsonAccess(JsonAccessType.WriteOnly)] + public IEnumerable WriteOnlyList { get; set; } = []; + + [JsonPropertyName("normal_list")] + public IEnumerable NormalList { get; set; } = []; + + [JsonPropertyName("normal_nullable_list")] + public IEnumerable? NullableNormalList { get; set; } + } + + [Test] + public void JsonAccessAttribute_ShouldWorkAsExpected() + { + const string json = """ + { + "read_only_prop": "read", + "write_only_prop": "write", + "normal_prop": "normal_prop", + "read_only_nullable_list": ["item1", "item2"], + "read_only_list": ["item3", "item4"], + "write_only_nullable_list": ["item5", "item6"], + "write_only_list": ["item7", "item8"], + "normal_list": ["normal1", "normal2"], + "normal_nullable_list": ["normal1", "normal2"] + } + """; + var obj = JsonUtils.Deserialize(json); + + Assert.Multiple(() => + { + // String properties + Assert.That(obj.ReadOnlyProp, Is.EqualTo("read")); + Assert.That(obj.WriteOnlyProp, Is.Null); + Assert.That(obj.NormalProp, Is.EqualTo("normal_prop")); + + // List properties - read only + var nullableReadOnlyList = obj.ReadOnlyNullableList?.ToArray(); + Assert.That(nullableReadOnlyList, Is.Not.Null); + Assert.That(nullableReadOnlyList, Has.Length.EqualTo(2)); + Assert.That(nullableReadOnlyList![0], Is.EqualTo("item1")); + Assert.That(nullableReadOnlyList![1], Is.EqualTo("item2")); + + var readOnlyList = obj.ReadOnlyList.ToArray(); + Assert.That(readOnlyList, Is.Not.Null); + Assert.That(readOnlyList, Has.Length.EqualTo(2)); + Assert.That(readOnlyList[0], Is.EqualTo("item3")); + Assert.That(readOnlyList[1], Is.EqualTo("item4")); + + // List properties - write only + Assert.That(obj.WriteOnlyNullableList, Is.Null); + Assert.That(obj.WriteOnlyList, Is.Not.Null); + Assert.That(obj.WriteOnlyList, Is.Empty); + + // Normal list property + var normalList = obj.NormalList.ToArray(); + Assert.That(normalList, Is.Not.Null); + Assert.That(normalList, Has.Length.EqualTo(2)); + Assert.That(normalList[0], Is.EqualTo("normal1")); + Assert.That(normalList[1], Is.EqualTo("normal2")); + }); + + // Set up values for serialization + obj.WriteOnlyProp = "write"; + obj.NormalProp = "new_value"; + obj.WriteOnlyNullableList = new List { "write1", "write2" }; + obj.WriteOnlyList = new List { "write3", "write4" }; + obj.NormalList = new List { "new_normal" }; + obj.NullableNormalList = new List { "new_normal" }; + + var serializedJson = JsonUtils.Serialize(obj); + const string expectedJson = """ + { + "write_only_prop": "write", + "normal_prop": "new_value", + "write_only_nullable_list": [ + "write1", + "write2" + ], + "write_only_list": [ + "write3", + "write4" + ], + "normal_list": [ + "new_normal" + ], + "normal_nullable_list": [ + "new_normal" + ] + } + """; + Assert.That(serializedJson, Is.EqualTo(expectedJson).IgnoreWhiteSpace); + } + + [Test] + public void JsonAccessAttribute_WithNullListsInJson_ShouldWorkAsExpected() + { + const string json = """ + { + "read_only_prop": "read", + "normal_prop": "normal_prop", + "read_only_nullable_list": null, + "read_only_list": [] + } + """; + var obj = JsonUtils.Deserialize(json); + + Assert.Multiple(() => + { + // Read-only nullable list should be null when JSON contains null + var nullableReadOnlyList = obj.ReadOnlyNullableList?.ToArray(); + Assert.That(nullableReadOnlyList, Is.Null); + + // Read-only non-nullable list should never be null, but empty when JSON contains null + var readOnlyList = obj.ReadOnlyList.ToArray(); // This should be initialized to an empty list by default + Assert.That(readOnlyList, Is.Not.Null); + Assert.That(readOnlyList, Is.Empty); + }); + + // Serialize and verify read-only lists are not included + var serializedJson = JsonUtils.Serialize(obj); + Assert.That(serializedJson, Does.Not.Contain("read_only_prop")); + Assert.That(serializedJson, Does.Not.Contain("read_only_nullable_list")); + Assert.That(serializedJson, Does.Not.Contain("read_only_list")); + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/Json/OneOfSerializerTests.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/Json/OneOfSerializerTests.cs new file mode 100644 index 000000000000..2d420f8b76df --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/Json/OneOfSerializerTests.cs @@ -0,0 +1,314 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using NUnit.Framework; +using OneOf; +using SeedInferredAuthImplicit.Core; + +namespace SeedInferredAuthImplicit.Test.Core.Json; + +[TestFixture] +[Parallelizable(ParallelScope.All)] +public class OneOfSerializerTests +{ + private class Foo + { + [JsonPropertyName("string_prop")] + public required string StringProp { get; set; } + } + + private class Bar + { + [JsonPropertyName("int_prop")] + public required int IntProp { get; set; } + } + + private static readonly OneOf OneOf1 = OneOf< + string, + int, + object, + Foo, + Bar + >.FromT2(new { }); + private const string OneOf1String = "{}"; + + private static readonly OneOf OneOf2 = OneOf< + string, + int, + object, + Foo, + Bar + >.FromT0("test"); + private const string OneOf2String = "\"test\""; + + private static readonly OneOf OneOf3 = OneOf< + string, + int, + object, + Foo, + Bar + >.FromT1(123); + private const string OneOf3String = "123"; + + private static readonly OneOf OneOf4 = OneOf< + string, + int, + object, + Foo, + Bar + >.FromT3(new Foo { StringProp = "test" }); + private const string OneOf4String = "{\"string_prop\": \"test\"}"; + + private static readonly OneOf OneOf5 = OneOf< + string, + int, + object, + Foo, + Bar + >.FromT4(new Bar { IntProp = 5 }); + private const string OneOf5String = "{\"int_prop\": 5}"; + + [Test] + public void Serialize_OneOfs_Should_Return_Expected_String() + { + (OneOf, string)[] testData = + [ + (OneOf1, OneOf1String), + (OneOf2, OneOf2String), + (OneOf3, OneOf3String), + (OneOf4, OneOf4String), + (OneOf5, OneOf5String), + ]; + Assert.Multiple(() => + { + foreach (var (oneOf, expected) in testData) + { + var result = JsonUtils.Serialize(oneOf); + Assert.That(result, Is.EqualTo(expected).IgnoreWhiteSpace); + } + }); + } + + [Test] + public void OneOfs_Should_Deserialize_From_String() + { + (OneOf, string)[] testData = + [ + (OneOf1, OneOf1String), + (OneOf2, OneOf2String), + (OneOf3, OneOf3String), + (OneOf4, OneOf4String), + (OneOf5, OneOf5String), + ]; + Assert.Multiple(() => + { + foreach (var (oneOf, json) in testData) + { + var result = JsonUtils.Deserialize>(json); + Assert.That(result.Index, Is.EqualTo(oneOf.Index)); + Assert.That(json, Is.EqualTo(JsonUtils.Serialize(result.Value)).IgnoreWhiteSpace); + } + }); + } + + private static readonly OneOf? NullableOneOf1 = null; + private const string NullableOneOf1String = "null"; + + private static readonly OneOf? NullableOneOf2 = OneOf< + string, + int, + object, + Foo, + Bar + >.FromT4(new Bar { IntProp = 5 }); + private const string NullableOneOf2String = "{\"int_prop\": 5}"; + + [Test] + public void Serialize_NullableOneOfs_Should_Return_Expected_String() + { + (OneOf?, string)[] testData = + [ + (NullableOneOf1, NullableOneOf1String), + (NullableOneOf2, NullableOneOf2String), + ]; + Assert.Multiple(() => + { + foreach (var (oneOf, expected) in testData) + { + var result = JsonUtils.Serialize(oneOf); + Assert.That(result, Is.EqualTo(expected).IgnoreWhiteSpace); + } + }); + } + + [Test] + public void NullableOneOfs_Should_Deserialize_From_String() + { + (OneOf?, string)[] testData = + [ + (NullableOneOf1, NullableOneOf1String), + (NullableOneOf2, NullableOneOf2String), + ]; + Assert.Multiple(() => + { + foreach (var (oneOf, json) in testData) + { + var result = JsonUtils.Deserialize?>(json); + Assert.That(result?.Index, Is.EqualTo(oneOf?.Index)); + Assert.That(json, Is.EqualTo(JsonUtils.Serialize(result?.Value)).IgnoreWhiteSpace); + } + }); + } + + private static readonly OneOf OneOfWithNullable1 = OneOf< + string, + int, + Foo? + >.FromT2(null); + private const string OneOfWithNullable1String = "null"; + + private static readonly OneOf OneOfWithNullable2 = OneOf< + string, + int, + Foo? + >.FromT2(new Foo { StringProp = "test" }); + private const string OneOfWithNullable2String = "{\"string_prop\": \"test\"}"; + + private static readonly OneOf OneOfWithNullable3 = OneOf< + string, + int, + Foo? + >.FromT0("test"); + private const string OneOfWithNullable3String = "\"test\""; + + [Test] + public void Serialize_OneOfWithNullables_Should_Return_Expected_String() + { + (OneOf, string)[] testData = + [ + (OneOfWithNullable1, OneOfWithNullable1String), + (OneOfWithNullable2, OneOfWithNullable2String), + (OneOfWithNullable3, OneOfWithNullable3String), + ]; + Assert.Multiple(() => + { + foreach (var (oneOf, expected) in testData) + { + var result = JsonUtils.Serialize(oneOf); + Assert.That(result, Is.EqualTo(expected).IgnoreWhiteSpace); + } + }); + } + + [Test] + public void OneOfWithNullables_Should_Deserialize_From_String() + { + (OneOf, string)[] testData = + [ + // (OneOfWithNullable1, OneOfWithNullable1String), // not possible with .NET's JSON serializer + (OneOfWithNullable2, OneOfWithNullable2String), + (OneOfWithNullable3, OneOfWithNullable3String), + ]; + Assert.Multiple(() => + { + foreach (var (oneOf, json) in testData) + { + var result = JsonUtils.Deserialize>(json); + Assert.That(result.Index, Is.EqualTo(oneOf.Index)); + Assert.That(json, Is.EqualTo(JsonUtils.Serialize(result.Value)).IgnoreWhiteSpace); + } + }); + } + + [Test] + public void Serialize_OneOfWithObjectLast_Should_Return_Expected_String() + { + var oneOfWithObjectLast = OneOf.FromT4( + new { random = "data" } + ); + const string oneOfWithObjectLastString = "{\"random\": \"data\"}"; + + var result = JsonUtils.Serialize(oneOfWithObjectLast); + Assert.That(result, Is.EqualTo(oneOfWithObjectLastString).IgnoreWhiteSpace); + } + + [Test] + public void OneOfWithObjectLast_Should_Deserialize_From_String() + { + const string oneOfWithObjectLastString = "{\"random\": \"data\"}"; + var result = JsonUtils.Deserialize>( + oneOfWithObjectLastString + ); + Assert.Multiple(() => + { + Assert.That(result.Index, Is.EqualTo(4)); + Assert.That(result.Value, Is.InstanceOf()); + Assert.That( + JsonUtils.Serialize(result.Value), + Is.EqualTo(oneOfWithObjectLastString).IgnoreWhiteSpace + ); + }); + } + + [Test] + public void Serialize_OneOfWithObjectNotLast_Should_Return_Expected_String() + { + var oneOfWithObjectNotLast = OneOf.FromT1( + new { random = "data" } + ); + const string oneOfWithObjectNotLastString = "{\"random\": \"data\"}"; + + var result = JsonUtils.Serialize(oneOfWithObjectNotLast); + Assert.That(result, Is.EqualTo(oneOfWithObjectNotLastString).IgnoreWhiteSpace); + } + + [Test] + public void OneOfWithObjectNotLast_Should_Deserialize_From_String() + { + const string oneOfWithObjectNotLastString = "{\"random\": \"data\"}"; + var result = JsonUtils.Deserialize>( + oneOfWithObjectNotLastString + ); + Assert.Multiple(() => + { + Assert.That(result.Index, Is.EqualTo(1)); + Assert.That(result.Value, Is.InstanceOf()); + Assert.That( + JsonUtils.Serialize(result.Value), + Is.EqualTo(oneOfWithObjectNotLastString).IgnoreWhiteSpace + ); + }); + } + + [Test] + public void Serialize_OneOfSingleType_Should_Return_Expected_String() + { + var oneOfSingle = OneOf.FromT0("single"); + const string oneOfSingleString = "\"single\""; + + var result = JsonUtils.Serialize(oneOfSingle); + Assert.That(result, Is.EqualTo(oneOfSingleString)); + } + + [Test] + public void OneOfSingleType_Should_Deserialize_From_String() + { + const string oneOfSingleString = "\"single\""; + var result = JsonUtils.Deserialize>(oneOfSingleString); + Assert.Multiple(() => + { + Assert.That(result.Index, Is.EqualTo(0)); + Assert.That(result.Value, Is.EqualTo("single")); + }); + } + + [Test] + public void Deserialize_InvalidData_Should_Throw_Exception() + { + const string invalidJson = "{\"invalid\": \"data\"}"; + + Assert.Throws(() => + { + JsonUtils.Deserialize>(invalidJson); + }); + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/Json/StringEnumSerializerTests.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/Json/StringEnumSerializerTests.cs new file mode 100644 index 000000000000..b1afde182649 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/Json/StringEnumSerializerTests.cs @@ -0,0 +1,138 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using NUnit.Framework; +using SeedInferredAuthImplicit.Core; + +namespace SeedInferredAuthImplicit.Test.Core.Json; + +[TestFixture] +[Parallelizable(ParallelScope.All)] +public class StringEnumSerializerTests +{ + private static readonly JsonSerializerOptions JsonOptions = new() { WriteIndented = true }; + + private static readonly DummyEnum KnownEnumValue2 = DummyEnum.KnownValue2; + private static readonly DummyEnum UnknownEnumValue = DummyEnum.FromCustom("unknown_value"); + + private static readonly string JsonWithKnownEnum2 = $$""" + { + "enum_property": "{{KnownEnumValue2}}" + } + """; + + private static readonly string JsonWithUnknownEnum = $$""" + { + "enum_property": "{{UnknownEnumValue}}" + } + """; + + [Test] + public void ShouldParseKnownEnumValue2() + { + var obj = JsonSerializer.Deserialize(JsonWithKnownEnum2, JsonOptions); + Assert.That(obj, Is.Not.Null); + Assert.That(obj.EnumProperty, Is.EqualTo(KnownEnumValue2)); + } + + [Test] + public void ShouldParseUnknownEnum() + { + var obj = JsonSerializer.Deserialize(JsonWithUnknownEnum, JsonOptions); + Assert.That(obj, Is.Not.Null); + Assert.That(obj.EnumProperty, Is.EqualTo(UnknownEnumValue)); + } + + [Test] + public void ShouldSerializeKnownEnumValue2() + { + var json = JsonSerializer.SerializeToElement( + new DummyObject { EnumProperty = KnownEnumValue2 }, + JsonOptions + ); + TestContext.Out.WriteLine("Serialized JSON: \n" + json); + var enumString = json.GetProperty("enum_property").GetString(); + Assert.That(enumString, Is.Not.Null); + Assert.That(enumString, Is.EqualTo(KnownEnumValue2)); + } + + [Test] + public void ShouldSerializeUnknownEnum() + { + var json = JsonSerializer.SerializeToElement( + new DummyObject { EnumProperty = UnknownEnumValue }, + JsonOptions + ); + TestContext.Out.WriteLine("Serialized JSON: \n" + json); + var enumString = json.GetProperty("enum_property").GetString(); + Assert.That(enumString, Is.Not.Null); + Assert.That(enumString, Is.EqualTo(UnknownEnumValue)); + } +} + +public class DummyObject +{ + [JsonPropertyName("enum_property")] + public DummyEnum EnumProperty { get; set; } +} + +[JsonConverter(typeof(StringEnumSerializer))] +public readonly record struct DummyEnum : IStringEnum +{ + public DummyEnum(string value) + { + Value = value; + } + + /// + /// The string value of the enum. + /// + public string Value { get; } + + public static readonly DummyEnum KnownValue1 = FromCustom(Values.KnownValue1); + + public static readonly DummyEnum KnownValue2 = FromCustom(Values.KnownValue2); + + /// + /// Constant strings for enum values + /// + public static class Values + { + public const string KnownValue1 = "known_value1"; + + public const string KnownValue2 = "known_value2"; + } + + /// + /// Create a string enum with the given value. + /// + public static DummyEnum FromCustom(string value) + { + return new DummyEnum(value); + } + + /// + /// Returns the string value of the enum. + /// + public override string ToString() + { + return Value; + } + + public bool Equals(string? other) + { + return Value.Equals(other); + } + + public override int GetHashCode() + { + return Value.GetHashCode(); + } + + public static explicit operator string(DummyEnum value) => value.Value; + + public static explicit operator DummyEnum(string value) => new(value); + + public static bool operator ==(DummyEnum value1, string value2) => value1.Value.Equals(value2); + + public static bool operator !=(DummyEnum value1, string value2) => !value1.Value.Equals(value2); +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/QueryStringConverterTests.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/QueryStringConverterTests.cs new file mode 100644 index 000000000000..9f916de191c9 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/QueryStringConverterTests.cs @@ -0,0 +1,124 @@ +using NUnit.Framework; +using SeedInferredAuthImplicit.Core; + +namespace SeedInferredAuthImplicit.Test.Core; + +[TestFixture] +public class QueryStringConverterTests +{ + [Test] + public void ToQueryStringCollection_Form() + { + var obj = new + { + Name = "John", + Age = 30, + Address = new + { + Street = "123 Main St", + City = "Anytown", + Coordinates = new[] { 39.781721f, -89.650148f }, + }, + Tags = new[] { "Developer", "Blogger" }, + }; + var result = QueryStringConverter.ToForm(obj); + var expected = new List> + { + new("Name", "John"), + new("Age", "30"), + new("Address[Street]", "123 Main St"), + new("Address[City]", "Anytown"), + new("Address[Coordinates]", "39.78172,-89.65015"), + new("Tags", "Developer,Blogger"), + }; + Assert.That(result, Is.EqualTo(expected)); + } + + [Test] + public void ToQueryStringCollection_ExplodedForm() + { + var obj = new + { + Name = "John", + Age = 30, + Address = new + { + Street = "123 Main St", + City = "Anytown", + Coordinates = new[] { 39.781721f, -89.650148f }, + }, + Tags = new[] { "Developer", "Blogger" }, + }; + var result = QueryStringConverter.ToExplodedForm(obj); + var expected = new List> + { + new("Name", "John"), + new("Age", "30"), + new("Address[Street]", "123 Main St"), + new("Address[City]", "Anytown"), + new("Address[Coordinates]", "39.78172"), + new("Address[Coordinates]", "-89.65015"), + new("Tags", "Developer"), + new("Tags", "Blogger"), + }; + Assert.That(result, Is.EqualTo(expected)); + } + + [Test] + public void ToQueryStringCollection_DeepObject() + { + var obj = new + { + Name = "John", + Age = 30, + Address = new + { + Street = "123 Main St", + City = "Anytown", + Coordinates = new[] { 39.781721f, -89.650148f }, + }, + Tags = new[] { "Developer", "Blogger" }, + }; + var result = QueryStringConverter.ToDeepObject(obj); + var expected = new List> + { + new("Name", "John"), + new("Age", "30"), + new("Address[Street]", "123 Main St"), + new("Address[City]", "Anytown"), + new("Address[Coordinates][0]", "39.78172"), + new("Address[Coordinates][1]", "-89.65015"), + new("Tags[0]", "Developer"), + new("Tags[1]", "Blogger"), + }; + Assert.That(result, Is.EqualTo(expected)); + } + + [Test] + public void ToQueryStringCollection_OnString_ThrowsException() + { + var exception = Assert.Throws(() => + QueryStringConverter.ToForm("invalid") + ); + Assert.That( + exception.Message, + Is.EqualTo( + "Only objects can be converted to query string collections. Given type is String." + ) + ); + } + + [Test] + public void ToQueryStringCollection_OnArray_ThrowsException() + { + var exception = Assert.Throws(() => + QueryStringConverter.ToForm(Array.Empty()) + ); + Assert.That( + exception.Message, + Is.EqualTo( + "Only objects can be converted to query string collections. Given type is Array." + ) + ); + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/RawClientTests/AdditionalHeadersTests.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/RawClientTests/AdditionalHeadersTests.cs new file mode 100644 index 000000000000..eb98056be166 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/RawClientTests/AdditionalHeadersTests.cs @@ -0,0 +1,138 @@ +using NUnit.Framework; +using SeedInferredAuthImplicit.Core; +using WireMock.Server; +using SystemTask = global::System.Threading.Tasks.Task; +using WireMockRequest = WireMock.RequestBuilders.Request; +using WireMockResponse = WireMock.ResponseBuilders.Response; + +// ReSharper disable NullableWarningSuppressionIsUsed + +namespace SeedInferredAuthImplicit.Test.Core.RawClientTests; + +[TestFixture] +[Parallelizable(ParallelScope.Self)] +public class AdditionalHeadersTests +{ + private WireMockServer _server; + private HttpClient _httpClient; + private RawClient _rawClient; + private string _baseUrl; + + [SetUp] + public void SetUp() + { + _server = WireMockServer.Start(); + _baseUrl = _server.Url ?? ""; + _httpClient = new HttpClient { BaseAddress = new Uri(_baseUrl) }; + _rawClient = new RawClient( + new ClientOptions + { + HttpClient = _httpClient, + Headers = new Headers( + new Dictionary + { + ["a"] = "client_headers", + ["b"] = "client_headers", + ["c"] = "client_headers", + ["d"] = "client_headers", + ["e"] = "client_headers", + ["f"] = "client_headers", + ["client_multiple"] = "client_headers", + } + ), + AdditionalHeaders = new List> + { + new("b", "client_additional_headers"), + new("c", "client_additional_headers"), + new("d", "client_additional_headers"), + new("e", null), + new("client_multiple", "client_additional_headers1"), + new("client_multiple", "client_additional_headers2"), + }, + } + ); + } + + [Test] + public async SystemTask SendRequestAsync_AdditionalHeaderParameters() + { + _server + .Given(WireMockRequest.Create().WithPath("/test").UsingGet()) + .RespondWith(WireMockResponse.Create().WithStatusCode(200).WithBody("Success")); + + var request = new JsonRequest + { + BaseUrl = _baseUrl, + Method = HttpMethod.Get, + Path = "/test", + Headers = new Headers( + new Dictionary + { + ["c"] = "request_headers", + ["d"] = "request_headers", + ["request_multiple"] = "request_headers", + } + ), + Options = new RequestOptions + { + AdditionalHeaders = new List> + { + new("d", "request_additional_headers"), + new("f", null), + new("request_multiple", "request_additional_headers1"), + new("request_multiple", "request_additional_headers2"), + }, + }, + }; + + var response = await _rawClient.SendRequestAsync(request); + Assert.That(response.StatusCode, Is.EqualTo(200)); + + var content = await response.Raw.Content.ReadAsStringAsync(); + Assert.Multiple(() => + { + Assert.That(content, Is.EqualTo("Success")); + Assert.That(_server.LogEntries.Count, Is.EqualTo(1)); + var headers = + _server.LogEntries[0].RequestMessage.Headers ?? throw new global::System.Exception( + "Headers are null" + ); + + Assert.That(headers, Contains.Key("client_multiple")); + Assert.That(headers!["client_multiple"][0], Does.Contain("client_additional_headers1")); + Assert.That(headers["client_multiple"][0], Does.Contain("client_additional_headers2")); + + Assert.That(headers, Contains.Key("request_multiple")); + Assert.That( + headers["request_multiple"][0], + Does.Contain("request_additional_headers1") + ); + Assert.That( + headers["request_multiple"][0], + Does.Contain("request_additional_headers2") + ); + + Assert.That(headers, Contains.Key("a")); + Assert.That(headers["a"][0], Does.Contain("client_headers")); + + Assert.That(headers, Contains.Key("b")); + Assert.That(headers["b"][0], Does.Contain("client_additional_headers")); + + Assert.That(headers, Contains.Key("c")); + Assert.That(headers["c"][0], Does.Contain("request_headers")); + + Assert.That(headers, Contains.Key("d")); + Assert.That(headers["d"][0], Does.Contain("request_additional_headers")); + + Assert.That(headers, Does.Not.ContainKey("e")); + Assert.That(headers, Does.Not.ContainKey("f")); + }); + } + + [TearDown] + public void TearDown() + { + _server.Dispose(); + _httpClient.Dispose(); + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/RawClientTests/AdditionalParametersTests.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/RawClientTests/AdditionalParametersTests.cs new file mode 100644 index 000000000000..680b47af3d6b --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/RawClientTests/AdditionalParametersTests.cs @@ -0,0 +1,300 @@ +using NUnit.Framework; +using SeedInferredAuthImplicit.Core; +using WireMock.Matchers; +using WireMock.Server; +using SystemTask = global::System.Threading.Tasks.Task; +using WireMockRequest = WireMock.RequestBuilders.Request; +using WireMockResponse = WireMock.ResponseBuilders.Response; + +namespace SeedInferredAuthImplicit.Test.Core.RawClientTests; + +[TestFixture] +[Parallelizable(ParallelScope.Self)] +public class AdditionalParametersTests +{ + private WireMockServer _server; + private HttpClient _httpClient; + private RawClient _rawClient; + private string _baseUrl; + + [SetUp] + public void SetUp() + { + _server = WireMockServer.Start(); + _baseUrl = _server.Url ?? ""; + _httpClient = new HttpClient { BaseAddress = new Uri(_baseUrl) }; + _rawClient = new RawClient(new ClientOptions { HttpClient = _httpClient }); + } + + [Test] + public async SystemTask SendRequestAsync_AdditionalQueryParameters() + { + _server + .Given(WireMockRequest.Create().WithPath("/test").WithParam("foo", "bar").UsingGet()) + .RespondWith(WireMockResponse.Create().WithStatusCode(200).WithBody("Success")); + + var request = new JsonRequest() + { + BaseUrl = _baseUrl, + Method = HttpMethod.Get, + Path = "/test", + Options = new RequestOptions + { + AdditionalQueryParameters = new List> + { + new("foo", "bar"), + }, + }, + }; + + var response = await _rawClient.SendRequestAsync(request); + Assert.That(response.StatusCode, Is.EqualTo(200)); + + var content = await response.Raw.Content.ReadAsStringAsync(); + Assert.That(content, Is.EqualTo("Success")); + + Assert.That(_server.LogEntries.Count, Is.EqualTo(1)); + } + + [Test] + public async SystemTask SendRequestAsync_AdditionalQueryParameters_Override() + { + _server + .Given(WireMockRequest.Create().WithPath("/test").WithParam("foo", "null").UsingGet()) + .RespondWith(WireMockResponse.Create().WithStatusCode(200).WithBody("Success")); + + var request = new JsonRequest() + { + BaseUrl = _baseUrl, + Method = HttpMethod.Get, + Path = "/test", + Query = new Dictionary { { "foo", "bar" } }, + Options = new RequestOptions + { + AdditionalQueryParameters = new List> + { + new("foo", "null"), + }, + }, + }; + + var response = await _rawClient.SendRequestAsync(request); + Assert.That(response.StatusCode, Is.EqualTo(200)); + + var content = await response.Raw.Content.ReadAsStringAsync(); + Assert.That(content, Is.EqualTo("Success")); + + Assert.That(_server.LogEntries.Count, Is.EqualTo(1)); + } + + [Test] + public async SystemTask SendRequestAsync_AdditionalQueryParameters_Merge() + { + _server + .Given(WireMockRequest.Create().WithPath("/test").UsingGet()) + .RespondWith(WireMockResponse.Create().WithStatusCode(200).WithBody("Success")); + + var request = new JsonRequest() + { + BaseUrl = _baseUrl, + Method = HttpMethod.Get, + Path = "/test", + Query = new Dictionary { { "foo", "baz" } }, + Options = new RequestOptions + { + AdditionalQueryParameters = new List> + { + new("foo", "one"), + new("foo", "two"), + }, + }, + }; + + var response = await _rawClient.SendRequestAsync(request); + Assert.That(response.StatusCode, Is.EqualTo(200)); + + var content = await response.Raw.Content.ReadAsStringAsync(); + Assert.That(content, Is.EqualTo("Success")); + + Assert.That(_server.LogEntries.Count, Is.EqualTo(1)); + + var requestUrl = _server.LogEntries.First().RequestMessage.Url; + Assert.That(requestUrl, Does.Contain("foo=one")); + Assert.That(requestUrl, Does.Contain("foo=two")); + Assert.That(requestUrl, Does.Not.Contain("foo=baz")); + } + + [Test] + public async SystemTask SendRequestAsync_AdditionalBodyProperties() + { + string expectedBody = "{\n \"foo\": \"bar\",\n \"baz\": \"qux\"\n}"; + _server + .Given( + WireMockRequest + .Create() + .WithPath("/test") + .UsingPost() + .WithBody(new JsonMatcher(expectedBody)) + ) + .RespondWith(WireMockResponse.Create().WithStatusCode(200).WithBody("Success")); + + var request = new JsonRequest + { + BaseUrl = _baseUrl, + Method = HttpMethod.Post, + Path = "/test", + Body = new Dictionary { { "foo", "bar" } }, + Options = new RequestOptions + { + AdditionalBodyProperties = new Dictionary { { "baz", "qux" } }, + }, + }; + + var response = await _rawClient.SendRequestAsync(request); + Assert.That(response.StatusCode, Is.EqualTo(200)); + + var content = await response.Raw.Content.ReadAsStringAsync(); + Assert.That(content, Is.EqualTo("Success")); + + Assert.That(_server.LogEntries.Count, Is.EqualTo(1)); + } + + [Test] + public async SystemTask SendRequestAsync_AdditionalBodyProperties_Override() + { + string expectedBody = "{\n \"foo\": null\n}"; + _server + .Given( + WireMockRequest + .Create() + .WithPath("/test") + .UsingPost() + .WithBody(new JsonMatcher(expectedBody)) + ) + .RespondWith(WireMockResponse.Create().WithStatusCode(200).WithBody("Success")); + + var request = new JsonRequest + { + BaseUrl = _baseUrl, + Method = HttpMethod.Post, + Path = "/test", + Body = new Dictionary { { "foo", "bar" } }, + Options = new RequestOptions + { + AdditionalBodyProperties = new Dictionary { { "foo", null } }, + }, + }; + + var response = await _rawClient.SendRequestAsync(request); + Assert.That(response.StatusCode, Is.EqualTo(200)); + + var content = await response.Raw.Content.ReadAsStringAsync(); + Assert.That(content, Is.EqualTo("Success")); + + Assert.That(_server.LogEntries.Count, Is.EqualTo(1)); + } + + [Test] + public async SystemTask SendRequestAsync_AdditionalBodyProperties_DeepMerge() + { + const string expectedBody = """ + { + "foo": { + "inner1": "original", + "inner2": "overridden", + "inner3": { + "deepProp1": "deep-override", + "deepProp2": "original", + "deepProp3": null, + "deepProp4": "new-value" + } + }, + "bar": "new-value", + "baz": ["new","value"] + } + """; + + _server + .Given( + WireMockRequest + .Create() + .WithPath("/test-deep-merge") + .UsingPost() + .WithBody(new JsonMatcher(expectedBody)) + ) + .RespondWith(WireMockResponse.Create().WithStatusCode(200).WithBody("Success")); + + var request = new JsonRequest + { + BaseUrl = _baseUrl, + Method = HttpMethod.Post, + Path = "/test-deep-merge", + Body = new Dictionary + { + { + "foo", + new Dictionary + { + { "inner1", "original" }, + { "inner2", "original" }, + { + "inner3", + new Dictionary + { + { "deepProp1", "deep-original" }, + { "deepProp2", "original" }, + { "deepProp3", "" }, + } + }, + } + }, + { + "baz", + new List { "original" } + }, + }, + Options = new RequestOptions + { + AdditionalBodyProperties = new Dictionary + { + { + "foo", + new Dictionary + { + { "inner2", "overridden" }, + { + "inner3", + new Dictionary + { + { "deepProp1", "deep-override" }, + { "deepProp3", null }, + { "deepProp4", "new-value" }, + } + }, + } + }, + { "bar", "new-value" }, + { + "baz", + new List { "new", "value" } + }, + }, + }, + }; + + var response = await _rawClient.SendRequestAsync(request); + Assert.That(response.StatusCode, Is.EqualTo(200)); + + var content = await response.Raw.Content.ReadAsStringAsync(); + Assert.That(content, Is.EqualTo("Success")); + + Assert.That(_server.LogEntries.Count, Is.EqualTo(1)); + } + + [TearDown] + public void TearDown() + { + _server.Dispose(); + _httpClient.Dispose(); + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/RawClientTests/MultipartFormTests.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/RawClientTests/MultipartFormTests.cs new file mode 100644 index 000000000000..0ec24336f088 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/RawClientTests/MultipartFormTests.cs @@ -0,0 +1,1120 @@ +using global::System.Net.Http; +using global::System.Text; +using global::System.Text.Json.Serialization; +using NUnit.Framework; +using SeedInferredAuthImplicit.Core; +using SystemTask = global::System.Threading.Tasks.Task; + +namespace SeedInferredAuthImplicit.Test.Core.RawClientTests; + +[TestFixture] +[Parallelizable(ParallelScope.Self)] +public class MultipartFormTests +{ + private static SimpleObject _simpleObject = new(); + + private static string _simpleFormEncoded = + "meta=data&Date=2023-10-01&Time=12:00:00&Duration=01:00:00&Id=1a1bb98f-47c6-407b-9481-78476affe52a&IsActive=true&Count=42&Initial=A&Values=data,2023-10-01,12:00:00,01:00:00,1a1bb98f-47c6-407b-9481-78476affe52a,true,42,A"; + + private static string _simpleExplodedFormEncoded = + "meta=data&Date=2023-10-01&Time=12:00:00&Duration=01:00:00&Id=1a1bb98f-47c6-407b-9481-78476affe52a&IsActive=true&Count=42&Initial=A&Values=data&Values=2023-10-01&Values=12:00:00&Values=01:00:00&Values=1a1bb98f-47c6-407b-9481-78476affe52a&Values=true&Values=42&Values=A"; + + private static ComplexObject _complexObject = new(); + + private static string _complexJson = """ + { + "meta": "data", + "Nested": { + "foo": "value" + }, + "NestedDictionary": { + "key": { + "foo": "value" + } + }, + "ListOfObjects": [ + { + "foo": "value" + }, + { + "foo": "value2" + } + ], + "Date": "2023-10-01", + "Time": "12:00:00", + "Duration": "01:00:00", + "Id": "1a1bb98f-47c6-407b-9481-78476affe52a", + "IsActive": true, + "Count": 42, + "Initial": "A" + } + """; + + [Test] + public async SystemTask ShouldAddStringPart() + { + const string partInput = "string content"; + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddStringPart("string", partInput); + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: text/plain + Content-Disposition: form-data; name=string + + {partInput} + --{boundary}-- + """; + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddStringParts() + { + const string partInput = "string content"; + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddStringParts("strings", [partInput, partInput]); + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: text/plain + Content-Disposition: form-data; name=strings + + {partInput} + --{boundary} + Content-Type: text/plain + Content-Disposition: form-data; name=strings + + {partInput} + --{boundary}-- + """; + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask GivenNull_ShouldNotAddStringPart() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddStringPart("string", null); + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + --{boundary}-- + """; + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddStringParts_WithNullsInList() + { + const string partInput = "string content"; + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddStringParts("strings", [partInput, null, partInput]); + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: text/plain + Content-Disposition: form-data; name=strings + + {partInput} + --{boundary} + Content-Type: text/plain + Content-Disposition: form-data; name=strings + + {partInput} + --{boundary}-- + """; + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddStringPart_WithContentType() + { + const string partInput = "string content"; + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddStringPart("string", partInput, "text/xml"); + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: text/xml + Content-Disposition: form-data; name=string + + {partInput} + --{boundary}-- + """; + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddStringPart_WithContentTypeAndCharset() + { + const string partInput = "string content"; + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddStringPart("string", partInput, "text/xml; charset=utf-8"); + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: text/xml; charset=utf-8 + Content-Disposition: form-data; name=string + + {partInput} + --{boundary}-- + """; + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddStringParts_WithContentType() + { + const string partInput = "string content"; + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddStringParts("strings", [partInput, partInput], "text/xml"); + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: text/xml + Content-Disposition: form-data; name=strings + + {partInput} + --{boundary} + Content-Type: text/xml + Content-Disposition: form-data; name=strings + + {partInput} + --{boundary}-- + """; + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddStringParts_WithContentTypeAndCharset() + { + const string partInput = "string content"; + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddStringParts( + "strings", + [partInput, partInput], + "text/xml; charset=utf-8" + ); + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: text/xml; charset=utf-8 + Content-Disposition: form-data; name=strings + + {partInput} + --{boundary} + Content-Type: text/xml; charset=utf-8 + Content-Disposition: form-data; name=strings + + {partInput} + --{boundary}-- + """; + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddFileParameter_WithFileName() + { + var (partInput, partExpectedString) = GetFileParameterTestData(); + var file = new FileParameter { Stream = partInput, FileName = "test.txt" }; + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddFileParameterPart("file", file); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: application/octet-stream + Content-Disposition: form-data; name=file; filename=test.txt; filename*=utf-8''test.txt + + {partExpectedString} + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddFileParameter_WithoutFileName() + { + var (partInput, partExpectedString) = GetFileParameterTestData(); + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddFileParameterPart("file", partInput); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: application/octet-stream + Content-Disposition: form-data; name=file + + {partExpectedString} + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddFileParameter_WithContentType() + { + var (partInput, partExpectedString) = GetFileParameterTestData(); + var file = new FileParameter + { + Stream = partInput, + FileName = "test.txt", + ContentType = "text/plain", + }; + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddFileParameterPart("file", file, "ignored-fallback-content-type"); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: text/plain + Content-Disposition: form-data; name=file; filename=test.txt; filename*=utf-8''test.txt + + {partExpectedString} + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddFileParameter_WithContentTypeAndCharset() + { + var (partInput, partExpectedString) = GetFileParameterTestData(); + var file = new FileParameter + { + Stream = partInput, + FileName = "test.txt", + ContentType = "text/plain; charset=utf-8", + }; + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddFileParameterPart("file", file, "ignored-fallback-content-type"); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: text/plain; charset=utf-8 + Content-Disposition: form-data; name=file; filename=test.txt; filename*=utf-8''test.txt + + {partExpectedString} + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddFileParameter_WithFallbackContentType() + { + var (partInput, partExpectedString) = GetFileParameterTestData(); + var file = new FileParameter { Stream = partInput, FileName = "test.txt" }; + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddFileParameterPart("file", file, "text/plain"); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: text/plain + Content-Disposition: form-data; name=file; filename=test.txt; filename*=utf-8''test.txt + + {partExpectedString} + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddFileParameter_WithFallbackContentTypeAndCharset() + { + var (partInput, partExpectedString) = GetFileParameterTestData(); + var file = new FileParameter { Stream = partInput, FileName = "test.txt" }; + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddFileParameterPart("file", file, "text/plain; charset=utf-8"); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: text/plain; charset=utf-8 + Content-Disposition: form-data; name=file; filename=test.txt; filename*=utf-8''test.txt + + {partExpectedString} + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddFileParameters() + { + var (partInput1, partExpectedString1) = GetFileParameterTestData(); + var (partInput2, partExpectedString2) = GetFileParameterTestData(); + var file1 = new FileParameter { Stream = partInput1, FileName = "test1.txt" }; + var file2 = new FileParameter { Stream = partInput2, FileName = "test2.txt" }; + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddFileParameterParts("file", [file1, file2]); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: application/octet-stream + Content-Disposition: form-data; name=file; filename=test1.txt; filename*=utf-8''test1.txt + + {partExpectedString1} + --{boundary} + Content-Type: application/octet-stream + Content-Disposition: form-data; name=file; filename=test2.txt; filename*=utf-8''test2.txt + + {partExpectedString2} + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddFileParameters_WithNullsInList() + { + var (partInput1, partExpectedString1) = GetFileParameterTestData(); + var (partInput2, partExpectedString2) = GetFileParameterTestData(); + var file1 = new FileParameter { Stream = partInput1, FileName = "test1.txt" }; + var file2 = new FileParameter { Stream = partInput2, FileName = "test2.txt" }; + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddFileParameterParts("file", [file1, null, file2]); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: application/octet-stream + Content-Disposition: form-data; name=file; filename=test1.txt; filename*=utf-8''test1.txt + + {partExpectedString1} + --{boundary} + Content-Type: application/octet-stream + Content-Disposition: form-data; name=file; filename=test2.txt; filename*=utf-8''test2.txt + + {partExpectedString2} + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask GivenNull_ShouldNotAddFileParameter() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddFileParameterPart("file", null); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddJsonPart_WithComplexObject() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddJsonPart("object", _complexObject); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: application/json + Content-Disposition: form-data; name=object + + {_complexJson} + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddJsonPart_WithComplexObjectList() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddJsonParts("objects", [_complexObject, _complexObject]); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: application/json + Content-Disposition: form-data; name=objects + + {_complexJson} + --{boundary} + Content-Type: application/json + Content-Disposition: form-data; name=objects + + {_complexJson} + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask GivenNull_ShouldNotAddJsonPart() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddJsonPart("object", null); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddJsonParts_WithNullsInList() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddJsonParts("objects", [_complexObject, null]); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: application/json + Content-Disposition: form-data; name=objects + + {_complexJson} + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddJsonParts_WithContentType() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddJsonParts("objects", [new { }], "application/json-patch+json"); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $$""" + --{{boundary}} + Content-Type: application/json-patch+json + Content-Disposition: form-data; name=objects + + {} + --{{boundary}}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddFormEncodedParts_WithSimpleObject() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddFormEncodedPart("object", _simpleObject); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: application/x-www-form-urlencoded + Content-Disposition: form-data; name=object + + {EscapeFormEncodedString(_simpleFormEncoded)} + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddFormEncodedParts_WithSimpleObjectList() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddFormEncodedParts("objects", [_simpleObject, _simpleObject]); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: application/x-www-form-urlencoded + Content-Disposition: form-data; name=objects + + {EscapeFormEncodedString(_simpleFormEncoded)} + --{boundary} + Content-Type: application/x-www-form-urlencoded + Content-Disposition: form-data; name=objects + + {EscapeFormEncodedString(_simpleFormEncoded)} + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldNotAddFormEncodedParts_WithNull() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddFormEncodedParts("object", null); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldNotAddFormEncodedParts_WithNullsInList() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddFormEncodedParts("objects", [_simpleObject, null]); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: application/x-www-form-urlencoded + Content-Disposition: form-data; name=objects + + {EscapeFormEncodedString(_simpleFormEncoded)} + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddFormEncodedPart_WithContentType() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddFormEncodedPart( + "objects", + new { foo = "bar" }, + "application/x-www-form-urlencoded" + ); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: application/x-www-form-urlencoded + Content-Disposition: form-data; name=objects + + foo=bar + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddFormEncodedPart_WithContentTypeAndCharset() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddFormEncodedPart( + "objects", + new { foo = "bar" }, + "application/x-www-form-urlencoded; charset=utf-8" + ); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: application/x-www-form-urlencoded; charset=utf-8 + Content-Disposition: form-data; name=objects + + foo=bar + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddFormEncodedParts_WithContentType() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddFormEncodedParts( + "objects", + [new { foo = "bar" }], + "application/x-www-form-urlencoded" + ); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: application/x-www-form-urlencoded + Content-Disposition: form-data; name=objects + + foo=bar + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddFormEncodedParts_WithContentTypeAndCharset() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddFormEncodedParts( + "objects", + [new { foo = "bar" }], + "application/x-www-form-urlencoded; charset=utf-8" + ); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: application/x-www-form-urlencoded; charset=utf-8 + Content-Disposition: form-data; name=objects + + foo=bar + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddExplodedFormEncodedParts_WithSimpleObject() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddExplodedFormEncodedPart("object", _simpleObject); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: application/x-www-form-urlencoded + Content-Disposition: form-data; name=object + + {EscapeFormEncodedString(_simpleExplodedFormEncoded)} + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddExplodedFormEncodedParts_WithSimpleObjectList() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddExplodedFormEncodedParts("objects", [_simpleObject, _simpleObject]); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: application/x-www-form-urlencoded + Content-Disposition: form-data; name=objects + + {EscapeFormEncodedString(_simpleExplodedFormEncoded)} + --{boundary} + Content-Type: application/x-www-form-urlencoded + Content-Disposition: form-data; name=objects + + {EscapeFormEncodedString(_simpleExplodedFormEncoded)} + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldNotAddExplodedFormEncodedParts_WithNull() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddExplodedFormEncodedPart("object", null); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldNotAddExplodedFormEncodedParts_WithNullsInList() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddExplodedFormEncodedParts("objects", [_simpleObject, null]); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: application/x-www-form-urlencoded + Content-Disposition: form-data; name=objects + + {EscapeFormEncodedString(_simpleExplodedFormEncoded)} + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddExplodedFormEncodedPart_WithContentType() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddExplodedFormEncodedPart( + "objects", + new { foo = "bar" }, + "application/x-www-form-urlencoded" + ); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: application/x-www-form-urlencoded + Content-Disposition: form-data; name=objects + + foo=bar + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddExplodedFormEncodedPart_WithContentTypeAndCharset() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddExplodedFormEncodedPart( + "objects", + new { foo = "bar" }, + "application/x-www-form-urlencoded; charset=utf-8" + ); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: application/x-www-form-urlencoded; charset=utf-8 + Content-Disposition: form-data; name=objects + + foo=bar + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddExplodedFormEncodedParts_WithContentType() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddExplodedFormEncodedParts( + "objects", + [new { foo = "bar" }], + "application/x-www-form-urlencoded" + ); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: application/x-www-form-urlencoded + Content-Disposition: form-data; name=objects + + foo=bar + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + [Test] + public async SystemTask ShouldAddExplodedFormEncodedParts_WithContentTypeAndCharset() + { + var multipartFormRequest = CreateMultipartFormRequest(); + multipartFormRequest.AddExplodedFormEncodedParts( + "objects", + [new { foo = "bar" }], + "application/x-www-form-urlencoded; charset=utf-8" + ); + + var httpContent = multipartFormRequest.CreateContent(); + Assert.That(httpContent, Is.InstanceOf()); + var multipartContent = (MultipartFormDataContent)httpContent; + + var boundary = GetBoundary(multipartContent); + var expected = $""" + --{boundary} + Content-Type: application/x-www-form-urlencoded; charset=utf-8 + Content-Disposition: form-data; name=objects + + foo=bar + --{boundary}-- + """; + + var actual = await multipartContent.ReadAsStringAsync(); + Assert.That(actual, Is.EqualTo(expected).IgnoreWhiteSpace); + } + + private static string EscapeFormEncodedString(string input) + { + return string.Join( + "&", + input + .Split('&') + .Select(x => x.Split('=')) + .Select(x => $"{Uri.EscapeDataString(x[0])}={Uri.EscapeDataString(x[1])}") + ); + } + + private static string GetBoundary(MultipartFormDataContent content) + { + return content + .Headers.ContentType?.Parameters.Single(p => + p.Name.Equals("boundary", StringComparison.OrdinalIgnoreCase) + ) + .Value?.Trim('"') ?? throw new global::System.Exception("Boundary not found"); + } + + private static SeedInferredAuthImplicit.Core.MultipartFormRequest CreateMultipartFormRequest() + { + return new SeedInferredAuthImplicit.Core.MultipartFormRequest + { + BaseUrl = "https://localhost", + Method = HttpMethod.Post, + Path = "", + }; + } + + private static (Stream partInput, string partExpectedString) GetFileParameterTestData() + { + const string partExpectedString = "file content"; + var partInput = new MemoryStream(Encoding.Default.GetBytes(partExpectedString)); + return (partInput, partExpectedString); + } + + private class SimpleObject + { + [JsonPropertyName("meta")] + public string Meta { get; set; } = "data"; + public DateOnly Date { get; set; } = DateOnly.Parse("2023-10-01"); + public TimeOnly Time { get; set; } = TimeOnly.Parse("12:00:00"); + public TimeSpan Duration { get; set; } = TimeSpan.FromHours(1); + public Guid Id { get; set; } = Guid.Parse("1a1bb98f-47c6-407b-9481-78476affe52a"); + public bool IsActive { get; set; } = true; + public int Count { get; set; } = 42; + public char Initial { get; set; } = 'A'; + public IEnumerable Values { get; set; } = + [ + "data", + DateOnly.Parse("2023-10-01"), + TimeOnly.Parse("12:00:00"), + TimeSpan.FromHours(1), + Guid.Parse("1a1bb98f-47c6-407b-9481-78476affe52a"), + true, + 42, + 'A', + ]; + } + + private class ComplexObject + { + [JsonPropertyName("meta")] + public string Meta { get; set; } = "data"; + + public object Nested { get; set; } = new { foo = "value" }; + + public Dictionary NestedDictionary { get; set; } = + new() { { "key", new { foo = "value" } } }; + + public IEnumerable ListOfObjects { get; set; } = + new List { new { foo = "value" }, new { foo = "value2" } }; + + public DateOnly Date { get; set; } = DateOnly.Parse("2023-10-01"); + public TimeOnly Time { get; set; } = TimeOnly.Parse("12:00:00"); + public TimeSpan Duration { get; set; } = TimeSpan.FromHours(1); + public Guid Id { get; set; } = Guid.Parse("1a1bb98f-47c6-407b-9481-78476affe52a"); + public bool IsActive { get; set; } = true; + public int Count { get; set; } = 42; + public char Initial { get; set; } = 'A'; + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/RawClientTests/QueryParameterTests.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/RawClientTests/QueryParameterTests.cs new file mode 100644 index 000000000000..dcbe03c26c87 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/RawClientTests/QueryParameterTests.cs @@ -0,0 +1,64 @@ +using NUnit.Framework; +using SeedInferredAuthImplicit.Core; +using WireMock.Matchers; +using WireMock.Server; +using SystemTask = global::System.Threading.Tasks.Task; +using WireMockRequest = WireMock.RequestBuilders.Request; +using WireMockResponse = WireMock.ResponseBuilders.Response; + +namespace SeedInferredAuthImplicit.Test.Core.RawClientTests; + +[TestFixture] +[Parallelizable(ParallelScope.Self)] +public class QueryParameterTests +{ + private WireMockServer _server; + private HttpClient _httpClient; + private RawClient _rawClient; + private string _baseUrl; + + [SetUp] + public void SetUp() + { + _server = WireMockServer.Start(); + _baseUrl = _server.Url ?? ""; + _httpClient = new HttpClient { BaseAddress = new Uri(_baseUrl) }; + _rawClient = new RawClient(new ClientOptions { HttpClient = _httpClient }); + } + + [Test] + public async SystemTask CreateRequest_QueryParametersEscaping() + { + _server + .Given(WireMockRequest.Create().WithPath("/test").WithParam("foo", "bar").UsingGet()) + .RespondWith(WireMockResponse.Create().WithStatusCode(200).WithBody("Success")); + + var request = new JsonRequest() + { + BaseUrl = _baseUrl, + Method = HttpMethod.Get, + Path = "/test", + Query = new Dictionary + { + { "sample", "value" }, + { "email", "bob+test@example.com" }, + { "%Complete", "100" }, + }, + Options = new RequestOptions(), + }; + + var httpRequest = await _rawClient.CreateHttpRequestAsync(request).ConfigureAwait(false); + var url = httpRequest.RequestUri!.AbsoluteUri; + + Assert.That(url, Does.Contain("sample=value")); + Assert.That(url, Does.Contain("email=bob%2Btest%40example.com")); + Assert.That(url, Does.Contain("%25Complete=100")); + } + + [TearDown] + public void TearDown() + { + _server.Dispose(); + _httpClient.Dispose(); + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/RawClientTests/RetriesTests.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/RawClientTests/RetriesTests.cs new file mode 100644 index 000000000000..84b6dd67f15d --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Core/RawClientTests/RetriesTests.cs @@ -0,0 +1,327 @@ +using global::System.Net.Http; +using NUnit.Framework; +using SeedInferredAuthImplicit.Core; +using WireMock.Server; +using SystemTask = global::System.Threading.Tasks.Task; +using WireMockRequest = WireMock.RequestBuilders.Request; +using WireMockResponse = WireMock.ResponseBuilders.Response; + +namespace SeedInferredAuthImplicit.Test.Core.RawClientTests; + +[TestFixture] +[Parallelizable(ParallelScope.Self)] +public class RetriesTests +{ + private const int MaxRetries = 3; + private WireMockServer _server; + private HttpClient _httpClient; + private RawClient _rawClient; + private string _baseUrl; + + [SetUp] + public void SetUp() + { + _server = WireMockServer.Start(); + _baseUrl = _server.Url ?? ""; + _httpClient = new HttpClient { BaseAddress = new Uri(_baseUrl) }; + _rawClient = new RawClient( + new ClientOptions { HttpClient = _httpClient, MaxRetries = MaxRetries } + ) + { + BaseRetryDelay = 0, + }; + } + + [Test] + [TestCase(408)] + [TestCase(429)] + [TestCase(500)] + [TestCase(504)] + public async SystemTask SendRequestAsync_ShouldRetry_OnRetryableStatusCodes(int statusCode) + { + _server + .Given(WireMockRequest.Create().WithPath("/test").UsingGet()) + .InScenario("Retry") + .WillSetStateTo("Server Error") + .RespondWith(WireMockResponse.Create().WithStatusCode(statusCode)); + + _server + .Given(WireMockRequest.Create().WithPath("/test").UsingGet()) + .InScenario("Retry") + .WhenStateIs("Server Error") + .WillSetStateTo("Success") + .RespondWith(WireMockResponse.Create().WithStatusCode(statusCode)); + + _server + .Given(WireMockRequest.Create().WithPath("/test").UsingGet()) + .InScenario("Retry") + .WhenStateIs("Success") + .RespondWith(WireMockResponse.Create().WithStatusCode(200).WithBody("Success")); + + var request = new EmptyRequest + { + BaseUrl = _baseUrl, + Method = HttpMethod.Get, + Path = "/test", + }; + + var response = await _rawClient.SendRequestAsync(request); + Assert.That(response.StatusCode, Is.EqualTo(200)); + + var content = await response.Raw.Content.ReadAsStringAsync(); + using (Assert.EnterMultipleScope()) + { + Assert.That(content, Is.EqualTo("Success")); + + Assert.That(_server.LogEntries, Has.Count.EqualTo(MaxRetries)); + } + } + + [Test] + [TestCase(400)] + [TestCase(409)] + public async SystemTask SendRequestAsync_ShouldRetry_OnNonRetryableStatusCodes(int statusCode) + { + _server + .Given(WireMockRequest.Create().WithPath("/test").UsingGet()) + .InScenario("Retry") + .WillSetStateTo("Server Error") + .RespondWith(WireMockResponse.Create().WithStatusCode(statusCode).WithBody("Failure")); + + var request = new JsonRequest + { + BaseUrl = _baseUrl, + Method = HttpMethod.Get, + Path = "/test", + Body = new { }, + }; + + var response = await _rawClient.SendRequestAsync(request); + Assert.That(response.StatusCode, Is.EqualTo(statusCode)); + + var content = await response.Raw.Content.ReadAsStringAsync(); + Assert.Multiple(() => + { + Assert.That(content, Is.EqualTo("Failure")); + + Assert.That(_server.LogEntries, Has.Count.EqualTo(1)); + }); + } + + [Test] + public async SystemTask SendRequestAsync_ShouldNotRetry_WithStreamRequest() + { + _server + .Given(WireMockRequest.Create().WithPath("/test").UsingPost()) + .InScenario("Retry") + .WillSetStateTo("Server Error") + .RespondWith(WireMockResponse.Create().WithStatusCode(429).WithBody("Failure")); + + var request = new StreamRequest + { + BaseUrl = _baseUrl, + Method = HttpMethod.Post, + Path = "/test", + Body = new MemoryStream(), + }; + + var response = await _rawClient.SendRequestAsync(request); + Assert.That(response.StatusCode, Is.EqualTo(429)); + + var content = await response.Raw.Content.ReadAsStringAsync(); + Assert.Multiple(() => + { + Assert.That(content, Is.EqualTo("Failure")); + Assert.That(_server.LogEntries, Has.Count.EqualTo(1)); + }); + } + + [Test] + public async SystemTask SendRequestAsync_ShouldNotRetry_WithMultiPartFormRequest_WithStream() + { + _server + .Given(WireMockRequest.Create().WithPath("/test").UsingPost()) + .InScenario("Retry") + .WillSetStateTo("Server Error") + .RespondWith(WireMockResponse.Create().WithStatusCode(429).WithBody("Failure")); + + var request = new SeedInferredAuthImplicit.Core.MultipartFormRequest + { + BaseUrl = _baseUrl, + Method = HttpMethod.Post, + Path = "/test", + }; + request.AddFileParameterPart("file", new MemoryStream()); + + var response = await _rawClient.SendRequestAsync(request); + Assert.That(response.StatusCode, Is.EqualTo(429)); + + var content = await response.Raw.Content.ReadAsStringAsync(); + Assert.Multiple(() => + { + Assert.That(content, Is.EqualTo("Failure")); + Assert.That(_server.LogEntries, Has.Count.EqualTo(1)); + }); + } + + [Test] + public async SystemTask SendRequestAsync_ShouldRetry_WithMultiPartFormRequest_WithoutStream() + { + _server + .Given(WireMockRequest.Create().WithPath("/test").UsingPost()) + .InScenario("Retry") + .WillSetStateTo("Server Error") + .RespondWith(WireMockResponse.Create().WithStatusCode(429)); + + _server + .Given(WireMockRequest.Create().WithPath("/test").UsingPost()) + .InScenario("Retry") + .WhenStateIs("Server Error") + .WillSetStateTo("Success") + .RespondWith(WireMockResponse.Create().WithStatusCode(429)); + + _server + .Given(WireMockRequest.Create().WithPath("/test").UsingPost()) + .InScenario("Retry") + .WhenStateIs("Success") + .RespondWith(WireMockResponse.Create().WithStatusCode(200).WithBody("Success")); + + var request = new SeedInferredAuthImplicit.Core.MultipartFormRequest + { + BaseUrl = _baseUrl, + Method = HttpMethod.Post, + Path = "/test", + }; + request.AddJsonPart("object", new { }); + + var response = await _rawClient.SendRequestAsync(request); + Assert.That(response.StatusCode, Is.EqualTo(200)); + + var content = await response.Raw.Content.ReadAsStringAsync(); + Assert.Multiple(() => + { + Assert.That(content, Is.EqualTo("Success")); + Assert.That(_server.LogEntries, Has.Count.EqualTo(MaxRetries)); + }); + } + + [Test] + public async SystemTask SendRequestAsync_ShouldRespectRetryAfterHeader_WithSecondsValue() + { + _server + .Given(WireMockRequest.Create().WithPath("/test").UsingGet()) + .InScenario("RetryAfter") + .WillSetStateTo("Success") + .RespondWith( + WireMockResponse.Create().WithStatusCode(429).WithHeader("Retry-After", "1") + ); + + _server + .Given(WireMockRequest.Create().WithPath("/test").UsingGet()) + .InScenario("RetryAfter") + .WhenStateIs("Success") + .RespondWith(WireMockResponse.Create().WithStatusCode(200).WithBody("Success")); + + var request = new EmptyRequest + { + BaseUrl = _baseUrl, + Method = HttpMethod.Get, + Path = "/test", + }; + + var response = await _rawClient.SendRequestAsync(request); + Assert.That(response.StatusCode, Is.EqualTo(200)); + + var content = await response.Raw.Content.ReadAsStringAsync(); + Assert.Multiple(() => + { + Assert.That(content, Is.EqualTo("Success")); + Assert.That(_server.LogEntries, Has.Count.EqualTo(2)); + }); + } + + [Test] + public async SystemTask SendRequestAsync_ShouldRespectRetryAfterHeader_WithHttpDateValue() + { + var retryAfterDate = DateTimeOffset.UtcNow.AddSeconds(1).ToString("R"); + _server + .Given(WireMockRequest.Create().WithPath("/test").UsingGet()) + .InScenario("RetryAfterDate") + .WillSetStateTo("Success") + .RespondWith( + WireMockResponse + .Create() + .WithStatusCode(429) + .WithHeader("Retry-After", retryAfterDate) + ); + + _server + .Given(WireMockRequest.Create().WithPath("/test").UsingGet()) + .InScenario("RetryAfterDate") + .WhenStateIs("Success") + .RespondWith(WireMockResponse.Create().WithStatusCode(200).WithBody("Success")); + + var request = new EmptyRequest + { + BaseUrl = _baseUrl, + Method = HttpMethod.Get, + Path = "/test", + }; + + var response = await _rawClient.SendRequestAsync(request); + Assert.That(response.StatusCode, Is.EqualTo(200)); + + var content = await response.Raw.Content.ReadAsStringAsync(); + Assert.Multiple(() => + { + Assert.That(content, Is.EqualTo("Success")); + Assert.That(_server.LogEntries, Has.Count.EqualTo(2)); + }); + } + + [Test] + public async SystemTask SendRequestAsync_ShouldRespectXRateLimitResetHeader() + { + var resetTime = DateTimeOffset.UtcNow.AddSeconds(1).ToUnixTimeSeconds().ToString(); + _server + .Given(WireMockRequest.Create().WithPath("/test").UsingGet()) + .InScenario("RateLimitReset") + .WillSetStateTo("Success") + .RespondWith( + WireMockResponse + .Create() + .WithStatusCode(429) + .WithHeader("X-RateLimit-Reset", resetTime) + ); + + _server + .Given(WireMockRequest.Create().WithPath("/test").UsingGet()) + .InScenario("RateLimitReset") + .WhenStateIs("Success") + .RespondWith(WireMockResponse.Create().WithStatusCode(200).WithBody("Success")); + + var request = new EmptyRequest + { + BaseUrl = _baseUrl, + Method = HttpMethod.Get, + Path = "/test", + }; + + var response = await _rawClient.SendRequestAsync(request); + Assert.That(response.StatusCode, Is.EqualTo(200)); + + var content = await response.Raw.Content.ReadAsStringAsync(); + Assert.Multiple(() => + { + Assert.That(content, Is.EqualTo("Success")); + Assert.That(_server.LogEntries, Has.Count.EqualTo(2)); + }); + } + + [TearDown] + public void TearDown() + { + _server.Dispose(); + _httpClient.Dispose(); + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/SeedInferredAuthImplicit.Test.Custom.props b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/SeedInferredAuthImplicit.Test.Custom.props new file mode 100644 index 000000000000..aac9b5020d80 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/SeedInferredAuthImplicit.Test.Custom.props @@ -0,0 +1,6 @@ + + diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/SeedInferredAuthImplicit.Test.csproj b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/SeedInferredAuthImplicit.Test.csproj new file mode 100644 index 000000000000..ed2590573df5 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/SeedInferredAuthImplicit.Test.csproj @@ -0,0 +1,39 @@ + + + net8.0 + 12 + enable + enable + false + true + true + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/TestClient.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/TestClient.cs new file mode 100644 index 000000000000..3697606936d1 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/TestClient.cs @@ -0,0 +1,6 @@ +using NUnit.Framework; + +namespace SeedInferredAuthImplicit.Test; + +[TestFixture] +public class TestClient; diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Unit/MockServer/BaseMockServerTest.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Unit/MockServer/BaseMockServerTest.cs new file mode 100644 index 000000000000..c38d5caccb04 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Unit/MockServer/BaseMockServerTest.cs @@ -0,0 +1,78 @@ +using NUnit.Framework; +using SeedInferredAuthImplicit; +using WireMock.Logging; +using WireMock.Server; +using WireMock.Settings; + +namespace SeedInferredAuthImplicit.Test.Unit.MockServer; + +[SetUpFixture] +public class BaseMockServerTest +{ + protected static WireMockServer Server { get; set; } = null!; + + protected static SeedInferredAuthImplicitClient Client { get; set; } = null!; + + protected static RequestOptions RequestOptions { get; set; } = new(); + + private void MockInferredAuthEndpoint() + { + const string requestJson = """ + { + "client_id": "client_id", + "client_secret": "client_secret", + "audience": "https://api.example.com", + "grant_type": "client_credentials", + "scope": "scope" + } + """; + + const string mockResponse = """ + { + "access_token": "access_token", + "expires_in": 1, + "refresh_token": "refresh_token" + } + """; + + Server + .Given( + WireMock + .RequestBuilders.Request.Create() + .WithPath("/token") + .UsingPost() + .WithBodyAsJson(requestJson) + ) + .RespondWith( + WireMock + .ResponseBuilders.Response.Create() + .WithStatusCode(200) + .WithBody(mockResponse) + ); + } + + [OneTimeSetUp] + public void GlobalSetup() + { + // Start the WireMock server + Server = WireMockServer.Start( + new WireMockServerSettings { Logger = new WireMockConsoleLogger() } + ); + + // Initialize the Client + Client = new SeedInferredAuthImplicitClient( + "client_id", + "client_secret", + "scope", + clientOptions: new ClientOptions { BaseUrl = Server.Urls[0], MaxRetries = 0 } + ); + MockInferredAuthEndpoint(); + } + + [OneTimeTearDown] + public void GlobalTeardown() + { + Server.Stop(); + Server.Dispose(); + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Unit/MockServer/GetSomethingTest.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Unit/MockServer/GetSomethingTest.cs new file mode 100644 index 000000000000..6dc6fb501d0b --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Unit/MockServer/GetSomethingTest.cs @@ -0,0 +1,17 @@ +using NUnit.Framework; + +namespace SeedInferredAuthImplicit.Test.Unit.MockServer; + +[TestFixture] +public class GetSomethingTest : BaseMockServerTest +{ + [NUnit.Framework.Test] + public void MockServerTest() + { + Server + .Given(WireMock.RequestBuilders.Request.Create().WithPath("/get-something").UsingGet()) + .RespondWith(WireMock.ResponseBuilders.Response.Create().WithStatusCode(200)); + + Assert.DoesNotThrowAsync(async () => await Client.Simple.GetSomethingAsync()); + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Unit/MockServer/GetTokenWithClientCredentialsTest.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Unit/MockServer/GetTokenWithClientCredentialsTest.cs new file mode 100644 index 000000000000..4eae4795df89 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Unit/MockServer/GetTokenWithClientCredentialsTest.cs @@ -0,0 +1,61 @@ +using NUnit.Framework; +using SeedInferredAuthImplicit; +using SeedInferredAuthImplicit.Core; + +namespace SeedInferredAuthImplicit.Test.Unit.MockServer; + +[TestFixture] +public class GetTokenWithClientCredentialsTest : BaseMockServerTest +{ + [NUnit.Framework.Test] + public async Task MockServerTest() + { + const string requestJson = """ + { + "client_id": "client_id", + "client_secret": "client_secret", + "audience": "https://api.example.com", + "grant_type": "client_credentials", + "scope": "scope" + } + """; + + const string mockResponse = """ + { + "access_token": "access_token", + "expires_in": 1, + "refresh_token": "refresh_token" + } + """; + + Server + .Given( + WireMock + .RequestBuilders.Request.Create() + .WithPath("/token") + .UsingPost() + .WithBodyAsJson(requestJson) + ) + .RespondWith( + WireMock + .ResponseBuilders.Response.Create() + .WithStatusCode(200) + .WithBody(mockResponse) + ); + + var response = await Client.Auth.GetTokenWithClientCredentialsAsync( + new GetTokenRequest + { + ClientId = "client_id", + ClientSecret = "client_secret", + Audience = "https://api.example.com", + GrantType = "client_credentials", + Scope = "scope", + } + ); + Assert.That( + response, + Is.EqualTo(JsonUtils.Deserialize(mockResponse)).UsingDefaults() + ); + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Unit/MockServer/Nested/GetSomethingTest.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Unit/MockServer/Nested/GetSomethingTest.cs new file mode 100644 index 000000000000..46529d711d48 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Unit/MockServer/Nested/GetSomethingTest.cs @@ -0,0 +1,23 @@ +using NUnit.Framework; +using SeedInferredAuthImplicit.Test.Unit.MockServer; + +namespace SeedInferredAuthImplicit.Test.Unit.MockServer.Nested; + +[TestFixture] +public class GetSomethingTest : BaseMockServerTest +{ + [NUnit.Framework.Test] + public void MockServerTest() + { + Server + .Given( + WireMock + .RequestBuilders.Request.Create() + .WithPath("/nested/get-something") + .UsingGet() + ) + .RespondWith(WireMock.ResponseBuilders.Response.Create().WithStatusCode(200)); + + Assert.DoesNotThrowAsync(async () => await Client.Nested.Api.GetSomethingAsync()); + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Unit/MockServer/NestedNoAuth/GetSomethingTest.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Unit/MockServer/NestedNoAuth/GetSomethingTest.cs new file mode 100644 index 000000000000..df9c7fb67e46 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Unit/MockServer/NestedNoAuth/GetSomethingTest.cs @@ -0,0 +1,23 @@ +using NUnit.Framework; +using SeedInferredAuthImplicit.Test.Unit.MockServer; + +namespace SeedInferredAuthImplicit.Test.Unit.MockServer.NestedNoAuth; + +[TestFixture] +public class GetSomethingTest : BaseMockServerTest +{ + [NUnit.Framework.Test] + public void MockServerTest() + { + Server + .Given( + WireMock + .RequestBuilders.Request.Create() + .WithPath("/nested-no-auth/get-something") + .UsingGet() + ) + .RespondWith(WireMock.ResponseBuilders.Response.Create().WithStatusCode(200)); + + Assert.DoesNotThrowAsync(async () => await Client.NestedNoAuth.Api.GetSomethingAsync()); + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Unit/MockServer/RefreshTokenTest.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Unit/MockServer/RefreshTokenTest.cs new file mode 100644 index 000000000000..63569b67afee --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Unit/MockServer/RefreshTokenTest.cs @@ -0,0 +1,63 @@ +using NUnit.Framework; +using SeedInferredAuthImplicit; +using SeedInferredAuthImplicit.Core; + +namespace SeedInferredAuthImplicit.Test.Unit.MockServer; + +[TestFixture] +public class RefreshTokenTest : BaseMockServerTest +{ + [NUnit.Framework.Test] + public async Task MockServerTest() + { + const string requestJson = """ + { + "client_id": "client_id", + "client_secret": "client_secret", + "refresh_token": "refresh_token", + "audience": "https://api.example.com", + "grant_type": "refresh_token", + "scope": "scope" + } + """; + + const string mockResponse = """ + { + "access_token": "access_token", + "expires_in": 1, + "refresh_token": "refresh_token" + } + """; + + Server + .Given( + WireMock + .RequestBuilders.Request.Create() + .WithPath("/token/refresh") + .UsingPost() + .WithBodyAsJson(requestJson) + ) + .RespondWith( + WireMock + .ResponseBuilders.Response.Create() + .WithStatusCode(200) + .WithBody(mockResponse) + ); + + var response = await Client.Auth.RefreshTokenAsync( + new RefreshTokenRequest + { + ClientId = "client_id", + ClientSecret = "client_secret", + RefreshToken = "refresh_token", + Audience = "https://api.example.com", + GrantType = "refresh_token", + Scope = "scope", + } + ); + Assert.That( + response, + Is.EqualTo(JsonUtils.Deserialize(mockResponse)).UsingDefaults() + ); + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Utils/JsonElementComparer.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Utils/JsonElementComparer.cs new file mode 100644 index 000000000000..1704c99af443 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Utils/JsonElementComparer.cs @@ -0,0 +1,236 @@ +using System.Text.Json; +using NUnit.Framework.Constraints; + +namespace NUnit.Framework; + +/// +/// Extensions for EqualConstraint to handle JsonElement objects. +/// +public static class JsonElementComparerExtensions +{ + /// + /// Extension method for comparing JsonElement objects in NUnit tests. + /// Property order doesn't matter, but array order does matter. + /// Includes special handling for DateTime string formats. + /// + /// The Is.EqualTo() constraint instance. + /// A constraint that can compare JsonElements with detailed diffs. + public static EqualConstraint UsingJsonElementComparer(this EqualConstraint constraint) + { + return constraint.Using(new JsonElementComparer()); + } +} + +/// +/// Equality comparer for JsonElement with detailed reporting. +/// Property order doesn't matter, but array order does matter. +/// Now includes special handling for DateTime string formats with improved null handling. +/// +public class JsonElementComparer : IEqualityComparer +{ + private string _failurePath = string.Empty; + + /// + public bool Equals(JsonElement x, JsonElement y) + { + _failurePath = string.Empty; + return CompareJsonElements(x, y, string.Empty); + } + + /// + public int GetHashCode(JsonElement obj) + { + return JsonSerializer.Serialize(obj).GetHashCode(); + } + + private bool CompareJsonElements(JsonElement x, JsonElement y, string path) + { + // If value kinds don't match, they're not equivalent + if (x.ValueKind != y.ValueKind) + { + _failurePath = $"{path}: Expected {x.ValueKind} but got {y.ValueKind}"; + return false; + } + + switch (x.ValueKind) + { + case JsonValueKind.Object: + return CompareJsonObjects(x, y, path); + + case JsonValueKind.Array: + return CompareJsonArraysInOrder(x, y, path); + + case JsonValueKind.String: + string? xStr = x.GetString(); + string? yStr = y.GetString(); + + // Handle null strings + if (xStr is null && yStr is null) + return true; + + if (xStr is null || yStr is null) + { + _failurePath = + $"{path}: Expected {(xStr is null ? "null" : $"\"{xStr}\"")} but got {(yStr is null ? "null" : $"\"{yStr}\"")}"; + return false; + } + + // Check if they are identical strings + if (xStr == yStr) + return true; + + // Try to handle DateTime strings + if (IsLikelyDateTimeString(xStr) && IsLikelyDateTimeString(yStr)) + { + if (AreEquivalentDateTimeStrings(xStr, yStr)) + return true; + } + + _failurePath = $"{path}: Expected \"{xStr}\" but got \"{yStr}\""; + return false; + + case JsonValueKind.Number: + if (x.GetDecimal() != y.GetDecimal()) + { + _failurePath = $"{path}: Expected {x.GetDecimal()} but got {y.GetDecimal()}"; + return false; + } + + return true; + + case JsonValueKind.True: + case JsonValueKind.False: + if (x.GetBoolean() != y.GetBoolean()) + { + _failurePath = $"{path}: Expected {x.GetBoolean()} but got {y.GetBoolean()}"; + return false; + } + + return true; + + case JsonValueKind.Null: + return true; + + default: + _failurePath = $"{path}: Unsupported JsonValueKind {x.ValueKind}"; + return false; + } + } + + private bool IsLikelyDateTimeString(string? str) + { + // Simple heuristic to identify likely ISO date time strings + return str != null + && (str.Contains("T") && (str.EndsWith("Z") || str.Contains("+") || str.Contains("-"))); + } + + private bool AreEquivalentDateTimeStrings(string str1, string str2) + { + // Try to parse both as DateTime + if (DateTime.TryParse(str1, out DateTime dt1) && DateTime.TryParse(str2, out DateTime dt2)) + { + return dt1 == dt2; + } + + return false; + } + + private bool CompareJsonObjects(JsonElement x, JsonElement y, string path) + { + // Create dictionaries for both JSON objects + var xProps = new Dictionary(); + var yProps = new Dictionary(); + + foreach (var prop in x.EnumerateObject()) + xProps[prop.Name] = prop.Value; + + foreach (var prop in y.EnumerateObject()) + yProps[prop.Name] = prop.Value; + + // Check if all properties in x exist in y + foreach (var key in xProps.Keys) + { + if (!yProps.ContainsKey(key)) + { + _failurePath = $"{path}: Missing property '{key}'"; + return false; + } + } + + // Check if y has extra properties + foreach (var key in yProps.Keys) + { + if (!xProps.ContainsKey(key)) + { + _failurePath = $"{path}: Unexpected property '{key}'"; + return false; + } + } + + // Compare each property value + foreach (var key in xProps.Keys) + { + var propPath = string.IsNullOrEmpty(path) ? key : $"{path}.{key}"; + if (!CompareJsonElements(xProps[key], yProps[key], propPath)) + { + return false; + } + } + + return true; + } + + private bool CompareJsonArraysInOrder(JsonElement x, JsonElement y, string path) + { + var xArray = x.EnumerateArray(); + var yArray = y.EnumerateArray(); + + // Count x elements + var xCount = 0; + var xElements = new List(); + foreach (var item in xArray) + { + xElements.Add(item); + xCount++; + } + + // Count y elements + var yCount = 0; + var yElements = new List(); + foreach (var item in yArray) + { + yElements.Add(item); + yCount++; + } + + // Check if counts match + if (xCount != yCount) + { + _failurePath = $"{path}: Expected {xCount} items but found {yCount}"; + return false; + } + + // Compare elements in order + for (var i = 0; i < xCount; i++) + { + var itemPath = $"{path}[{i}]"; + if (!CompareJsonElements(xElements[i], yElements[i], itemPath)) + { + return false; + } + } + + return true; + } + + /// + public override string ToString() + { + if (!string.IsNullOrEmpty(_failurePath)) + { + return $"JSON comparison failed at {_failurePath}"; + } + + return "JsonElementEqualityComparer"; + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Utils/NUnitExtensions.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Utils/NUnitExtensions.cs new file mode 100644 index 000000000000..426df1245388 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Utils/NUnitExtensions.cs @@ -0,0 +1,28 @@ +using NUnit.Framework.Constraints; + +namespace NUnit.Framework; + +/// +/// Extensions for NUnit constraints. +/// +public static class NUnitExtensions +{ + /// + /// Modifies the EqualConstraint to use our own set of default comparers. + /// + /// + /// + public static EqualConstraint UsingDefaults(this EqualConstraint constraint) => + constraint + .UsingPropertiesComparer() + .UsingReadOnlyMemoryComparer() + .UsingReadOnlyMemoryComparer() + .UsingReadOnlyMemoryComparer() + .UsingReadOnlyMemoryComparer() + .UsingReadOnlyMemoryComparer() + .UsingReadOnlyMemoryComparer() + .UsingReadOnlyMemoryComparer() + .UsingReadOnlyMemoryComparer() + .UsingOneOfComparer() + .UsingJsonElementComparer(); +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Utils/OneOfComparer.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Utils/OneOfComparer.cs new file mode 100644 index 000000000000..0c975b471ff3 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Utils/OneOfComparer.cs @@ -0,0 +1,43 @@ +using NUnit.Framework.Constraints; +using OneOf; + +namespace NUnit.Framework; + +/// +/// Extensions for EqualConstraint to handle OneOf values. +/// +public static class EqualConstraintExtensions +{ + /// + /// Modifies the EqualConstraint to handle OneOf instances by comparing their inner values. + /// This works alongside other comparison modifiers like UsingPropertiesComparer. + /// + /// The EqualConstraint to modify. + /// The same constraint instance for method chaining. + public static EqualConstraint UsingOneOfComparer(this EqualConstraint constraint) + { + // Register a comparer factory for IOneOf types + constraint.Using( + (x, y) => + { + // ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract + if (x.Value is null && y.Value is null) + { + return true; + } + + if (x.Value is null) + { + return false; + } + + var propertiesComparer = new NUnitEqualityComparer(); + var tolerance = Tolerance.Default; + propertiesComparer.CompareProperties = true; + return propertiesComparer.AreEqual(x.Value, y.Value, ref tolerance); + } + ); + + return constraint; + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Utils/ReadOnlyMemoryComparer.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Utils/ReadOnlyMemoryComparer.cs new file mode 100644 index 000000000000..fc0b595a5e54 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit.Test/Utils/ReadOnlyMemoryComparer.cs @@ -0,0 +1,87 @@ +using NUnit.Framework.Constraints; + +namespace NUnit.Framework; + +/// +/// Extensions for NUnit constraints. +/// +public static class ReadOnlyMemoryComparerExtensions +{ + /// + /// Extension method for comparing ReadOnlyMemory<T> in NUnit tests. + /// + /// The type of elements in the ReadOnlyMemory. + /// The Is.EqualTo() constraint instance. + /// A constraint that can compare ReadOnlyMemory<T>. + public static EqualConstraint UsingReadOnlyMemoryComparer(this EqualConstraint constraint) + where T : IComparable + { + return constraint.Using(new ReadOnlyMemoryComparer()); + } +} + +/// +/// Comparer for ReadOnlyMemory<T>. Compares sequences by value. +/// +/// +/// The type of elements in the ReadOnlyMemory. +/// +public class ReadOnlyMemoryComparer : IComparer> + where T : IComparable +{ + /// + public int Compare(ReadOnlyMemory x, ReadOnlyMemory y) + { + // Check if sequences are equal + var xSpan = x.Span; + var ySpan = y.Span; + + // Optimized case for IEquatable implementations + if (typeof(IEquatable).IsAssignableFrom(typeof(T))) + { + var areEqual = xSpan.SequenceEqual(ySpan); + if (areEqual) + { + return 0; // Sequences are equal + } + } + else + { + // Manual equality check for non-IEquatable types + if (xSpan.Length == ySpan.Length) + { + var areEqual = true; + for (var i = 0; i < xSpan.Length; i++) + { + if (!EqualityComparer.Default.Equals(xSpan[i], ySpan[i])) + { + areEqual = false; + break; + } + } + + if (areEqual) + { + return 0; // Sequences are equal + } + } + } + + // For non-equal sequences, we need to return a consistent ordering + // First compare lengths + if (x.Length != y.Length) + return x.Length.CompareTo(y.Length); + + // Same length but different content - compare first differing element + for (var i = 0; i < x.Length; i++) + { + if (!EqualityComparer.Default.Equals(xSpan[i], ySpan[i])) + { + return xSpan[i].CompareTo(ySpan[i]); + } + } + + // Should never reach here if not equal + return 0; + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Auth/AuthClient.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Auth/AuthClient.cs new file mode 100644 index 000000000000..17befe890e58 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Auth/AuthClient.cs @@ -0,0 +1,123 @@ +using System.Text.Json; +using SeedInferredAuthImplicit.Core; + +namespace SeedInferredAuthImplicit; + +public partial class AuthClient +{ + private RawClient _client; + + internal AuthClient(RawClient client) + { + _client = client; + } + + /// + /// await client.Auth.GetTokenWithClientCredentialsAsync( + /// new GetTokenRequest + /// { + /// ClientId = "client_id", + /// ClientSecret = "client_secret", + /// Audience = "https://api.example.com", + /// GrantType = "client_credentials", + /// Scope = "scope", + /// } + /// ); + /// + public async Task GetTokenWithClientCredentialsAsync( + GetTokenRequest request, + RequestOptions? options = null, + CancellationToken cancellationToken = default + ) + { + var response = await _client + .SendRequestAsync( + new JsonRequest + { + BaseUrl = _client.Options.BaseUrl, + Method = HttpMethod.Post, + Path = "/token", + Body = request, + Options = options, + }, + cancellationToken + ) + .ConfigureAwait(false); + if (response.StatusCode is >= 200 and < 400) + { + var responseBody = await response.Raw.Content.ReadAsStringAsync(); + try + { + return JsonUtils.Deserialize(responseBody)!; + } + catch (JsonException e) + { + throw new SeedInferredAuthImplicitException("Failed to deserialize response", e); + } + } + + { + var responseBody = await response.Raw.Content.ReadAsStringAsync(); + throw new SeedInferredAuthImplicitApiException( + $"Error with status code {response.StatusCode}", + response.StatusCode, + responseBody + ); + } + } + + /// + /// await client.Auth.RefreshTokenAsync( + /// new RefreshTokenRequest + /// { + /// ClientId = "client_id", + /// ClientSecret = "client_secret", + /// RefreshToken = "refresh_token", + /// Audience = "https://api.example.com", + /// GrantType = "refresh_token", + /// Scope = "scope", + /// } + /// ); + /// + public async Task RefreshTokenAsync( + RefreshTokenRequest request, + RequestOptions? options = null, + CancellationToken cancellationToken = default + ) + { + var response = await _client + .SendRequestAsync( + new JsonRequest + { + BaseUrl = _client.Options.BaseUrl, + Method = HttpMethod.Post, + Path = "/token/refresh", + Body = request, + Options = options, + }, + cancellationToken + ) + .ConfigureAwait(false); + if (response.StatusCode is >= 200 and < 400) + { + var responseBody = await response.Raw.Content.ReadAsStringAsync(); + try + { + return JsonUtils.Deserialize(responseBody)!; + } + catch (JsonException e) + { + throw new SeedInferredAuthImplicitException("Failed to deserialize response", e); + } + } + + { + var responseBody = await response.Raw.Content.ReadAsStringAsync(); + throw new SeedInferredAuthImplicitApiException( + $"Error with status code {response.StatusCode}", + response.StatusCode, + responseBody + ); + } + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Auth/Types/GetTokenRequest.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Auth/Types/GetTokenRequest.cs new file mode 100644 index 000000000000..a926b4442797 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Auth/Types/GetTokenRequest.cs @@ -0,0 +1,43 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using SeedInferredAuthImplicit.Core; + +namespace SeedInferredAuthImplicit; + +/// +/// A request to obtain an OAuth token. +/// +[Serializable] +public record GetTokenRequest : IJsonOnDeserialized +{ + [JsonExtensionData] + private readonly IDictionary _extensionData = + new Dictionary(); + + [JsonPropertyName("client_id")] + public required string ClientId { get; set; } + + [JsonPropertyName("client_secret")] + public required string ClientSecret { get; set; } + + [JsonPropertyName("audience")] + public string Audience { get; set; } = "https://api.example.com"; + + [JsonPropertyName("grant_type")] + public string GrantType { get; set; } = "client_credentials"; + + [JsonPropertyName("scope")] + public string? Scope { get; set; } + + [JsonIgnore] + public ReadOnlyAdditionalProperties AdditionalProperties { get; private set; } = new(); + + void IJsonOnDeserialized.OnDeserialized() => + AdditionalProperties.CopyFromExtensionData(_extensionData); + + /// + public override string ToString() + { + return JsonUtils.Serialize(this); + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Auth/Types/RefreshTokenRequest.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Auth/Types/RefreshTokenRequest.cs new file mode 100644 index 000000000000..f99d014e4b8e --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Auth/Types/RefreshTokenRequest.cs @@ -0,0 +1,46 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using SeedInferredAuthImplicit.Core; + +namespace SeedInferredAuthImplicit; + +/// +/// A request to refresh an OAuth token. +/// +[Serializable] +public record RefreshTokenRequest : IJsonOnDeserialized +{ + [JsonExtensionData] + private readonly IDictionary _extensionData = + new Dictionary(); + + [JsonPropertyName("client_id")] + public required string ClientId { get; set; } + + [JsonPropertyName("client_secret")] + public required string ClientSecret { get; set; } + + [JsonPropertyName("refresh_token")] + public required string RefreshToken { get; set; } + + [JsonPropertyName("audience")] + public string Audience { get; set; } = "https://api.example.com"; + + [JsonPropertyName("grant_type")] + public string GrantType { get; set; } = "refresh_token"; + + [JsonPropertyName("scope")] + public string? Scope { get; set; } + + [JsonIgnore] + public ReadOnlyAdditionalProperties AdditionalProperties { get; private set; } = new(); + + void IJsonOnDeserialized.OnDeserialized() => + AdditionalProperties.CopyFromExtensionData(_extensionData); + + /// + public override string ToString() + { + return JsonUtils.Serialize(this); + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Auth/Types/TokenResponse.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Auth/Types/TokenResponse.cs new file mode 100644 index 000000000000..8755bd2bb3be --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Auth/Types/TokenResponse.cs @@ -0,0 +1,37 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using SeedInferredAuthImplicit.Core; + +namespace SeedInferredAuthImplicit; + +/// +/// An OAuth token response. +/// +[Serializable] +public record TokenResponse : IJsonOnDeserialized +{ + [JsonExtensionData] + private readonly IDictionary _extensionData = + new Dictionary(); + + [JsonPropertyName("access_token")] + public required string AccessToken { get; set; } + + [JsonPropertyName("expires_in")] + public required int ExpiresIn { get; set; } + + [JsonPropertyName("refresh_token")] + public string? RefreshToken { get; set; } + + [JsonIgnore] + public ReadOnlyAdditionalProperties AdditionalProperties { get; private set; } = new(); + + void IJsonOnDeserialized.OnDeserialized() => + AdditionalProperties.CopyFromExtensionData(_extensionData); + + /// + public override string ToString() + { + return JsonUtils.Serialize(this); + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/ApiResponse.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/ApiResponse.cs new file mode 100644 index 000000000000..7d59e50b35b6 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/ApiResponse.cs @@ -0,0 +1,13 @@ +using System.Net.Http; + +namespace SeedInferredAuthImplicit.Core; + +/// +/// The response object returned from the API. +/// +internal record ApiResponse +{ + internal required int StatusCode { get; init; } + + internal required HttpResponseMessage Raw { get; init; } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/BaseRequest.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/BaseRequest.cs new file mode 100644 index 000000000000..27f88b76c3c4 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/BaseRequest.cs @@ -0,0 +1,63 @@ +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; + +namespace SeedInferredAuthImplicit.Core; + +internal abstract record BaseRequest +{ + internal required string BaseUrl { get; init; } + + internal required HttpMethod Method { get; init; } + + internal required string Path { get; init; } + + internal string? ContentType { get; init; } + + internal Dictionary Query { get; init; } = new(); + + internal Headers Headers { get; init; } = new(); + + internal IRequestOptions? Options { get; init; } + + internal abstract HttpContent? CreateContent(); + + protected static ( + Encoding encoding, + string? charset, + string mediaType + ) ParseContentTypeOrDefault( + string? contentType, + Encoding encodingFallback, + string mediaTypeFallback + ) + { + var encoding = encodingFallback; + var mediaType = mediaTypeFallback; + string? charset = null; + if (string.IsNullOrEmpty(contentType)) + { + return (encoding, charset, mediaType); + } + + if (!MediaTypeHeaderValue.TryParse(contentType, out var mediaTypeHeaderValue)) + { + return (encoding, charset, mediaType); + } + + if (!string.IsNullOrEmpty(mediaTypeHeaderValue.CharSet)) + { + charset = mediaTypeHeaderValue.CharSet; + encoding = Encoding.GetEncoding(mediaTypeHeaderValue.CharSet); + } + + if (!string.IsNullOrEmpty(mediaTypeHeaderValue.MediaType)) + { + mediaType = mediaTypeHeaderValue.MediaType; + } + + return (encoding, charset, mediaType); + } + + protected static Encoding Utf8NoBom => EncodingCache.Utf8NoBom; +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/CollectionItemSerializer.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/CollectionItemSerializer.cs new file mode 100644 index 000000000000..55e220e5e4e1 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/CollectionItemSerializer.cs @@ -0,0 +1,89 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace SeedInferredAuthImplicit.Core; + +/// +/// Json collection converter. +/// +/// Type of item to convert. +/// Converter to use for individual items. +internal class CollectionItemSerializer + : JsonConverter> + where TConverterType : JsonConverter +{ + /// + /// Reads a json string and deserializes it into an object. + /// + /// Json reader. + /// Type to convert. + /// Serializer options. + /// Created object. + public override IEnumerable? Read( + ref Utf8JsonReader reader, + global::System.Type typeToConvert, + JsonSerializerOptions options + ) + { + if (reader.TokenType == JsonTokenType.Null) + { + return default; + } + + var jsonSerializerOptions = new JsonSerializerOptions(options); + jsonSerializerOptions.Converters.Clear(); + jsonSerializerOptions.Converters.Add(Activator.CreateInstance()); + + var returnValue = new List(); + + while (reader.TokenType != JsonTokenType.EndArray) + { + if (reader.TokenType != JsonTokenType.StartArray) + { + var item = (TDatatype)( + JsonSerializer.Deserialize(ref reader, typeof(TDatatype), jsonSerializerOptions) + ?? throw new global::System.Exception( + $"Failed to deserialize collection item of type {typeof(TDatatype)}" + ) + ); + returnValue.Add(item); + } + + reader.Read(); + } + + return returnValue; + } + + /// + /// Writes a json string. + /// + /// Json writer. + /// Value to write. + /// Serializer options. + public override void Write( + Utf8JsonWriter writer, + IEnumerable? value, + JsonSerializerOptions options + ) + { + if (value == null) + { + writer.WriteNullValue(); + return; + } + + var jsonSerializerOptions = new JsonSerializerOptions(options); + jsonSerializerOptions.Converters.Clear(); + jsonSerializerOptions.Converters.Add(Activator.CreateInstance()); + + writer.WriteStartArray(); + + foreach (var data in value) + { + JsonSerializer.Serialize(writer, data, jsonSerializerOptions); + } + + writer.WriteEndArray(); + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/Constants.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/Constants.cs new file mode 100644 index 000000000000..4319fec065f3 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/Constants.cs @@ -0,0 +1,7 @@ +namespace SeedInferredAuthImplicit.Core; + +internal static class Constants +{ + public const string DateTimeFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.fffK"; + public const string DateFormat = "yyyy-MM-dd"; +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/DateOnlyConverter.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/DateOnlyConverter.cs new file mode 100644 index 000000000000..3a454faac096 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/DateOnlyConverter.cs @@ -0,0 +1,747 @@ +// ReSharper disable All +#pragma warning disable + +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using global::System.Diagnostics; +using global::System.Diagnostics.CodeAnalysis; +using global::System.Globalization; +using global::System.Runtime.CompilerServices; +using global::System.Runtime.InteropServices; +using global::System.Text.Json; +using global::System.Text.Json.Serialization; + +// ReSharper disable SuggestVarOrType_SimpleTypes +// ReSharper disable SuggestVarOrType_BuiltInTypes + +namespace SeedInferredAuthImplicit.Core +{ + /// + /// Custom converter for handling the data type with the System.Text.Json library. + /// + /// + /// This class backported from: + /// + /// System.Text.Json.Serialization.Converters.DateOnlyConverter + /// + public sealed class DateOnlyConverter : JsonConverter + { + private const int FormatLength = 10; // YYYY-MM-DD + + private const int MaxEscapedFormatLength = + FormatLength * JsonConstants.MaxExpansionFactorWhileEscaping; + + /// + public override DateOnly Read( + ref Utf8JsonReader reader, + global::System.Type typeToConvert, + JsonSerializerOptions options + ) + { + if (reader.TokenType != JsonTokenType.String) + { + ThrowHelper.ThrowInvalidOperationException_ExpectedString(reader.TokenType); + } + + return ReadCore(ref reader); + } + + /// + public override DateOnly ReadAsPropertyName( + ref Utf8JsonReader reader, + global::System.Type typeToConvert, + JsonSerializerOptions options + ) + { + Debug.Assert(reader.TokenType == JsonTokenType.PropertyName); + return ReadCore(ref reader); + } + + private static DateOnly ReadCore(ref Utf8JsonReader reader) + { + if ( + !JsonHelpers.IsInRangeInclusive( + reader.ValueLength(), + FormatLength, + MaxEscapedFormatLength + ) + ) + { + ThrowHelper.ThrowFormatException(DataType.DateOnly); + } + + scoped ReadOnlySpan source; + if (!reader.HasValueSequence && !reader.ValueIsEscaped) + { + source = reader.ValueSpan; + } + else + { + Span stackSpan = stackalloc byte[MaxEscapedFormatLength]; + int bytesWritten = reader.CopyString(stackSpan); + source = stackSpan.Slice(0, bytesWritten); + } + + if (!JsonHelpers.TryParseAsIso(source, out DateOnly value)) + { + ThrowHelper.ThrowFormatException(DataType.DateOnly); + } + + return value; + } + + /// + public override void Write( + Utf8JsonWriter writer, + DateOnly value, + JsonSerializerOptions options + ) + { +#if NET8_0_OR_GREATER + Span buffer = stackalloc byte[FormatLength]; +#else + Span buffer = stackalloc char[FormatLength]; +#endif + // ReSharper disable once RedundantAssignment + bool formattedSuccessfully = value.TryFormat( + buffer, + out int charsWritten, + "O".AsSpan(), + CultureInfo.InvariantCulture + ); + Debug.Assert(formattedSuccessfully && charsWritten == FormatLength); + writer.WriteStringValue(buffer); + } + + /// + public override void WriteAsPropertyName( + Utf8JsonWriter writer, + DateOnly value, + JsonSerializerOptions options + ) + { +#if NET8_0_OR_GREATER + Span buffer = stackalloc byte[FormatLength]; +#else + Span buffer = stackalloc char[FormatLength]; +#endif + // ReSharper disable once RedundantAssignment + bool formattedSuccessfully = value.TryFormat( + buffer, + out int charsWritten, + "O".AsSpan(), + CultureInfo.InvariantCulture + ); + Debug.Assert(formattedSuccessfully && charsWritten == FormatLength); + writer.WritePropertyName(buffer); + } + } + + internal static class JsonConstants + { + // The maximum number of fraction digits the Json DateTime parser allows + public const int DateTimeParseNumFractionDigits = 16; + + // In the worst case, an ASCII character represented as a single utf-8 byte could expand 6x when escaped. + public const int MaxExpansionFactorWhileEscaping = 6; + + // The largest fraction expressible by TimeSpan and DateTime formats + public const int MaxDateTimeFraction = 9_999_999; + + // TimeSpan and DateTime formats allow exactly up to many digits for specifying the fraction after the seconds. + public const int DateTimeNumFractionDigits = 7; + + public const byte UtcOffsetToken = (byte)'Z'; + + public const byte TimePrefix = (byte)'T'; + + public const byte Period = (byte)'.'; + + public const byte Hyphen = (byte)'-'; + + public const byte Colon = (byte)':'; + + public const byte Plus = (byte)'+'; + } + + // ReSharper disable SuggestVarOrType_Elsewhere + // ReSharper disable SuggestVarOrType_SimpleTypes + // ReSharper disable SuggestVarOrType_BuiltInTypes + + internal static class JsonHelpers + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsInRangeInclusive(int value, int lowerBound, int upperBound) => + (uint)(value - lowerBound) <= (uint)(upperBound - lowerBound); + + public static bool IsDigit(byte value) => (uint)(value - '0') <= '9' - '0'; + + [StructLayout(LayoutKind.Auto)] + private struct DateTimeParseData + { + public int Year; + public int Month; + public int Day; + public bool IsCalendarDateOnly; + public int Hour; + public int Minute; + public int Second; + public int Fraction; // This value should never be greater than 9_999_999. + public int OffsetHours; + public int OffsetMinutes; + + // ReSharper disable once NotAccessedField.Local + public byte OffsetToken; + } + + public static bool TryParseAsIso(ReadOnlySpan source, out DateOnly value) + { + if ( + TryParseDateTimeOffset(source, out DateTimeParseData parseData) + && parseData.IsCalendarDateOnly + && TryCreateDateTime(parseData, DateTimeKind.Unspecified, out DateTime dateTime) + ) + { + value = DateOnly.FromDateTime(dateTime); + return true; + } + + value = default; + return false; + } + + /// + /// ISO 8601 date time parser (ISO 8601-1:2019). + /// + /// The date/time to parse in UTF-8 format. + /// The parsed for the given . + /// + /// Supports extended calendar date (5.2.2.1) and complete (5.4.2.1) calendar date/time of day + /// representations with optional specification of seconds and fractional seconds. + /// + /// Times can be explicitly specified as UTC ("Z" - 5.3.3) or offsets from UTC ("+/-hh:mm" 5.3.4.2). + /// If unspecified they are considered to be local per spec. + /// + /// Examples: (TZD is either "Z" or hh:mm offset from UTC) + /// + /// YYYY-MM-DD (e.g. 1997-07-16) + /// YYYY-MM-DDThh:mm (e.g. 1997-07-16T19:20) + /// YYYY-MM-DDThh:mm:ss (e.g. 1997-07-16T19:20:30) + /// YYYY-MM-DDThh:mm:ss.s (e.g. 1997-07-16T19:20:30.45) + /// YYYY-MM-DDThh:mmTZD (e.g. 1997-07-16T19:20+01:00) + /// YYYY-MM-DDThh:mm:ssTZD (e.g. 1997-07-16T19:20:3001:00) + /// YYYY-MM-DDThh:mm:ss.sTZD (e.g. 1997-07-16T19:20:30.45Z) + /// + /// Generally speaking we always require the "extended" option when one exists (3.1.3.5). + /// The extended variants have separator characters between components ('-', ':', '.', etc.). + /// Spaces are not permitted. + /// + /// "true" if successfully parsed. + private static bool TryParseDateTimeOffset( + ReadOnlySpan source, + out DateTimeParseData parseData + ) + { + parseData = default; + + // too short datetime + Debug.Assert(source.Length >= 10); + + // Parse the calendar date + // ----------------------- + // ISO 8601-1:2019 5.2.2.1b "Calendar date complete extended format" + // [dateX] = [year]["-"][month]["-"][day] + // [year] = [YYYY] [0000 - 9999] (4.3.2) + // [month] = [MM] [01 - 12] (4.3.3) + // [day] = [DD] [01 - 28, 29, 30, 31] (4.3.4) + // + // Note: 5.2.2.2 "Representations with reduced precision" allows for + // just [year]["-"][month] (a) and just [year] (b), but we currently + // don't permit it. + + { + uint digit1 = source[0] - (uint)'0'; + uint digit2 = source[1] - (uint)'0'; + uint digit3 = source[2] - (uint)'0'; + uint digit4 = source[3] - (uint)'0'; + + if (digit1 > 9 || digit2 > 9 || digit3 > 9 || digit4 > 9) + { + return false; + } + + parseData.Year = (int)(digit1 * 1000 + digit2 * 100 + digit3 * 10 + digit4); + } + + if ( + source[4] != JsonConstants.Hyphen + || !TryGetNextTwoDigits(source.Slice(start: 5, length: 2), ref parseData.Month) + || source[7] != JsonConstants.Hyphen + || !TryGetNextTwoDigits(source.Slice(start: 8, length: 2), ref parseData.Day) + ) + { + return false; + } + + // We now have YYYY-MM-DD [dateX] + // ReSharper disable once ConvertIfStatementToSwitchStatement + if (source.Length == 10) + { + parseData.IsCalendarDateOnly = true; + return true; + } + + // Parse the time of day + // --------------------- + // + // ISO 8601-1:2019 5.3.1.2b "Local time of day complete extended format" + // [timeX] = ["T"][hour][":"][min][":"][sec] + // [hour] = [hh] [00 - 23] (4.3.8a) + // [minute] = [mm] [00 - 59] (4.3.9a) + // [sec] = [ss] [00 - 59, 60 with a leap second] (4.3.10a) + // + // ISO 8601-1:2019 5.3.3 "UTC of day" + // [timeX]["Z"] + // + // ISO 8601-1:2019 5.3.4.2 "Local time of day with the time shift between + // local timescale and UTC" (Extended format) + // + // [shiftX] = ["+"|"-"][hour][":"][min] + // + // Notes: + // + // "T" is optional per spec, but _only_ when times are used alone. In our + // case, we're reading out a complete date & time and as such require "T". + // (5.4.2.1b). + // + // For [timeX] We allow seconds to be omitted per 5.3.1.3a "Representations + // with reduced precision". 5.3.1.3b allows just specifying the hour, but + // we currently don't permit this. + // + // Decimal fractions are allowed for hours, minutes and seconds (5.3.14). + // We only allow fractions for seconds currently. Lower order components + // can't follow, i.e. you can have T23.3, but not T23.3:04. There must be + // one digit, but the max number of digits is implementation defined. We + // currently allow up to 16 digits of fractional seconds only. While we + // support 16 fractional digits we only parse the first seven, anything + // past that is considered a zero. This is to stay compatible with the + // DateTime implementation which is limited to this resolution. + + if (source.Length < 16) + { + // Source does not have enough characters for YYYY-MM-DDThh:mm + return false; + } + + // Parse THH:MM (e.g. "T10:32") + if ( + source[10] != JsonConstants.TimePrefix + || source[13] != JsonConstants.Colon + || !TryGetNextTwoDigits(source.Slice(start: 11, length: 2), ref parseData.Hour) + || !TryGetNextTwoDigits(source.Slice(start: 14, length: 2), ref parseData.Minute) + ) + { + return false; + } + + // We now have YYYY-MM-DDThh:mm + Debug.Assert(source.Length >= 16); + if (source.Length == 16) + { + return true; + } + + byte curByte = source[16]; + int sourceIndex = 17; + + // Either a TZD ['Z'|'+'|'-'] or a seconds separator [':'] is valid at this point + switch (curByte) + { + case JsonConstants.UtcOffsetToken: + parseData.OffsetToken = JsonConstants.UtcOffsetToken; + return sourceIndex == source.Length; + case JsonConstants.Plus: + case JsonConstants.Hyphen: + parseData.OffsetToken = curByte; + return ParseOffset(ref parseData, source.Slice(sourceIndex)); + case JsonConstants.Colon: + break; + default: + return false; + } + + // Try reading the seconds + if ( + source.Length < 19 + || !TryGetNextTwoDigits(source.Slice(start: 17, length: 2), ref parseData.Second) + ) + { + return false; + } + + // We now have YYYY-MM-DDThh:mm:ss + Debug.Assert(source.Length >= 19); + if (source.Length == 19) + { + return true; + } + + curByte = source[19]; + sourceIndex = 20; + + // Either a TZD ['Z'|'+'|'-'] or a seconds decimal fraction separator ['.'] is valid at this point + switch (curByte) + { + case JsonConstants.UtcOffsetToken: + parseData.OffsetToken = JsonConstants.UtcOffsetToken; + return sourceIndex == source.Length; + case JsonConstants.Plus: + case JsonConstants.Hyphen: + parseData.OffsetToken = curByte; + return ParseOffset(ref parseData, source.Slice(sourceIndex)); + case JsonConstants.Period: + break; + default: + return false; + } + + // Source does not have enough characters for second fractions (i.e. ".s") + // YYYY-MM-DDThh:mm:ss.s + if (source.Length < 21) + { + return false; + } + + // Parse fraction. This value should never be greater than 9_999_999 + int numDigitsRead = 0; + int fractionEnd = Math.Min( + sourceIndex + JsonConstants.DateTimeParseNumFractionDigits, + source.Length + ); + + while (sourceIndex < fractionEnd && IsDigit(curByte = source[sourceIndex])) + { + if (numDigitsRead < JsonConstants.DateTimeNumFractionDigits) + { + parseData.Fraction = parseData.Fraction * 10 + (int)(curByte - (uint)'0'); + numDigitsRead++; + } + + sourceIndex++; + } + + if (parseData.Fraction != 0) + { + while (numDigitsRead < JsonConstants.DateTimeNumFractionDigits) + { + parseData.Fraction *= 10; + numDigitsRead++; + } + } + + // We now have YYYY-MM-DDThh:mm:ss.s + Debug.Assert(sourceIndex <= source.Length); + if (sourceIndex == source.Length) + { + return true; + } + + curByte = source[sourceIndex++]; + + // TZD ['Z'|'+'|'-'] is valid at this point + switch (curByte) + { + case JsonConstants.UtcOffsetToken: + parseData.OffsetToken = JsonConstants.UtcOffsetToken; + return sourceIndex == source.Length; + case JsonConstants.Plus: + case JsonConstants.Hyphen: + parseData.OffsetToken = curByte; + return ParseOffset(ref parseData, source.Slice(sourceIndex)); + default: + return false; + } + + static bool ParseOffset(ref DateTimeParseData parseData, ReadOnlySpan offsetData) + { + // Parse the hours for the offset + if ( + offsetData.Length < 2 + || !TryGetNextTwoDigits(offsetData.Slice(0, 2), ref parseData.OffsetHours) + ) + { + return false; + } + + // We now have YYYY-MM-DDThh:mm:ss.s+|-hh + + if (offsetData.Length == 2) + { + // Just hours offset specified + return true; + } + + // Ensure we have enough for ":mm" + return offsetData.Length == 5 + && offsetData[2] == JsonConstants.Colon + && TryGetNextTwoDigits(offsetData.Slice(3), ref parseData.OffsetMinutes); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + // ReSharper disable once RedundantAssignment + private static bool TryGetNextTwoDigits(ReadOnlySpan source, ref int value) + { + Debug.Assert(source.Length == 2); + + uint digit1 = source[0] - (uint)'0'; + uint digit2 = source[1] - (uint)'0'; + + if (digit1 > 9 || digit2 > 9) + { + value = 0; + return false; + } + + value = (int)(digit1 * 10 + digit2); + return true; + } + + // The following methods are borrowed verbatim from src/Common/src/CoreLib/System/Buffers/Text/Utf8Parser/Utf8Parser.Date.Helpers.cs + + /// + /// Overflow-safe DateTime factory. + /// + private static bool TryCreateDateTime( + DateTimeParseData parseData, + DateTimeKind kind, + out DateTime value + ) + { + if (parseData.Year == 0) + { + value = default; + return false; + } + + Debug.Assert(parseData.Year <= 9999); // All of our callers to date parse the year from fixed 4-digit fields so this value is trusted. + + if ((uint)parseData.Month - 1 >= 12) + { + value = default; + return false; + } + + uint dayMinusOne = (uint)parseData.Day - 1; + if ( + dayMinusOne >= 28 + && dayMinusOne >= DateTime.DaysInMonth(parseData.Year, parseData.Month) + ) + { + value = default; + return false; + } + + if ((uint)parseData.Hour > 23) + { + value = default; + return false; + } + + if ((uint)parseData.Minute > 59) + { + value = default; + return false; + } + + // This needs to allow leap seconds when appropriate. + // See https://github.com/dotnet/runtime/issues/30135. + if ((uint)parseData.Second > 59) + { + value = default; + return false; + } + + Debug.Assert(parseData.Fraction is >= 0 and <= JsonConstants.MaxDateTimeFraction); // All of our callers to date parse the fraction from fixed 7-digit fields so this value is trusted. + + ReadOnlySpan days = DateTime.IsLeapYear(parseData.Year) + ? DaysToMonth366 + : DaysToMonth365; + int yearMinusOne = parseData.Year - 1; + int totalDays = + yearMinusOne * 365 + + yearMinusOne / 4 + - yearMinusOne / 100 + + yearMinusOne / 400 + + days[parseData.Month - 1] + + parseData.Day + - 1; + long ticks = totalDays * TimeSpan.TicksPerDay; + int totalSeconds = parseData.Hour * 3600 + parseData.Minute * 60 + parseData.Second; + ticks += totalSeconds * TimeSpan.TicksPerSecond; + ticks += parseData.Fraction; + value = new DateTime(ticks: ticks, kind: kind); + return true; + } + + private static ReadOnlySpan DaysToMonth365 => + [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365]; + private static ReadOnlySpan DaysToMonth366 => + [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366]; + } + + internal static class ThrowHelper + { + private const string ExceptionSourceValueToRethrowAsJsonException = + "System.Text.Json.Rethrowable"; + + [DoesNotReturn] + public static void ThrowInvalidOperationException_ExpectedString(JsonTokenType tokenType) + { + throw GetInvalidOperationException("string", tokenType); + } + + public static void ThrowFormatException(DataType dataType) + { + throw new FormatException(SR.Format(SR.UnsupportedFormat, dataType)) + { + Source = ExceptionSourceValueToRethrowAsJsonException, + }; + } + + private static global::System.Exception GetInvalidOperationException( + string message, + JsonTokenType tokenType + ) + { + return GetInvalidOperationException(SR.Format(SR.InvalidCast, tokenType, message)); + } + + private static InvalidOperationException GetInvalidOperationException(string message) + { + return new InvalidOperationException(message) + { + Source = ExceptionSourceValueToRethrowAsJsonException, + }; + } + } + + internal static class Utf8JsonReaderExtensions + { + internal static int ValueLength(this Utf8JsonReader reader) => + reader.HasValueSequence + ? checked((int)reader.ValueSequence.Length) + : reader.ValueSpan.Length; + } + + internal enum DataType + { + TimeOnly, + DateOnly, + } + + [SuppressMessage("ReSharper", "InconsistentNaming")] + internal static class SR + { + private static readonly bool s_usingResourceKeys = + AppContext.TryGetSwitch( + "System.Resources.UseSystemResourceKeys", + out bool usingResourceKeys + ) && usingResourceKeys; + + public static string UnsupportedFormat => Strings.UnsupportedFormat; + + public static string InvalidCast => Strings.InvalidCast; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static string Format(string resourceFormat, object? p1) => + s_usingResourceKeys + ? string.Join(", ", resourceFormat, p1) + : string.Format(resourceFormat, p1); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static string Format(string resourceFormat, object? p1, object? p2) => + s_usingResourceKeys + ? string.Join(", ", resourceFormat, p1, p2) + : string.Format(resourceFormat, p1, p2); + } + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute( + "System.Resources.Tools.StronglyTypedResourceBuilder", + "17.0.0.0" + )] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Strings + { + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute( + "Microsoft.Performance", + "CA1811:AvoidUncalledPrivateCode" + )] + internal Strings() { } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute( + global::System.ComponentModel.EditorBrowsableState.Advanced + )] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if (object.ReferenceEquals(resourceMan, null)) + { + global::System.Resources.ResourceManager temp = + new global::System.Resources.ResourceManager( + "System.Text.Json.Resources.Strings", + typeof(Strings).Assembly + ); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute( + global::System.ComponentModel.EditorBrowsableState.Advanced + )] + internal static global::System.Globalization.CultureInfo Culture + { + get { return resourceCulture; } + set { resourceCulture = value; } + } + + /// + /// Looks up a localized string similar to Cannot get the value of a token type '{0}' as a {1}.. + /// + internal static string InvalidCast + { + get { return ResourceManager.GetString("InvalidCast", resourceCulture); } + } + + /// + /// Looks up a localized string similar to The JSON value is not in a supported {0} format.. + /// + internal static string UnsupportedFormat + { + get { return ResourceManager.GetString("UnsupportedFormat", resourceCulture); } + } + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/DateTimeSerializer.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/DateTimeSerializer.cs new file mode 100644 index 000000000000..a0551441c402 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/DateTimeSerializer.cs @@ -0,0 +1,22 @@ +using System.Globalization; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace SeedInferredAuthImplicit.Core; + +internal class DateTimeSerializer : JsonConverter +{ + public override DateTime Read( + ref Utf8JsonReader reader, + global::System.Type typeToConvert, + JsonSerializerOptions options + ) + { + return DateTime.Parse(reader.GetString()!, null, DateTimeStyles.RoundtripKind); + } + + public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.ToString(Constants.DateTimeFormat)); + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/EmptyRequest.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/EmptyRequest.cs new file mode 100644 index 000000000000..b3a5ff32fc04 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/EmptyRequest.cs @@ -0,0 +1,11 @@ +using System.Net.Http; + +namespace SeedInferredAuthImplicit.Core; + +/// +/// The request object to send without a request body. +/// +internal record EmptyRequest : BaseRequest +{ + internal override HttpContent? CreateContent() => null; +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/EncodingCache.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/EncodingCache.cs new file mode 100644 index 000000000000..c760e7735eb1 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/EncodingCache.cs @@ -0,0 +1,11 @@ +using System.Text; + +namespace SeedInferredAuthImplicit.Core; + +internal static class EncodingCache +{ + internal static readonly Encoding Utf8NoBom = new UTF8Encoding( + encoderShouldEmitUTF8Identifier: false, + throwOnInvalidBytes: true + ); +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/Extensions.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/Extensions.cs new file mode 100644 index 000000000000..6d8c83ed4869 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/Extensions.cs @@ -0,0 +1,55 @@ +using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization; + +namespace SeedInferredAuthImplicit.Core; + +internal static class Extensions +{ + public static string Stringify(this Enum value) + { + var field = value.GetType().GetField(value.ToString()); + if (field != null) + { + var attribute = (EnumMemberAttribute?) + global::System.Attribute.GetCustomAttribute(field, typeof(EnumMemberAttribute)); + return attribute?.Value ?? value.ToString(); + } + return value.ToString(); + } + + /// + /// Asserts that a condition is true, throwing an exception with the specified message if it is false. + /// + /// The condition to assert. + /// The exception message if the assertion fails. + /// Thrown when the condition is false. + internal static void Assert(this object value, bool condition, string message) + { + if (!condition) + { + throw new global::System.Exception(message); + } + } + + /// + /// Asserts that a value is not null, throwing an exception with the specified message if it is null. + /// + /// The type of the value to assert. + /// The value to assert is not null. + /// The exception message if the assertion fails. + /// The non-null value. + /// Thrown when the value is null. + internal static TValue Assert( + this object _unused, + [NotNull] TValue? value, + string message + ) + where TValue : class + { + if (value == null) + { + throw new global::System.Exception(message); + } + return value; + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/FormUrlEncoder.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/FormUrlEncoder.cs new file mode 100644 index 000000000000..95c6e9f2615a --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/FormUrlEncoder.cs @@ -0,0 +1,33 @@ +using global::System.Net.Http; + +namespace SeedInferredAuthImplicit.Core; + +/// +/// Encodes an object into a form URL-encoded content. +/// +public static class FormUrlEncoder +{ + /// + /// Encodes an object into a form URL-encoded content using Deep Object notation. + /// + /// Object to form URL-encode. You can pass in an object or dictionary, but not lists, strings, or primitives. + /// Throws when passing in a list, a string, or a primitive value. + internal static FormUrlEncodedContent EncodeAsDeepObject(object value) => + new(QueryStringConverter.ToDeepObject(value)); + + /// + /// Encodes an object into a form URL-encoded content using Exploded Form notation. + /// + /// Object to form URL-encode. You can pass in an object or dictionary, but not lists, strings, or primitives. + /// Throws when passing in a list, a string, or a primitive value. + internal static FormUrlEncodedContent EncodeAsExplodedForm(object value) => + new(QueryStringConverter.ToExplodedForm(value)); + + /// + /// Encodes an object into a form URL-encoded content using Form notation without exploding parameters. + /// + /// Object to form URL-encode. You can pass in an object or dictionary, but not lists, strings, or primitives. + /// Throws when passing in a list, a string, or a primitive value. + internal static FormUrlEncodedContent EncodeAsForm(object value) => + new(QueryStringConverter.ToForm(value)); +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/HeaderValue.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/HeaderValue.cs new file mode 100644 index 000000000000..74b154edc8d7 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/HeaderValue.cs @@ -0,0 +1,41 @@ +using OneOf; + +namespace SeedInferredAuthImplicit.Core; + +internal sealed class HeaderValue( + OneOf< + string, + Func, + Func>, + Func> + > value +) + : OneOfBase< + string, + Func, + Func>, + Func> + >(value) +{ + public static implicit operator HeaderValue(string value) => new(value); + + public static implicit operator HeaderValue(Func value) => new(value); + + public static implicit operator HeaderValue( + Func> value + ) => new(value); + + public static implicit operator HeaderValue( + Func> value + ) => new(value); + + internal global::System.Threading.Tasks.ValueTask ResolveAsync() + { + return Match( + str => new global::System.Threading.Tasks.ValueTask(str), + syncFunc => new global::System.Threading.Tasks.ValueTask(syncFunc()), + valueTaskFunc => valueTaskFunc(), + taskFunc => new global::System.Threading.Tasks.ValueTask(taskFunc()) + ); + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/Headers.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/Headers.cs new file mode 100644 index 000000000000..1f6c94707695 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/Headers.cs @@ -0,0 +1,28 @@ +namespace SeedInferredAuthImplicit.Core; + +/// +/// Represents the headers sent with the request. +/// +internal sealed class Headers : Dictionary +{ + internal Headers() { } + + /// + /// Initializes a new instance of the Headers class with the specified value. + /// + /// + internal Headers(Dictionary value) + { + foreach (var kvp in value) + { + this[kvp.Key] = new HeaderValue(kvp.Value); + } + } + + /// + /// Initializes a new instance of the Headers class with the specified value. + /// + /// + internal Headers(IEnumerable> value) + : base(value.ToDictionary(e => e.Key, e => e.Value)) { } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/HttpMethodExtensions.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/HttpMethodExtensions.cs new file mode 100644 index 000000000000..cbf083f3b9bd --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/HttpMethodExtensions.cs @@ -0,0 +1,8 @@ +using System.Net.Http; + +namespace SeedInferredAuthImplicit.Core; + +internal static class HttpMethodExtensions +{ + public static readonly HttpMethod Patch = new("PATCH"); +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/IIsRetryableContent.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/IIsRetryableContent.cs new file mode 100644 index 000000000000..c937a393328d --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/IIsRetryableContent.cs @@ -0,0 +1,6 @@ +namespace SeedInferredAuthImplicit.Core; + +public interface IIsRetryableContent +{ + public bool IsRetryable { get; } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/IRequestOptions.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/IRequestOptions.cs new file mode 100644 index 000000000000..528d1c778b6c --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/IRequestOptions.cs @@ -0,0 +1,88 @@ +namespace SeedInferredAuthImplicit.Core; + +internal interface IRequestOptions +{ + /// + /// The Base URL for the API. + /// + public string? BaseUrl { get; +#if NET5_0_OR_GREATER + init; +#else + set; +#endif + } + + /// + /// The http client used to make requests. + /// + public HttpClient? HttpClient { get; +#if NET5_0_OR_GREATER + init; +#else + set; +#endif + } + + /// + /// The http headers sent with the request. + /// + internal Headers Headers { get; init; } + + /// + /// Additional headers to be sent with the request. + /// Headers previously set with matching keys will be overwritten. + /// + public IEnumerable> AdditionalHeaders { get; +#if NET5_0_OR_GREATER + init; +#else + set; +#endif + } + + /// + /// The http client used to make requests. + /// + public int? MaxRetries { get; +#if NET5_0_OR_GREATER + init; +#else + set; +#endif + } + + /// + /// The timeout for the request. + /// + public TimeSpan? Timeout { get; +#if NET5_0_OR_GREATER + init; +#else + set; +#endif + } + + /// + /// Additional query parameters sent with the request. + /// + public IEnumerable> AdditionalQueryParameters { get; +#if NET5_0_OR_GREATER + init; +#else + set; +#endif + } + + /// + /// Additional body properties sent with the request. + /// This is only applied to JSON requests. + /// + public object? AdditionalBodyProperties { get; +#if NET5_0_OR_GREATER + init; +#else + set; +#endif + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/InferredAuthTokenProvider.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/InferredAuthTokenProvider.cs new file mode 100644 index 000000000000..ecc6014b7413 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/InferredAuthTokenProvider.cs @@ -0,0 +1,78 @@ +using SeedInferredAuthImplicit; + +namespace SeedInferredAuthImplicit.Core; + +internal partial class InferredAuthTokenProvider +{ + private const double BufferInMinutes = 2; + + private AuthClient _client; + + private IDictionary? _cachedHeaders; + + private DateTime? _expiresAt; + + private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1); + + private string _clientId; + + private string _clientSecret; + + private string? _scope; + + internal InferredAuthTokenProvider( + string clientId, + string clientSecret, + string? scope, + AuthClient client + ) + { + _clientId = clientId; + _clientSecret = clientSecret; + _scope = scope; + _client = client; + } + + internal async Task> GetAuthHeadersAsync() + { + if (_cachedHeaders == null || DateTime.UtcNow >= _expiresAt) + { + await _lock.WaitAsync().ConfigureAwait(false); + try + { + if (_cachedHeaders == null || DateTime.UtcNow >= _expiresAt) + { + try + { + var tokenResponse = await _client + .GetTokenWithClientCredentialsAsync( + new GetTokenRequest + { + ClientId = _clientId, + ClientSecret = _clientSecret, + Scope = _scope, + } + ) + .ConfigureAwait(false); + _cachedHeaders = new Dictionary(); + _cachedHeaders["Authorization"] = $"Bearer {tokenResponse.AccessToken}"; + _expiresAt = DateTime + .UtcNow.AddSeconds(tokenResponse.ExpiresIn) + .AddMinutes(-BufferInMinutes); + } + catch + { + _cachedHeaders = null; + _expiresAt = null; + throw; + } + } + } + finally + { + _lock.Release(); + } + } + return _cachedHeaders; + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/JsonAccessAttribute.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/JsonAccessAttribute.cs new file mode 100644 index 000000000000..a7b16ebab1e8 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/JsonAccessAttribute.cs @@ -0,0 +1,15 @@ +namespace SeedInferredAuthImplicit.Core; + +[global::System.AttributeUsage( + global::System.AttributeTargets.Property | global::System.AttributeTargets.Field +)] +internal class JsonAccessAttribute(JsonAccessType accessType) : global::System.Attribute +{ + internal JsonAccessType AccessType { get; init; } = accessType; +} + +internal enum JsonAccessType +{ + ReadOnly, + WriteOnly, +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/JsonConfiguration.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/JsonConfiguration.cs new file mode 100644 index 000000000000..deed11ff0ce1 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/JsonConfiguration.cs @@ -0,0 +1,180 @@ +using global::System.Reflection; +using global::System.Text.Json; +using global::System.Text.Json.Nodes; +using global::System.Text.Json.Serialization; +using global::System.Text.Json.Serialization.Metadata; + +namespace SeedInferredAuthImplicit.Core; + +internal static partial class JsonOptions +{ + internal static readonly JsonSerializerOptions JsonSerializerOptions; + + static JsonOptions() + { + var options = new JsonSerializerOptions + { + Converters = { new DateTimeSerializer(), +#if USE_PORTABLE_DATE_ONLY + new DateOnlyConverter(), +#endif + new OneOfSerializer() }, +#if DEBUG + WriteIndented = true, +#endif + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + TypeInfoResolver = new DefaultJsonTypeInfoResolver + { + Modifiers = + { + static typeInfo => + { + if (typeInfo.Kind != JsonTypeInfoKind.Object) + return; + + foreach (var propertyInfo in typeInfo.Properties) + { + var jsonAccessAttribute = propertyInfo + .AttributeProvider?.GetCustomAttributes( + typeof(JsonAccessAttribute), + true + ) + .OfType() + .FirstOrDefault(); + + if (jsonAccessAttribute != null) + { + propertyInfo.IsRequired = false; + switch (jsonAccessAttribute.AccessType) + { + case JsonAccessType.ReadOnly: + propertyInfo.ShouldSerialize = (_, _) => false; + break; + case JsonAccessType.WriteOnly: + propertyInfo.Set = null; + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + + var jsonIgnoreAttribute = propertyInfo + .AttributeProvider?.GetCustomAttributes( + typeof(JsonIgnoreAttribute), + true + ) + .OfType() + .FirstOrDefault(); + + if (jsonIgnoreAttribute is not null) + { + propertyInfo.IsRequired = false; + } + } + + if ( + typeInfo.Kind == JsonTypeInfoKind.Object + && typeInfo.Properties.All(prop => !prop.IsExtensionData) + ) + { + var extensionProp = typeInfo + .Type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic) + .FirstOrDefault(prop => + prop.GetCustomAttribute() != null + ); + + if (extensionProp is not null) + { + var jsonPropertyInfo = typeInfo.CreateJsonPropertyInfo( + extensionProp.FieldType, + extensionProp.Name + ); + jsonPropertyInfo.Get = extensionProp.GetValue; + jsonPropertyInfo.Set = extensionProp.SetValue; + jsonPropertyInfo.IsExtensionData = true; + typeInfo.Properties.Add(jsonPropertyInfo); + } + } + }, + }, + }, + }; + ConfigureJsonSerializerOptions(options); + JsonSerializerOptions = options; + } + + static partial void ConfigureJsonSerializerOptions(JsonSerializerOptions defaultOptions); +} + +internal static class JsonUtils +{ + internal static string Serialize(T obj) => + JsonSerializer.Serialize(obj, JsonOptions.JsonSerializerOptions); + + internal static JsonElement SerializeToElement(T obj) => + JsonSerializer.SerializeToElement(obj, JsonOptions.JsonSerializerOptions); + + internal static JsonDocument SerializeToDocument(T obj) => + JsonSerializer.SerializeToDocument(obj, JsonOptions.JsonSerializerOptions); + + internal static JsonNode? SerializeToNode(T obj) => + JsonSerializer.SerializeToNode(obj, JsonOptions.JsonSerializerOptions); + + internal static byte[] SerializeToUtf8Bytes(T obj) => + JsonSerializer.SerializeToUtf8Bytes(obj, JsonOptions.JsonSerializerOptions); + + internal static string SerializeWithAdditionalProperties( + T obj, + object? additionalProperties = null + ) + { + if (additionalProperties == null) + { + return Serialize(obj); + } + var additionalPropertiesJsonNode = SerializeToNode(additionalProperties); + if (additionalPropertiesJsonNode is not JsonObject additionalPropertiesJsonObject) + { + throw new InvalidOperationException( + "The additional properties must serialize to a JSON object." + ); + } + var jsonNode = SerializeToNode(obj); + if (jsonNode is not JsonObject jsonObject) + { + throw new InvalidOperationException( + "The serialized object must be a JSON object to add properties." + ); + } + MergeJsonObjects(jsonObject, additionalPropertiesJsonObject); + return jsonObject.ToJsonString(JsonOptions.JsonSerializerOptions); + } + + private static void MergeJsonObjects(JsonObject baseObject, JsonObject overrideObject) + { + foreach (var property in overrideObject) + { + if (!baseObject.TryGetPropertyValue(property.Key, out JsonNode? existingValue)) + { + baseObject[property.Key] = + property.Value != null ? JsonNode.Parse(property.Value.ToJsonString()) : null; + continue; + } + if ( + existingValue is JsonObject nestedBaseObject + && property.Value is JsonObject nestedOverrideObject + ) + { + // If both values are objects, recursively merge them. + MergeJsonObjects(nestedBaseObject, nestedOverrideObject); + continue; + } + // Otherwise, the overrideObject takes precedence. + baseObject[property.Key] = + property.Value != null ? JsonNode.Parse(property.Value.ToJsonString()) : null; + } + } + + internal static T Deserialize(string json) => + JsonSerializer.Deserialize(json, JsonOptions.JsonSerializerOptions)!; +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/JsonRequest.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/JsonRequest.cs new file mode 100644 index 000000000000..58a52ac96639 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/JsonRequest.cs @@ -0,0 +1,36 @@ +using System.Net.Http; + +namespace SeedInferredAuthImplicit.Core; + +/// +/// The request object to be sent for JSON APIs. +/// +internal record JsonRequest : BaseRequest +{ + internal object? Body { get; init; } + + internal override HttpContent? CreateContent() + { + if (Body is null && Options?.AdditionalBodyProperties is null) + { + return null; + } + + var (encoding, charset, mediaType) = ParseContentTypeOrDefault( + ContentType, + Utf8NoBom, + "application/json" + ); + var content = new StringContent( + JsonUtils.SerializeWithAdditionalProperties(Body, Options?.AdditionalBodyProperties), + encoding, + mediaType + ); + if (string.IsNullOrEmpty(charset) && content.Headers.ContentType is not null) + { + content.Headers.ContentType.CharSet = ""; + } + + return content; + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/MultipartFormRequest.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/MultipartFormRequest.cs new file mode 100644 index 000000000000..d3d3ede4675d --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/MultipartFormRequest.cs @@ -0,0 +1,294 @@ +using System.Net.Http; +using System.Net.Http.Headers; + +namespace SeedInferredAuthImplicit.Core; + +/// +/// The request object to be sent for multipart form data. +/// +internal record MultipartFormRequest : BaseRequest +{ + private readonly List> _partAdders = []; + + internal void AddJsonPart(string name, object? value) => AddJsonPart(name, value, null); + + internal void AddJsonPart(string name, object? value, string? contentType) + { + if (value is null) + { + return; + } + + _partAdders.Add(form => + { + var (encoding, charset, mediaType) = ParseContentTypeOrDefault( + contentType, + Utf8NoBom, + "application/json" + ); + var content = new StringContent(JsonUtils.Serialize(value), encoding, mediaType); + if (string.IsNullOrEmpty(charset) && content.Headers.ContentType is not null) + { + content.Headers.ContentType.CharSet = ""; + } + + form.Add(content, name); + }); + } + + internal void AddJsonParts(string name, IEnumerable? value) => + AddJsonParts(name, value, null); + + internal void AddJsonParts(string name, IEnumerable? value, string? contentType) + { + if (value is null) + { + return; + } + + foreach (var item in value) + { + AddJsonPart(name, item, contentType); + } + } + + internal void AddJsonParts(string name, IEnumerable? value) => + AddJsonParts(name, value, null); + + internal void AddJsonParts(string name, IEnumerable? value, string? contentType) + { + if (value is null) + { + return; + } + + foreach (var item in value) + { + AddJsonPart(name, item, contentType); + } + } + + internal void AddStringPart(string name, object? value) => AddStringPart(name, value, null); + + internal void AddStringPart(string name, object? value, string? contentType) + { + if (value is null) + { + return; + } + + AddStringPart(name, ValueConvert.ToString(value), contentType); + } + + internal void AddStringPart(string name, string? value) => AddStringPart(name, value, null); + + internal void AddStringPart(string name, string? value, string? contentType) + { + if (value is null) + { + return; + } + + _partAdders.Add(form => + { + var (encoding, charset, mediaType) = ParseContentTypeOrDefault( + contentType, + Utf8NoBom, + "text/plain" + ); + var content = new StringContent(value, encoding, mediaType); + if (string.IsNullOrEmpty(charset) && content.Headers.ContentType is not null) + { + content.Headers.ContentType.CharSet = ""; + } + + form.Add(content, name); + }); + } + + internal void AddStringParts(string name, IEnumerable? value) => + AddStringParts(name, value, null); + + internal void AddStringParts(string name, IEnumerable? value, string? contentType) + { + if (value is null) + { + return; + } + + AddStringPart(name, ValueConvert.ToString(value), contentType); + } + + internal void AddStringParts(string name, IEnumerable? value) => + AddStringParts(name, value, null); + + internal void AddStringParts(string name, IEnumerable? value, string? contentType) + { + if (value is null) + { + return; + } + + foreach (var item in value) + { + AddStringPart(name, item, contentType); + } + } + + internal void AddStreamPart(string name, Stream? stream, string? fileName) => + AddStreamPart(name, stream, fileName, null); + + internal void AddStreamPart(string name, Stream? stream, string? fileName, string? contentType) + { + if (stream is null) + { + return; + } + + _partAdders.Add(form => + { + var content = new StreamContent(stream) + { + Headers = + { + ContentType = MediaTypeHeaderValue.Parse( + contentType ?? "application/octet-stream" + ), + }, + }; + + if (fileName is not null) + { + form.Add(content, name, fileName); + } + else + { + form.Add(content, name); + } + }); + } + + internal void AddFileParameterPart(string name, Stream? stream) => + AddStreamPart(name, stream, null, null); + + internal void AddFileParameterPart(string name, FileParameter? file) => + AddFileParameterPart(name, file, null); + + internal void AddFileParameterPart( + string name, + FileParameter? file, + string? fallbackContentType + ) => + AddStreamPart(name, file?.Stream, file?.FileName, file?.ContentType ?? fallbackContentType); + + internal void AddFileParameterParts(string name, IEnumerable? files) => + AddFileParameterParts(name, files, null); + + internal void AddFileParameterParts( + string name, + IEnumerable? files, + string? fallbackContentType + ) + { + if (files is null) + { + return; + } + + foreach (var file in files) + { + AddFileParameterPart(name, file, fallbackContentType); + } + } + + internal void AddFormEncodedPart(string name, object? value) => + AddFormEncodedPart(name, value, null); + + internal void AddFormEncodedPart(string name, object? value, string? contentType) + { + if (value is null) + { + return; + } + + _partAdders.Add(form => + { + var content = FormUrlEncoder.EncodeAsForm(value); + if (!string.IsNullOrEmpty(contentType)) + { + content.Headers.ContentType = MediaTypeHeaderValue.Parse(contentType); + } + + form.Add(content, name); + }); + } + + internal void AddFormEncodedParts(string name, IEnumerable? value) => + AddFormEncodedParts(name, value, null); + + internal void AddFormEncodedParts(string name, IEnumerable? value, string? contentType) + { + if (value is null) + { + return; + } + + foreach (var item in value) + { + AddFormEncodedPart(name, item, contentType); + } + } + + internal void AddExplodedFormEncodedPart(string name, object? value) => + AddExplodedFormEncodedPart(name, value, null); + + internal void AddExplodedFormEncodedPart(string name, object? value, string? contentType) + { + if (value is null) + { + return; + } + + _partAdders.Add(form => + { + var content = FormUrlEncoder.EncodeAsExplodedForm(value); + if (!string.IsNullOrEmpty(contentType)) + { + content.Headers.ContentType = MediaTypeHeaderValue.Parse(contentType); + } + + form.Add(content, name); + }); + } + + internal void AddExplodedFormEncodedParts(string name, IEnumerable? value) => + AddExplodedFormEncodedParts(name, value, null); + + internal void AddExplodedFormEncodedParts( + string name, + IEnumerable? value, + string? contentType + ) + { + if (value is null) + { + return; + } + + foreach (var item in value) + { + AddExplodedFormEncodedPart(name, item, contentType); + } + } + + internal override HttpContent CreateContent() + { + var form = new MultipartFormDataContent(); + foreach (var adder in _partAdders) + { + adder(form); + } + + return form; + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/OneOfSerializer.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/OneOfSerializer.cs new file mode 100644 index 000000000000..fb1428363660 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/OneOfSerializer.cs @@ -0,0 +1,91 @@ +using System.Reflection; +using System.Text.Json; +using System.Text.Json.Serialization; +using OneOf; + +namespace SeedInferredAuthImplicit.Core; + +internal class OneOfSerializer : JsonConverter +{ + public override IOneOf? Read( + ref Utf8JsonReader reader, + global::System.Type typeToConvert, + JsonSerializerOptions options + ) + { + if (reader.TokenType is JsonTokenType.Null) + return default; + + foreach (var (type, cast) in GetOneOfTypes(typeToConvert)) + { + try + { + var readerCopy = reader; + var result = JsonSerializer.Deserialize(ref readerCopy, type, options); + reader.Skip(); + return (IOneOf)cast.Invoke(null, [result])!; + } + catch (JsonException) { } + } + + throw new JsonException( + $"Cannot deserialize into one of the supported types for {typeToConvert}" + ); + } + + public override void Write(Utf8JsonWriter writer, IOneOf value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.Value, options); + } + + private static (global::System.Type type, MethodInfo cast)[] GetOneOfTypes( + global::System.Type typeToConvert + ) + { + var type = typeToConvert; + if (Nullable.GetUnderlyingType(type) is { } underlyingType) + { + type = underlyingType; + } + + var casts = type.GetRuntimeMethods() + .Where(m => m.IsSpecialName && m.Name == "op_Implicit") + .ToArray(); + while (type != null) + { + if ( + type.IsGenericType + && (type.Name.StartsWith("OneOf`") || type.Name.StartsWith("OneOfBase`")) + ) + { + var genericArguments = type.GetGenericArguments(); + if (genericArguments.Length == 1) + { + return [(genericArguments[0], casts[0])]; + } + + // if object type is present, make sure it is last + var indexOfObjectType = Array.IndexOf(genericArguments, typeof(object)); + if (indexOfObjectType != -1 && genericArguments.Length - 1 != indexOfObjectType) + { + genericArguments = genericArguments + .OrderBy(t => t == typeof(object) ? 1 : 0) + .ToArray(); + } + + return genericArguments + .Select(t => (t, casts.First(c => c.GetParameters()[0].ParameterType == t))) + .ToArray(); + } + + type = type.BaseType; + } + + throw new InvalidOperationException($"{type} isn't OneOf or OneOfBase"); + } + + public override bool CanConvert(global::System.Type typeToConvert) + { + return typeof(IOneOf).IsAssignableFrom(typeToConvert); + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/Public/AdditionalProperties.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/Public/AdditionalProperties.cs new file mode 100644 index 000000000000..4d881bdf77b7 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/Public/AdditionalProperties.cs @@ -0,0 +1,353 @@ +using global::System.Collections; +using global::System.Collections.ObjectModel; +using global::System.Text.Json; +using global::System.Text.Json.Nodes; +using SeedInferredAuthImplicit.Core; + +namespace SeedInferredAuthImplicit; + +public record ReadOnlyAdditionalProperties : ReadOnlyAdditionalProperties +{ + internal ReadOnlyAdditionalProperties() { } + + internal ReadOnlyAdditionalProperties(IDictionary properties) + : base(properties) { } +} + +public record ReadOnlyAdditionalProperties : IReadOnlyDictionary +{ + private readonly Dictionary _extensionData = new(); + private readonly Dictionary _convertedCache = new(); + + internal ReadOnlyAdditionalProperties() + { + _extensionData = new Dictionary(); + _convertedCache = new Dictionary(); + } + + internal ReadOnlyAdditionalProperties(IDictionary properties) + { + _extensionData = new Dictionary(properties.Count); + _convertedCache = new Dictionary(properties.Count); + foreach (var kvp in properties) + { + if (kvp.Value is JsonElement element) + { + _extensionData.Add(kvp.Key, element); + } + else + { + _extensionData[kvp.Key] = JsonUtils.SerializeToElement(kvp.Value); + } + + _convertedCache[kvp.Key] = kvp.Value; + } + } + + private static T ConvertToT(JsonElement value) + { + if (typeof(T) == typeof(JsonElement)) + { + return (T)(object)value; + } + + return value.Deserialize(JsonOptions.JsonSerializerOptions)!; + } + + internal void CopyFromExtensionData(IDictionary extensionData) + { + _extensionData.Clear(); + _convertedCache.Clear(); + foreach (var kvp in extensionData) + { + _extensionData[kvp.Key] = kvp.Value; + if (kvp.Value is T value) + { + _convertedCache[kvp.Key] = value; + } + } + } + + private T GetCached(string key) + { + if (_convertedCache.TryGetValue(key, out var cached)) + { + return cached; + } + + var value = ConvertToT(_extensionData[key]); + _convertedCache[key] = value; + return value; + } + + public IEnumerator> GetEnumerator() + { + return _extensionData + .Select(kvp => new KeyValuePair(kvp.Key, GetCached(kvp.Key))) + .GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public int Count => _extensionData.Count; + + public bool ContainsKey(string key) => _extensionData.ContainsKey(key); + + public bool TryGetValue(string key, out T value) + { + if (_convertedCache.TryGetValue(key, out value!)) + { + return true; + } + + if (_extensionData.TryGetValue(key, out var element)) + { + value = ConvertToT(element); + _convertedCache[key] = value; + return true; + } + + return false; + } + + public T this[string key] => GetCached(key); + + public IEnumerable Keys => _extensionData.Keys; + + public IEnumerable Values => Keys.Select(GetCached); +} + +public record AdditionalProperties : AdditionalProperties +{ + public AdditionalProperties() { } + + public AdditionalProperties(IDictionary properties) + : base(properties) { } +} + +public record AdditionalProperties : IDictionary +{ + private readonly Dictionary _extensionData; + private readonly Dictionary _convertedCache; + + public AdditionalProperties() + { + _extensionData = new Dictionary(); + _convertedCache = new Dictionary(); + } + + public AdditionalProperties(IDictionary properties) + { + _extensionData = new Dictionary(properties.Count); + _convertedCache = new Dictionary(properties.Count); + foreach (var kvp in properties) + { + _extensionData[kvp.Key] = kvp.Value; + _convertedCache[kvp.Key] = kvp.Value; + } + } + + private static T ConvertToT(object? extensionDataValue) + { + return extensionDataValue switch + { + T value => value, + JsonElement jsonElement => jsonElement.Deserialize( + JsonOptions.JsonSerializerOptions + )!, + JsonNode jsonNode => jsonNode.Deserialize(JsonOptions.JsonSerializerOptions)!, + _ => JsonUtils + .SerializeToElement(extensionDataValue) + .Deserialize(JsonOptions.JsonSerializerOptions)!, + }; + } + + internal void CopyFromExtensionData(IDictionary extensionData) + { + _extensionData.Clear(); + _convertedCache.Clear(); + foreach (var kvp in extensionData) + { + _extensionData[kvp.Key] = kvp.Value; + if (kvp.Value is T value) + { + _convertedCache[kvp.Key] = value; + } + } + } + + internal void CopyToExtensionData(IDictionary extensionData) + { + extensionData.Clear(); + foreach (var kvp in _extensionData) + { + extensionData[kvp.Key] = kvp.Value; + } + } + + public JsonObject ToJsonObject() => + ( + JsonUtils.SerializeToNode(_extensionData) + ?? throw new InvalidOperationException( + "Failed to serialize AdditionalProperties to JSON Node." + ) + ).AsObject(); + + public JsonNode ToJsonNode() => + JsonUtils.SerializeToNode(_extensionData) + ?? throw new InvalidOperationException( + "Failed to serialize AdditionalProperties to JSON Node." + ); + + public JsonElement ToJsonElement() => JsonUtils.SerializeToElement(_extensionData); + + public JsonDocument ToJsonDocument() => JsonUtils.SerializeToDocument(_extensionData); + + public IReadOnlyDictionary ToJsonElementDictionary() + { + return new ReadOnlyDictionary( + _extensionData.ToDictionary( + kvp => kvp.Key, + kvp => + { + if (kvp.Value is JsonElement jsonElement) + { + return jsonElement; + } + + return JsonUtils.SerializeToElement(kvp.Value); + } + ) + ); + } + + public ICollection Keys => _extensionData.Keys; + + public ICollection Values + { + get + { + var values = new T[_extensionData.Count]; + var i = 0; + foreach (var key in Keys) + { + values[i++] = GetCached(key); + } + + return values; + } + } + + private T GetCached(string key) + { + if (_convertedCache.TryGetValue(key, out var value)) + { + return value; + } + + value = ConvertToT(_extensionData[key]); + _convertedCache.Add(key, value); + return value; + } + + private void SetCached(string key, T value) + { + _extensionData[key] = value; + _convertedCache[key] = value; + } + + private void AddCached(string key, T value) + { + _extensionData.Add(key, value); + _convertedCache.Add(key, value); + } + + private bool RemoveCached(string key) + { + var isRemoved = _extensionData.Remove(key); + _convertedCache.Remove(key); + return isRemoved; + } + + public int Count => _extensionData.Count; + public bool IsReadOnly => false; + + public T this[string key] + { + get => GetCached(key); + set => SetCached(key, value); + } + + public void Add(string key, T value) => AddCached(key, value); + + public void Add(KeyValuePair item) => AddCached(item.Key, item.Value); + + public bool Remove(string key) => RemoveCached(key); + + public bool Remove(KeyValuePair item) => RemoveCached(item.Key); + + public bool ContainsKey(string key) => _extensionData.ContainsKey(key); + + public bool Contains(KeyValuePair item) + { + return _extensionData.ContainsKey(item.Key) + && EqualityComparer.Default.Equals(GetCached(item.Key), item.Value); + } + + public bool TryGetValue(string key, out T value) + { + if (_convertedCache.TryGetValue(key, out value!)) + { + return true; + } + + if (_extensionData.TryGetValue(key, out var extensionDataValue)) + { + value = ConvertToT(extensionDataValue); + _convertedCache[key] = value; + return true; + } + + return false; + } + + public void Clear() + { + _extensionData.Clear(); + _convertedCache.Clear(); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + if (array is null) + { + throw new ArgumentNullException(nameof(array)); + } + + if (arrayIndex < 0 || arrayIndex > array.Length) + { + throw new ArgumentOutOfRangeException(nameof(arrayIndex)); + } + + if (array.Length - arrayIndex < _extensionData.Count) + { + throw new ArgumentException( + "The array does not have enough space to copy the elements." + ); + } + + foreach (var kvp in _extensionData) + { + array[arrayIndex++] = new KeyValuePair(kvp.Key, GetCached(kvp.Key)); + } + } + + public IEnumerator> GetEnumerator() + { + return _extensionData + .Select(kvp => new KeyValuePair(kvp.Key, GetCached(kvp.Key))) + .GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/Public/ClientOptions.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/Public/ClientOptions.cs new file mode 100644 index 000000000000..7da7b510b1f0 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/Public/ClientOptions.cs @@ -0,0 +1,83 @@ +using SeedInferredAuthImplicit.Core; + +namespace SeedInferredAuthImplicit; + +[Serializable] +public partial class ClientOptions +{ + /// + /// The http headers sent with the request. + /// + internal Headers Headers { get; init; } = new(); + + /// + /// The Base URL for the API. + /// + public string BaseUrl { get; +#if NET5_0_OR_GREATER + init; +#else + set; +#endif + } = ""; + + /// + /// The http client used to make requests. + /// + public HttpClient HttpClient { get; +#if NET5_0_OR_GREATER + init; +#else + set; +#endif + } = new HttpClient(); + + /// + /// Additional headers to be sent with HTTP requests. + /// Headers with matching keys will be overwritten by headers set on the request. + /// + public IEnumerable> AdditionalHeaders { get; +#if NET5_0_OR_GREATER + init; +#else + set; +#endif + } = []; + + /// + /// The http client used to make requests. + /// + public int MaxRetries { get; +#if NET5_0_OR_GREATER + init; +#else + set; +#endif + } = 2; + + /// + /// The timeout for the request. + /// + public TimeSpan Timeout { get; +#if NET5_0_OR_GREATER + init; +#else + set; +#endif + } = TimeSpan.FromSeconds(30); + + /// + /// Clones this and returns a new instance + /// + internal ClientOptions Clone() + { + return new ClientOptions + { + BaseUrl = BaseUrl, + HttpClient = HttpClient, + MaxRetries = MaxRetries, + Timeout = Timeout, + Headers = new Headers(new Dictionary(Headers)), + }; + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/Public/FileParameter.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/Public/FileParameter.cs new file mode 100644 index 000000000000..34a7ae32ebd1 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/Public/FileParameter.cs @@ -0,0 +1,63 @@ +namespace SeedInferredAuthImplicit; + +/// +/// File parameter for uploading files. +/// +public record FileParameter : IDisposable +#if NET6_0_OR_GREATER + , IAsyncDisposable +#endif +{ + private bool _disposed; + + /// + /// The name of the file to be uploaded. + /// + public string? FileName { get; set; } + + /// + /// The content type of the file to be uploaded. + /// + public string? ContentType { get; set; } + + /// + /// The content of the file to be uploaded. + /// + public required Stream Stream { get; set; } + + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + protected virtual void Dispose(bool disposing) + { + if (_disposed) + return; + if (disposing) + { + Stream.Dispose(); + } + + _disposed = true; + } + +#if NET6_0_OR_GREATER + /// + public async ValueTask DisposeAsync() + { + if (!_disposed) + { + await Stream.DisposeAsync().ConfigureAwait(false); + _disposed = true; + } + + GC.SuppressFinalize(this); + } +#endif + + public static implicit operator FileParameter(Stream stream) => new() { Stream = stream }; +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/Public/RequestOptions.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/Public/RequestOptions.cs new file mode 100644 index 000000000000..3ecb351227ab --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/Public/RequestOptions.cs @@ -0,0 +1,91 @@ +using SeedInferredAuthImplicit.Core; + +namespace SeedInferredAuthImplicit; + +[Serializable] +public partial class RequestOptions : IRequestOptions +{ + /// + /// The http headers sent with the request. + /// + Headers IRequestOptions.Headers { get; init; } = new(); + + /// + /// The Base URL for the API. + /// + public string? BaseUrl { get; +#if NET5_0_OR_GREATER + init; +#else + set; +#endif + } + + /// + /// The http client used to make requests. + /// + public HttpClient? HttpClient { get; +#if NET5_0_OR_GREATER + init; +#else + set; +#endif + } + + /// + /// Additional headers to be sent with the request. + /// Headers previously set with matching keys will be overwritten. + /// + public IEnumerable> AdditionalHeaders { get; +#if NET5_0_OR_GREATER + init; +#else + set; +#endif + } = []; + + /// + /// The http client used to make requests. + /// + public int? MaxRetries { get; +#if NET5_0_OR_GREATER + init; +#else + set; +#endif + } + + /// + /// The timeout for the request. + /// + public TimeSpan? Timeout { get; +#if NET5_0_OR_GREATER + init; +#else + set; +#endif + } + + /// + /// Additional query parameters sent with the request. + /// + public IEnumerable> AdditionalQueryParameters { get; +#if NET5_0_OR_GREATER + init; +#else + set; +#endif + } = Enumerable.Empty>(); + + /// + /// Additional body properties sent with the request. + /// This is only applied to JSON requests. + /// + public object? AdditionalBodyProperties { get; +#if NET5_0_OR_GREATER + init; +#else + set; +#endif + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/Public/SeedInferredAuthImplicitApiException.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/Public/SeedInferredAuthImplicitApiException.cs new file mode 100644 index 000000000000..2b2fd9cb01b4 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/Public/SeedInferredAuthImplicitApiException.cs @@ -0,0 +1,18 @@ +namespace SeedInferredAuthImplicit; + +/// +/// This exception type will be thrown for any non-2XX API responses. +/// +public class SeedInferredAuthImplicitApiException(string message, int statusCode, object body) + : SeedInferredAuthImplicitException(message) +{ + /// + /// The error code of the response that triggered the exception. + /// + public int StatusCode => statusCode; + + /// + /// The body of the response that triggered the exception. + /// + public object Body => body; +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/Public/SeedInferredAuthImplicitException.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/Public/SeedInferredAuthImplicitException.cs new file mode 100644 index 000000000000..3bd1e063ef87 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/Public/SeedInferredAuthImplicitException.cs @@ -0,0 +1,7 @@ +namespace SeedInferredAuthImplicit; + +/// +/// Base exception class for all exceptions thrown by the SDK. +/// +public class SeedInferredAuthImplicitException(string message, Exception? innerException = null) + : Exception(message, innerException); diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/Public/Version.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/Public/Version.cs new file mode 100644 index 000000000000..e6999e7e080d --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/Public/Version.cs @@ -0,0 +1,7 @@ +namespace SeedInferredAuthImplicit; + +[Serializable] +internal class Version +{ + public const string Current = "0.0.1"; +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/QueryStringConverter.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/QueryStringConverter.cs new file mode 100644 index 000000000000..a5288d89b375 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/QueryStringConverter.cs @@ -0,0 +1,229 @@ +using global::System.Text.Json; + +namespace SeedInferredAuthImplicit.Core; + +/// +/// Converts an object into a query string collection. +/// +internal static class QueryStringConverter +{ + /// + /// Converts an object into a query string collection using Deep Object notation. + /// + /// Object to form URL-encode. You can pass in an object or dictionary, but not lists, strings, or primitives. + /// Throws when passing in a list, a string, or a primitive value. + /// A collection of key value pairs. The keys and values are not URL encoded. + internal static IEnumerable> ToDeepObject(object value) + { + var queryCollection = new List>(); + var json = JsonUtils.SerializeToElement(value); + AssertRootJson(json); + JsonToDeepObject(json, "", queryCollection); + return queryCollection; + } + + /// + /// Converts an object into a query string collection using Exploded Form notation. + /// + /// Object to form URL-encode. You can pass in an object or dictionary, but not lists, strings, or primitives. + /// Throws when passing in a list, a string, or a primitive value. + /// A collection of key value pairs. The keys and values are not URL encoded. + internal static IEnumerable> ToExplodedForm(object value) + { + var queryCollection = new List>(); + var json = JsonUtils.SerializeToElement(value); + AssertRootJson(json); + JsonToFormExploded(json, "", queryCollection); + return queryCollection; + } + + /// + /// Converts an object into a query string collection using Form notation without exploding parameters. + /// + /// Object to form URL-encode. You can pass in an object or dictionary, but not lists, strings, or primitives. + /// Throws when passing in a list, a string, or a primitive value. + /// A collection of key value pairs. The keys and values are not URL encoded. + internal static IEnumerable> ToForm(object value) + { + var queryCollection = new List>(); + var json = JsonUtils.SerializeToElement(value); + AssertRootJson(json); + JsonToForm(json, "", queryCollection); + return queryCollection; + } + + private static void AssertRootJson(JsonElement json) + { + switch (json.ValueKind) + { + case JsonValueKind.Object: + break; + case JsonValueKind.Array: + case JsonValueKind.Undefined: + case JsonValueKind.String: + case JsonValueKind.Number: + case JsonValueKind.True: + case JsonValueKind.False: + case JsonValueKind.Null: + default: + throw new global::System.Exception( + $"Only objects can be converted to query string collections. Given type is {json.ValueKind}." + ); + } + } + + private static void JsonToForm( + JsonElement element, + string prefix, + List> parameters + ) + { + switch (element.ValueKind) + { + case JsonValueKind.Object: + foreach (var property in element.EnumerateObject()) + { + var newPrefix = string.IsNullOrEmpty(prefix) + ? property.Name + : $"{prefix}[{property.Name}]"; + + JsonToForm(property.Value, newPrefix, parameters); + } + break; + case JsonValueKind.Array: + var arrayValues = element.EnumerateArray().Select(ValueToString).ToArray(); + parameters.Add( + new KeyValuePair(prefix, string.Join(",", arrayValues)) + ); + break; + case JsonValueKind.Null: + break; + case JsonValueKind.Undefined: + case JsonValueKind.String: + case JsonValueKind.Number: + case JsonValueKind.True: + case JsonValueKind.False: + default: + parameters.Add(new KeyValuePair(prefix, ValueToString(element))); + break; + } + } + + private static void JsonToFormExploded( + JsonElement element, + string prefix, + List> parameters + ) + { + switch (element.ValueKind) + { + case JsonValueKind.Object: + foreach (var property in element.EnumerateObject()) + { + var newPrefix = string.IsNullOrEmpty(prefix) + ? property.Name + : $"{prefix}[{property.Name}]"; + + JsonToFormExploded(property.Value, newPrefix, parameters); + } + + break; + case JsonValueKind.Array: + foreach (var item in element.EnumerateArray()) + { + if ( + item.ValueKind != JsonValueKind.Object + && item.ValueKind != JsonValueKind.Array + ) + { + parameters.Add( + new KeyValuePair(prefix, ValueToString(item)) + ); + } + else + { + JsonToFormExploded(item, prefix, parameters); + } + } + + break; + case JsonValueKind.Null: + break; + case JsonValueKind.Undefined: + case JsonValueKind.String: + case JsonValueKind.Number: + case JsonValueKind.True: + case JsonValueKind.False: + default: + parameters.Add(new KeyValuePair(prefix, ValueToString(element))); + break; + } + } + + private static void JsonToDeepObject( + JsonElement element, + string prefix, + List> parameters + ) + { + switch (element.ValueKind) + { + case JsonValueKind.Object: + foreach (var property in element.EnumerateObject()) + { + var newPrefix = string.IsNullOrEmpty(prefix) + ? property.Name + : $"{prefix}[{property.Name}]"; + + JsonToDeepObject(property.Value, newPrefix, parameters); + } + + break; + case JsonValueKind.Array: + var index = 0; + foreach (var item in element.EnumerateArray()) + { + var newPrefix = $"{prefix}[{index++}]"; + + if ( + item.ValueKind != JsonValueKind.Object + && item.ValueKind != JsonValueKind.Array + ) + { + parameters.Add( + new KeyValuePair(newPrefix, ValueToString(item)) + ); + } + else + { + JsonToDeepObject(item, newPrefix, parameters); + } + } + + break; + case JsonValueKind.Null: + break; + case JsonValueKind.Undefined: + case JsonValueKind.String: + case JsonValueKind.Number: + case JsonValueKind.True: + case JsonValueKind.False: + default: + parameters.Add(new KeyValuePair(prefix, ValueToString(element))); + break; + } + } + + private static string ValueToString(JsonElement element) + { + return element.ValueKind switch + { + JsonValueKind.String => element.GetString() ?? "", + JsonValueKind.Number => element.GetRawText(), + JsonValueKind.True => "true", + JsonValueKind.False => "false", + JsonValueKind.Null => "", + _ => element.GetRawText(), + }; + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/RawClient.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/RawClient.cs new file mode 100644 index 000000000000..974b5a71cb50 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/RawClient.cs @@ -0,0 +1,504 @@ +using global::System.Net.Http; +using global::System.Net.Http.Headers; +using global::System.Text; +using SystemTask = global::System.Threading.Tasks.Task; + +namespace SeedInferredAuthImplicit.Core; + +/// +/// Utility class for making raw HTTP requests to the API. +/// +internal partial class RawClient(ClientOptions clientOptions) +{ + private const int MaxRetryDelayMs = 60000; + private const double JitterFactor = 0.2; +#if NET6_0_OR_GREATER + // Use Random.Shared for thread-safe random number generation on .NET 6+ +#else + private static readonly object JitterLock = new(); + private static readonly Random JitterRandom = new(); +#endif + internal int BaseRetryDelay { get; set; } = 1000; + + /// + /// The client options applied on every request. + /// + internal readonly ClientOptions Options = clientOptions; + + [Obsolete("Use SendRequestAsync instead.")] + internal global::System.Threading.Tasks.Task MakeRequestAsync( + global::SeedInferredAuthImplicit.Core.BaseRequest request, + CancellationToken cancellationToken = default + ) + { + return SendRequestAsync(request, cancellationToken); + } + + internal async global::System.Threading.Tasks.Task SendRequestAsync( + global::SeedInferredAuthImplicit.Core.BaseRequest request, + CancellationToken cancellationToken = default + ) + { + // Apply the request timeout. + using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + var timeout = request.Options?.Timeout ?? Options.Timeout; + cts.CancelAfter(timeout); + + var httpRequest = await CreateHttpRequestAsync(request).ConfigureAwait(false); + // Send the request. + return await SendWithRetriesAsync(httpRequest, request.Options, cts.Token) + .ConfigureAwait(false); + } + + internal async global::System.Threading.Tasks.Task SendRequestAsync( + HttpRequestMessage request, + IRequestOptions? options, + CancellationToken cancellationToken = default + ) + { + // Apply the request timeout. + using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + var timeout = options?.Timeout ?? Options.Timeout; + cts.CancelAfter(timeout); + + // Send the request. + return await SendWithRetriesAsync(request, options, cts.Token).ConfigureAwait(false); + } + + private static async global::System.Threading.Tasks.Task CloneRequestAsync( + HttpRequestMessage request + ) + { + var clonedRequest = new HttpRequestMessage(request.Method, request.RequestUri); + clonedRequest.Version = request.Version; + switch (request.Content) + { + case MultipartContent oldMultipartFormContent: + var originalBoundary = + oldMultipartFormContent + .Headers.ContentType?.Parameters.First(p => + p.Name.Equals("boundary", StringComparison.OrdinalIgnoreCase) + ) + .Value?.Trim('"') ?? Guid.NewGuid().ToString(); + var newMultipartContent = oldMultipartFormContent switch + { + MultipartFormDataContent => new MultipartFormDataContent(originalBoundary), + _ => new MultipartContent(), + }; + foreach (var content in oldMultipartFormContent) + { + var ms = new MemoryStream(); + await content.CopyToAsync(ms).ConfigureAwait(false); + ms.Position = 0; + var newPart = new StreamContent(ms); + foreach (var header in oldMultipartFormContent.Headers) + { + newPart.Headers.TryAddWithoutValidation(header.Key, header.Value); + } + + newMultipartContent.Add(newPart); + } + + clonedRequest.Content = newMultipartContent; + break; + default: + clonedRequest.Content = request.Content; + break; + } + + foreach (var header in request.Headers) + { + clonedRequest.Headers.TryAddWithoutValidation(header.Key, header.Value); + } + + return clonedRequest; + } + + /// + /// Sends the request with retries, unless the request content is not retryable, + /// such as stream requests and multipart form data with stream content. + /// + private async global::System.Threading.Tasks.Task SendWithRetriesAsync( + HttpRequestMessage request, + IRequestOptions? options, + CancellationToken cancellationToken + ) + { + var httpClient = options?.HttpClient ?? Options.HttpClient; + var maxRetries = options?.MaxRetries ?? Options.MaxRetries; + var response = await httpClient + .SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken) + .ConfigureAwait(false); + var isRetryableContent = IsRetryableContent(request); + + if (!isRetryableContent) + { + return new global::SeedInferredAuthImplicit.Core.ApiResponse + { + StatusCode = (int)response.StatusCode, + Raw = response, + }; + } + + for (var i = 0; i < maxRetries; i++) + { + if (!ShouldRetry(response)) + { + break; + } + + var delayMs = GetRetryDelayFromHeaders(response, i); + await SystemTask.Delay(delayMs, cancellationToken).ConfigureAwait(false); + using var retryRequest = await CloneRequestAsync(request).ConfigureAwait(false); + response = await httpClient + .SendAsync( + retryRequest, + HttpCompletionOption.ResponseHeadersRead, + cancellationToken + ) + .ConfigureAwait(false); + } + + return new global::SeedInferredAuthImplicit.Core.ApiResponse + { + StatusCode = (int)response.StatusCode, + Raw = response, + }; + } + + private static bool ShouldRetry(HttpResponseMessage response) + { + var statusCode = (int)response.StatusCode; + return statusCode is 408 or 429 or >= 500; + } + + private static int AddPositiveJitter(int delayMs) + { +#if NET6_0_OR_GREATER + var random = Random.Shared.NextDouble(); +#else + double random; + lock (JitterLock) + { + random = JitterRandom.NextDouble(); + } +#endif + var jitterMultiplier = 1 + random * JitterFactor; + return (int)(delayMs * jitterMultiplier); + } + + private static int AddSymmetricJitter(int delayMs) + { +#if NET6_0_OR_GREATER + var random = Random.Shared.NextDouble(); +#else + double random; + lock (JitterLock) + { + random = JitterRandom.NextDouble(); + } +#endif + var jitterMultiplier = 1 + (random - 0.5) * JitterFactor; + return (int)(delayMs * jitterMultiplier); + } + + private int GetRetryDelayFromHeaders(HttpResponseMessage response, int retryAttempt) + { + if (response.Headers.TryGetValues("Retry-After", out var retryAfterValues)) + { + var retryAfter = retryAfterValues.FirstOrDefault(); + if (!string.IsNullOrEmpty(retryAfter)) + { + if (int.TryParse(retryAfter, out var retryAfterSeconds) && retryAfterSeconds > 0) + { + return Math.Min(retryAfterSeconds * 1000, MaxRetryDelayMs); + } + + if (DateTimeOffset.TryParse(retryAfter, out var retryAfterDate)) + { + var delay = (int)(retryAfterDate - DateTimeOffset.UtcNow).TotalMilliseconds; + if (delay > 0) + { + return Math.Min(delay, MaxRetryDelayMs); + } + } + } + } + + if (response.Headers.TryGetValues("X-RateLimit-Reset", out var rateLimitResetValues)) + { + var rateLimitReset = rateLimitResetValues.FirstOrDefault(); + if ( + !string.IsNullOrEmpty(rateLimitReset) + && long.TryParse(rateLimitReset, out var resetTime) + ) + { + var resetDateTime = DateTimeOffset.FromUnixTimeSeconds(resetTime); + var delay = (int)(resetDateTime - DateTimeOffset.UtcNow).TotalMilliseconds; + if (delay > 0) + { + return AddPositiveJitter(Math.Min(delay, MaxRetryDelayMs)); + } + } + } + + var exponentialDelay = Math.Min(BaseRetryDelay * (1 << retryAttempt), MaxRetryDelayMs); + return AddSymmetricJitter(exponentialDelay); + } + + private static bool IsRetryableContent(HttpRequestMessage request) + { + return request.Content switch + { + IIsRetryableContent c => c.IsRetryable, + StreamContent => false, + MultipartContent content => !content.Any(c => c is StreamContent), + _ => true, + }; + } + + internal async global::System.Threading.Tasks.Task CreateHttpRequestAsync( + global::SeedInferredAuthImplicit.Core.BaseRequest request + ) + { + var url = BuildUrl(request); + var httpRequest = new HttpRequestMessage(request.Method, url); + httpRequest.Content = request.CreateContent(); + var mergedHeaders = new Dictionary>(); + await MergeHeadersAsync(mergedHeaders, Options.Headers).ConfigureAwait(false); + MergeAdditionalHeaders(mergedHeaders, Options.AdditionalHeaders); + await MergeHeadersAsync(mergedHeaders, request.Headers).ConfigureAwait(false); + await MergeHeadersAsync(mergedHeaders, request.Options?.Headers).ConfigureAwait(false); + + MergeAdditionalHeaders(mergedHeaders, request.Options?.AdditionalHeaders ?? []); + SetHeaders(httpRequest, mergedHeaders); + return httpRequest; + } + + private static string BuildUrl(global::SeedInferredAuthImplicit.Core.BaseRequest request) + { + var baseUrl = request.Options?.BaseUrl ?? request.BaseUrl; + var trimmedBaseUrl = baseUrl.TrimEnd('/'); + var trimmedBasePath = request.Path.TrimStart('/'); + var url = $"{trimmedBaseUrl}/{trimmedBasePath}"; + + var queryParameters = GetQueryParameters(request); + if (!queryParameters.Any()) + return url; + + url += "?"; + url = queryParameters.Aggregate( + url, + (current, queryItem) => + { + if ( + queryItem.Value + is global::System.Collections.IEnumerable collection + and not string + ) + { + var items = collection + .Cast() + .Select(value => + $"{Uri.EscapeDataString(queryItem.Key)}={Uri.EscapeDataString(value?.ToString() ?? "")}" + ) + .ToList(); + if (items.Any()) + { + current += string.Join("&", items) + "&"; + } + } + else + { + current += + $"{Uri.EscapeDataString(queryItem.Key)}={Uri.EscapeDataString(queryItem.Value)}&"; + } + + return current; + } + ); + url = url[..^1]; + return url; + } + + private static List> GetQueryParameters( + global::SeedInferredAuthImplicit.Core.BaseRequest request + ) + { + var result = TransformToKeyValuePairs(request.Query); + if ( + request.Options?.AdditionalQueryParameters is null + || !request.Options.AdditionalQueryParameters.Any() + ) + { + return result; + } + + var additionalKeys = request + .Options.AdditionalQueryParameters.Select(p => p.Key) + .Distinct(); + foreach (var key in additionalKeys) + { + result.RemoveAll(kv => kv.Key == key); + } + + result.AddRange(request.Options.AdditionalQueryParameters); + return result; + } + + private static List> TransformToKeyValuePairs( + Dictionary inputDict + ) + { + var result = new List>(); + foreach (var kvp in inputDict) + { + switch (kvp.Value) + { + case string str: + result.Add(new KeyValuePair(kvp.Key, str)); + break; + case IEnumerable strList: + { + foreach (var value in strList) + { + result.Add(new KeyValuePair(kvp.Key, value)); + } + + break; + } + } + } + + return result; + } + + private static async SystemTask MergeHeadersAsync( + Dictionary> mergedHeaders, + Headers? headers + ) + { + if (headers is null) + { + return; + } + + foreach (var header in headers) + { + var value = await header.Value.ResolveAsync().ConfigureAwait(false); + if (value is not null) + { + mergedHeaders[header.Key] = [value]; + } + } + } + + private static void MergeAdditionalHeaders( + Dictionary> mergedHeaders, + IEnumerable>? headers + ) + { + if (headers is null) + { + return; + } + + var usedKeys = new HashSet(); + foreach (var header in headers) + { + if (header.Value is null) + { + mergedHeaders.Remove(header.Key); + usedKeys.Remove(header.Key); + continue; + } + + if (usedKeys.Contains(header.Key)) + { + mergedHeaders[header.Key].Add(header.Value); + } + else + { + mergedHeaders[header.Key] = [header.Value]; + usedKeys.Add(header.Key); + } + } + } + + private void SetHeaders( + HttpRequestMessage httpRequest, + Dictionary> mergedHeaders + ) + { + foreach (var kv in mergedHeaders) + { + foreach (var header in kv.Value) + { + if (header is null) + { + continue; + } + + httpRequest.Headers.TryAddWithoutValidation(kv.Key, header); + } + } + } + + private static (Encoding encoding, string? charset, string mediaType) ParseContentTypeOrDefault( + string? contentType, + Encoding encodingFallback, + string mediaTypeFallback + ) + { + var encoding = encodingFallback; + var mediaType = mediaTypeFallback; + string? charset = null; + if (string.IsNullOrEmpty(contentType)) + { + return (encoding, charset, mediaType); + } + + if (!MediaTypeHeaderValue.TryParse(contentType, out var mediaTypeHeaderValue)) + { + return (encoding, charset, mediaType); + } + + if (!string.IsNullOrEmpty(mediaTypeHeaderValue.CharSet)) + { + charset = mediaTypeHeaderValue.CharSet; + encoding = Encoding.GetEncoding(mediaTypeHeaderValue.CharSet); + } + + if (!string.IsNullOrEmpty(mediaTypeHeaderValue.MediaType)) + { + mediaType = mediaTypeHeaderValue.MediaType; + } + + return (encoding, charset, mediaType); + } + + /// + [Obsolete("Use global::SeedInferredAuthImplicit.Core.ApiResponse instead.")] + internal record ApiResponse : global::SeedInferredAuthImplicit.Core.ApiResponse; + + /// + [Obsolete("Use global::SeedInferredAuthImplicit.Core.BaseRequest instead.")] + internal abstract record BaseApiRequest : global::SeedInferredAuthImplicit.Core.BaseRequest; + + /// + [Obsolete("Use global::SeedInferredAuthImplicit.Core.EmptyRequest instead.")] + internal abstract record EmptyApiRequest : global::SeedInferredAuthImplicit.Core.EmptyRequest; + + /// + [Obsolete("Use global::SeedInferredAuthImplicit.Core.JsonRequest instead.")] + internal abstract record JsonApiRequest : global::SeedInferredAuthImplicit.Core.JsonRequest; + + /// + [Obsolete("Use global::SeedInferredAuthImplicit.Core.MultipartFormRequest instead.")] + internal abstract record MultipartFormRequest + : global::SeedInferredAuthImplicit.Core.MultipartFormRequest; + + /// + [Obsolete("Use global::SeedInferredAuthImplicit.Core.StreamRequest instead.")] + internal abstract record StreamApiRequest : global::SeedInferredAuthImplicit.Core.StreamRequest; +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/StreamRequest.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/StreamRequest.cs new file mode 100644 index 000000000000..4ab7ad22e936 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/StreamRequest.cs @@ -0,0 +1,29 @@ +using System.Net.Http; +using System.Net.Http.Headers; + +namespace SeedInferredAuthImplicit.Core; + +/// +/// The request object to be sent for streaming uploads. +/// +internal record StreamRequest : BaseRequest +{ + internal Stream? Body { get; init; } + + internal override HttpContent? CreateContent() + { + if (Body is null) + { + return null; + } + + var content = new StreamContent(Body) + { + Headers = + { + ContentType = MediaTypeHeaderValue.Parse(ContentType ?? "application/octet-stream"), + }, + }; + return content; + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/StringEnum.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/StringEnum.cs new file mode 100644 index 000000000000..f2dbf4a377fc --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/StringEnum.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace SeedInferredAuthImplicit.Core; + +public interface IStringEnum : IEquatable +{ + public string Value { get; } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/StringEnumExtensions.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/StringEnumExtensions.cs new file mode 100644 index 000000000000..6b8070df3e70 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/StringEnumExtensions.cs @@ -0,0 +1,6 @@ +namespace SeedInferredAuthImplicit.Core; + +internal static class StringEnumExtensions +{ + public static string Stringify(this IStringEnum stringEnum) => stringEnum.Value; +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/StringEnumSerializer.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/StringEnumSerializer.cs new file mode 100644 index 000000000000..098356479f88 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/StringEnumSerializer.cs @@ -0,0 +1,25 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace SeedInferredAuthImplicit.Core; + +internal class StringEnumSerializer : JsonConverter + where T : IStringEnum +{ + public override T? Read( + ref Utf8JsonReader reader, + global::System.Type typeToConvert, + JsonSerializerOptions options + ) + { + var stringValue = + reader.GetString() + ?? throw new global::System.Exception("The JSON value could not be read as a string."); + return (T?)Activator.CreateInstance(typeToConvert, stringValue); + } + + public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.Value); + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/ValueConvert.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/ValueConvert.cs new file mode 100644 index 000000000000..bb741fd4fa36 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Core/ValueConvert.cs @@ -0,0 +1,115 @@ +using global::System.Globalization; + +namespace SeedInferredAuthImplicit.Core; + +/// +/// Convert values to string for path and query parameters. +/// +public static class ValueConvert +{ + internal static string ToPathParameterString(T value) => ToString(value); + + internal static string ToPathParameterString(bool v) => ToString(v); + + internal static string ToPathParameterString(int v) => ToString(v); + + internal static string ToPathParameterString(long v) => ToString(v); + + internal static string ToPathParameterString(float v) => ToString(v); + + internal static string ToPathParameterString(double v) => ToString(v); + + internal static string ToPathParameterString(decimal v) => ToString(v); + + internal static string ToPathParameterString(short v) => ToString(v); + + internal static string ToPathParameterString(ushort v) => ToString(v); + + internal static string ToPathParameterString(uint v) => ToString(v); + + internal static string ToPathParameterString(ulong v) => ToString(v); + + internal static string ToPathParameterString(string v) => ToString(v); + + internal static string ToPathParameterString(char v) => ToString(v); + + internal static string ToPathParameterString(Guid v) => ToString(v); + + internal static string ToQueryStringValue(T value) => value is null ? "" : ToString(value); + + internal static string ToQueryStringValue(bool v) => ToString(v); + + internal static string ToQueryStringValue(int v) => ToString(v); + + internal static string ToQueryStringValue(long v) => ToString(v); + + internal static string ToQueryStringValue(float v) => ToString(v); + + internal static string ToQueryStringValue(double v) => ToString(v); + + internal static string ToQueryStringValue(decimal v) => ToString(v); + + internal static string ToQueryStringValue(short v) => ToString(v); + + internal static string ToQueryStringValue(ushort v) => ToString(v); + + internal static string ToQueryStringValue(uint v) => ToString(v); + + internal static string ToQueryStringValue(ulong v) => ToString(v); + + internal static string ToQueryStringValue(string v) => v is null ? "" : v; + + internal static string ToQueryStringValue(char v) => ToString(v); + + internal static string ToQueryStringValue(Guid v) => ToString(v); + + internal static string ToString(T value) + { + return value switch + { + null => "null", + string str => str, + true => "true", + false => "false", + int i => ToString(i), + long l => ToString(l), + float f => ToString(f), + double d => ToString(d), + decimal dec => ToString(dec), + short s => ToString(s), + ushort u => ToString(u), + uint u => ToString(u), + ulong u => ToString(u), + char c => ToString(c), + Guid guid => ToString(guid), + Enum e => JsonUtils.Serialize(e).Trim('"'), + _ => JsonUtils.Serialize(value).Trim('"'), + }; + } + + internal static string ToString(bool v) => v ? "true" : "false"; + + internal static string ToString(int v) => v.ToString(CultureInfo.InvariantCulture); + + internal static string ToString(long v) => v.ToString(CultureInfo.InvariantCulture); + + internal static string ToString(float v) => v.ToString(CultureInfo.InvariantCulture); + + internal static string ToString(double v) => v.ToString(CultureInfo.InvariantCulture); + + internal static string ToString(decimal v) => v.ToString(CultureInfo.InvariantCulture); + + internal static string ToString(short v) => v.ToString(CultureInfo.InvariantCulture); + + internal static string ToString(ushort v) => v.ToString(CultureInfo.InvariantCulture); + + internal static string ToString(uint v) => v.ToString(CultureInfo.InvariantCulture); + + internal static string ToString(ulong v) => v.ToString(CultureInfo.InvariantCulture); + + internal static string ToString(char v) => v.ToString(CultureInfo.InvariantCulture); + + internal static string ToString(string v) => v; + + internal static string ToString(Guid v) => v.ToString("D"); +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Nested/Api/ApiClient.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Nested/Api/ApiClient.cs new file mode 100644 index 000000000000..b3880502a46d --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Nested/Api/ApiClient.cs @@ -0,0 +1,48 @@ +using SeedInferredAuthImplicit; +using SeedInferredAuthImplicit.Core; + +namespace SeedInferredAuthImplicit.Nested; + +public partial class ApiClient +{ + private RawClient _client; + + internal ApiClient(RawClient client) + { + _client = client; + } + + /// + /// await client.Nested.Api.GetSomethingAsync(); + /// + public async Task GetSomethingAsync( + RequestOptions? options = null, + CancellationToken cancellationToken = default + ) + { + var response = await _client + .SendRequestAsync( + new JsonRequest + { + BaseUrl = _client.Options.BaseUrl, + Method = HttpMethod.Get, + Path = "/nested/get-something", + Options = options, + }, + cancellationToken + ) + .ConfigureAwait(false); + if (response.StatusCode is >= 200 and < 400) + { + return; + } + { + var responseBody = await response.Raw.Content.ReadAsStringAsync(); + throw new SeedInferredAuthImplicitApiException( + $"Error with status code {response.StatusCode}", + response.StatusCode, + responseBody + ); + } + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Nested/NestedClient.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Nested/NestedClient.cs new file mode 100644 index 000000000000..9ba405af88c8 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Nested/NestedClient.cs @@ -0,0 +1,16 @@ +using SeedInferredAuthImplicit.Core; + +namespace SeedInferredAuthImplicit.Nested; + +public partial class NestedClient +{ + private RawClient _client; + + internal NestedClient(RawClient client) + { + _client = client; + Api = new ApiClient(_client); + } + + public ApiClient Api { get; } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/NestedNoAuth/Api/ApiClient.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/NestedNoAuth/Api/ApiClient.cs new file mode 100644 index 000000000000..aadcb3fa8e5d --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/NestedNoAuth/Api/ApiClient.cs @@ -0,0 +1,48 @@ +using SeedInferredAuthImplicit; +using SeedInferredAuthImplicit.Core; + +namespace SeedInferredAuthImplicit.NestedNoAuth; + +public partial class ApiClient +{ + private RawClient _client; + + internal ApiClient(RawClient client) + { + _client = client; + } + + /// + /// await client.NestedNoAuth.Api.GetSomethingAsync(); + /// + public async Task GetSomethingAsync( + RequestOptions? options = null, + CancellationToken cancellationToken = default + ) + { + var response = await _client + .SendRequestAsync( + new JsonRequest + { + BaseUrl = _client.Options.BaseUrl, + Method = HttpMethod.Get, + Path = "/nested-no-auth/get-something", + Options = options, + }, + cancellationToken + ) + .ConfigureAwait(false); + if (response.StatusCode is >= 200 and < 400) + { + return; + } + { + var responseBody = await response.Raw.Content.ReadAsStringAsync(); + throw new SeedInferredAuthImplicitApiException( + $"Error with status code {response.StatusCode}", + response.StatusCode, + responseBody + ); + } + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/NestedNoAuth/NestedNoAuthClient.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/NestedNoAuth/NestedNoAuthClient.cs new file mode 100644 index 000000000000..7f0f75b8e1c5 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/NestedNoAuth/NestedNoAuthClient.cs @@ -0,0 +1,16 @@ +using SeedInferredAuthImplicit.Core; + +namespace SeedInferredAuthImplicit.NestedNoAuth; + +public partial class NestedNoAuthClient +{ + private RawClient _client; + + internal NestedNoAuthClient(RawClient client) + { + _client = client; + Api = new ApiClient(_client); + } + + public ApiClient Api { get; } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/SeedInferredAuthImplicit.Custom.props b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/SeedInferredAuthImplicit.Custom.props new file mode 100644 index 000000000000..17a84cada530 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/SeedInferredAuthImplicit.Custom.props @@ -0,0 +1,20 @@ + + + + diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/SeedInferredAuthImplicit.csproj b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/SeedInferredAuthImplicit.csproj new file mode 100644 index 000000000000..b763226e2269 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/SeedInferredAuthImplicit.csproj @@ -0,0 +1,61 @@ + + + net462;net8.0;net7.0;net6.0;netstandard2.0 + enable + 12 + enable + 0.0.1 + $(Version) + $(Version) + README.md + https://github.com/inferred-auth-implicit-reference/fern + true + + + + false + + + $(DefineConstants);USE_PORTABLE_DATE_ONLY + true + + + + + + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + + + + + <_Parameter1>SeedInferredAuthImplicit.Test + + + + + diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/SeedInferredAuthImplicitClient.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/SeedInferredAuthImplicitClient.cs new file mode 100644 index 000000000000..78dbb89b7ac0 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/SeedInferredAuthImplicitClient.cs @@ -0,0 +1,61 @@ +using SeedInferredAuthImplicit.Core; +using SeedInferredAuthImplicit.Nested; +using SeedInferredAuthImplicit.NestedNoAuth; + +namespace SeedInferredAuthImplicit; + +public partial class SeedInferredAuthImplicitClient +{ + private readonly RawClient _client; + + public SeedInferredAuthImplicitClient( + string clientId, + string clientSecret, + string? scope = null, + ClientOptions? clientOptions = null + ) + { + var defaultHeaders = new Headers( + new Dictionary() + { + { "X-Fern-Language", "C#" }, + { "X-Fern-SDK-Name", "SeedInferredAuthImplicit" }, + { "X-Fern-SDK-Version", Version.Current }, + { "User-Agent", "Ferninferred-auth-implicit-reference/0.0.1" }, + } + ); + clientOptions ??= new ClientOptions(); + foreach (var header in defaultHeaders) + { + if (!clientOptions.Headers.ContainsKey(header.Key)) + { + clientOptions.Headers[header.Key] = header.Value; + } + } + var inferredAuthProvider = new InferredAuthTokenProvider( + clientId, + clientSecret, + scope, + new AuthClient(new RawClient(clientOptions.Clone())) + ); + clientOptions.Headers["Authorization"] = + new Func>(async () => + (await inferredAuthProvider.GetAuthHeadersAsync().ConfigureAwait(false)) + .First() + .Value + ); + _client = new RawClient(clientOptions); + Auth = new AuthClient(_client); + NestedNoAuth = new NestedNoAuthClient(_client); + Nested = new NestedClient(_client); + Simple = new SimpleClient(_client); + } + + public AuthClient Auth { get; } + + public NestedNoAuthClient NestedNoAuth { get; } + + public NestedClient Nested { get; } + + public SimpleClient Simple { get; } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Simple/SimpleClient.cs b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Simple/SimpleClient.cs new file mode 100644 index 000000000000..aedf4b23daf6 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit-reference/src/SeedInferredAuthImplicit/Simple/SimpleClient.cs @@ -0,0 +1,47 @@ +using SeedInferredAuthImplicit.Core; + +namespace SeedInferredAuthImplicit; + +public partial class SimpleClient +{ + private RawClient _client; + + internal SimpleClient(RawClient client) + { + _client = client; + } + + /// + /// await client.Simple.GetSomethingAsync(); + /// + public async Task GetSomethingAsync( + RequestOptions? options = null, + CancellationToken cancellationToken = default + ) + { + var response = await _client + .SendRequestAsync( + new JsonRequest + { + BaseUrl = _client.Options.BaseUrl, + Method = HttpMethod.Get, + Path = "/get-something", + Options = options, + }, + cancellationToken + ) + .ConfigureAwait(false); + if (response.StatusCode is >= 200 and < 400) + { + return; + } + { + var responseBody = await response.Raw.Content.ReadAsStringAsync(); + throw new SeedInferredAuthImplicitApiException( + $"Error with status code {response.StatusCode}", + response.StatusCode, + responseBody + ); + } + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit/README.md b/seed/csharp-sdk/inferred-auth-implicit/README.md index caa9c3103336..7f4d95936645 100644 --- a/seed/csharp-sdk/inferred-auth-implicit/README.md +++ b/seed/csharp-sdk/inferred-auth-implicit/README.md @@ -38,7 +38,7 @@ Instantiate and use the client with the following: ```csharp using SeedInferredAuthImplicit; -var client = new SeedInferredAuthImplicitClient(); +var client = new SeedInferredAuthImplicitClient("X-Api-Key", "client_id", "client_secret", "scope"); await client.Auth.GetTokenWithClientCredentialsAsync( new GetTokenRequest { diff --git a/seed/csharp-sdk/inferred-auth-implicit/snippet.json b/seed/csharp-sdk/inferred-auth-implicit/snippet.json index f16c8d85bb85..208ada48eac7 100644 --- a/seed/csharp-sdk/inferred-auth-implicit/snippet.json +++ b/seed/csharp-sdk/inferred-auth-implicit/snippet.json @@ -10,7 +10,7 @@ }, "snippet": { "type": "csharp", - "client": "using SeedInferredAuthImplicit;\n\nvar client = new SeedInferredAuthImplicitClient();\nawait client.Auth.GetTokenWithClientCredentialsAsync(\n new GetTokenRequest\n {\n XApiKey = \"X-Api-Key\",\n ClientId = \"client_id\",\n ClientSecret = \"client_secret\",\n Audience = \"https://api.example.com\",\n GrantType = \"client_credentials\",\n Scope = \"scope\",\n }\n);\n" + "client": "using SeedInferredAuthImplicit;\n\nvar client = new SeedInferredAuthImplicitClient(\"X-Api-Key\", \"client_id\", \"client_secret\", \"scope\");\nawait client.Auth.GetTokenWithClientCredentialsAsync(\n new GetTokenRequest\n {\n XApiKey = \"X-Api-Key\",\n ClientId = \"client_id\",\n ClientSecret = \"client_secret\",\n Audience = \"https://api.example.com\",\n GrantType = \"client_credentials\",\n Scope = \"scope\",\n }\n);\n" } }, { @@ -22,7 +22,7 @@ }, "snippet": { "type": "csharp", - "client": "using SeedInferredAuthImplicit;\n\nvar client = new SeedInferredAuthImplicitClient();\nawait client.Auth.RefreshTokenAsync(\n new RefreshTokenRequest\n {\n XApiKey = \"X-Api-Key\",\n ClientId = \"client_id\",\n ClientSecret = \"client_secret\",\n RefreshToken = \"refresh_token\",\n Audience = \"https://api.example.com\",\n GrantType = \"refresh_token\",\n Scope = \"scope\",\n }\n);\n" + "client": "using SeedInferredAuthImplicit;\n\nvar client = new SeedInferredAuthImplicitClient(\"X-Api-Key\", \"client_id\", \"client_secret\", \"scope\");\nawait client.Auth.RefreshTokenAsync(\n new RefreshTokenRequest\n {\n XApiKey = \"X-Api-Key\",\n ClientId = \"client_id\",\n ClientSecret = \"client_secret\",\n RefreshToken = \"refresh_token\",\n Audience = \"https://api.example.com\",\n GrantType = \"refresh_token\",\n Scope = \"scope\",\n }\n);\n" } }, { @@ -34,7 +34,7 @@ }, "snippet": { "type": "csharp", - "client": "using SeedInferredAuthImplicit;\n\nvar client = new SeedInferredAuthImplicitClient();\nawait client.NestedNoAuth.Api.GetSomethingAsync();\n" + "client": "using SeedInferredAuthImplicit;\n\nvar client = new SeedInferredAuthImplicitClient(\"X-Api-Key\", \"client_id\", \"client_secret\", \"scope\");\nawait client.NestedNoAuth.Api.GetSomethingAsync();\n" } }, { @@ -46,7 +46,7 @@ }, "snippet": { "type": "csharp", - "client": "using SeedInferredAuthImplicit;\n\nvar client = new SeedInferredAuthImplicitClient();\nawait client.Nested.Api.GetSomethingAsync();\n" + "client": "using SeedInferredAuthImplicit;\n\nvar client = new SeedInferredAuthImplicitClient(\"X-Api-Key\", \"client_id\", \"client_secret\", \"scope\");\nawait client.Nested.Api.GetSomethingAsync();\n" } }, { @@ -58,7 +58,7 @@ }, "snippet": { "type": "csharp", - "client": "using SeedInferredAuthImplicit;\n\nvar client = new SeedInferredAuthImplicitClient();\nawait client.Simple.GetSomethingAsync();\n" + "client": "using SeedInferredAuthImplicit;\n\nvar client = new SeedInferredAuthImplicitClient(\"X-Api-Key\", \"client_id\", \"client_secret\", \"scope\");\nawait client.Simple.GetSomethingAsync();\n" } } ] diff --git a/seed/csharp-sdk/inferred-auth-implicit/src/SeedApi.DynamicSnippets/Example0.cs b/seed/csharp-sdk/inferred-auth-implicit/src/SeedApi.DynamicSnippets/Example0.cs index a095f3ef2caf..d0fd27bd125f 100644 --- a/seed/csharp-sdk/inferred-auth-implicit/src/SeedApi.DynamicSnippets/Example0.cs +++ b/seed/csharp-sdk/inferred-auth-implicit/src/SeedApi.DynamicSnippets/Example0.cs @@ -6,6 +6,10 @@ public class Example0 { public async Task Do() { var client = new SeedInferredAuthImplicitClient( + xApiKey: "X-Api-Key", + clientId: "client_id", + clientSecret: "client_secret", + scope: "scope", clientOptions: new ClientOptions { BaseUrl = "https://api.fern.com" } diff --git a/seed/csharp-sdk/inferred-auth-implicit/src/SeedApi.DynamicSnippets/Example1.cs b/seed/csharp-sdk/inferred-auth-implicit/src/SeedApi.DynamicSnippets/Example1.cs index 866eb4a86f7e..5205c19760b8 100644 --- a/seed/csharp-sdk/inferred-auth-implicit/src/SeedApi.DynamicSnippets/Example1.cs +++ b/seed/csharp-sdk/inferred-auth-implicit/src/SeedApi.DynamicSnippets/Example1.cs @@ -6,6 +6,10 @@ public class Example1 { public async Task Do() { var client = new SeedInferredAuthImplicitClient( + xApiKey: "X-Api-Key", + clientId: "client_id", + clientSecret: "client_secret", + scope: "scope", clientOptions: new ClientOptions { BaseUrl = "https://api.fern.com" } diff --git a/seed/csharp-sdk/inferred-auth-implicit/src/SeedApi.DynamicSnippets/Example2.cs b/seed/csharp-sdk/inferred-auth-implicit/src/SeedApi.DynamicSnippets/Example2.cs index a25fcc33aa2b..0676fdfe0539 100644 --- a/seed/csharp-sdk/inferred-auth-implicit/src/SeedApi.DynamicSnippets/Example2.cs +++ b/seed/csharp-sdk/inferred-auth-implicit/src/SeedApi.DynamicSnippets/Example2.cs @@ -6,6 +6,10 @@ public class Example2 { public async Task Do() { var client = new SeedInferredAuthImplicitClient( + xApiKey: "X-Api-Key", + clientId: "client_id", + clientSecret: "client_secret", + scope: "scope", clientOptions: new ClientOptions { BaseUrl = "https://api.fern.com" } diff --git a/seed/csharp-sdk/inferred-auth-implicit/src/SeedApi.DynamicSnippets/Example3.cs b/seed/csharp-sdk/inferred-auth-implicit/src/SeedApi.DynamicSnippets/Example3.cs index b2bce9fb20e5..9fe490b57a47 100644 --- a/seed/csharp-sdk/inferred-auth-implicit/src/SeedApi.DynamicSnippets/Example3.cs +++ b/seed/csharp-sdk/inferred-auth-implicit/src/SeedApi.DynamicSnippets/Example3.cs @@ -6,6 +6,10 @@ public class Example3 { public async Task Do() { var client = new SeedInferredAuthImplicitClient( + xApiKey: "X-Api-Key", + clientId: "client_id", + clientSecret: "client_secret", + scope: "scope", clientOptions: new ClientOptions { BaseUrl = "https://api.fern.com" } diff --git a/seed/csharp-sdk/inferred-auth-implicit/src/SeedApi.DynamicSnippets/Example4.cs b/seed/csharp-sdk/inferred-auth-implicit/src/SeedApi.DynamicSnippets/Example4.cs index 0f64e8cabcce..7ff7d5c9e69d 100644 --- a/seed/csharp-sdk/inferred-auth-implicit/src/SeedApi.DynamicSnippets/Example4.cs +++ b/seed/csharp-sdk/inferred-auth-implicit/src/SeedApi.DynamicSnippets/Example4.cs @@ -6,6 +6,10 @@ public class Example4 { public async Task Do() { var client = new SeedInferredAuthImplicitClient( + xApiKey: "X-Api-Key", + clientId: "client_id", + clientSecret: "client_secret", + scope: "scope", clientOptions: new ClientOptions { BaseUrl = "https://api.fern.com" } diff --git a/seed/csharp-sdk/inferred-auth-implicit/src/SeedInferredAuthImplicit.Test/Unit/MockServer/BaseMockServerTest.cs b/seed/csharp-sdk/inferred-auth-implicit/src/SeedInferredAuthImplicit.Test/Unit/MockServer/BaseMockServerTest.cs index 307fa92492d8..18d6051c5701 100644 --- a/seed/csharp-sdk/inferred-auth-implicit/src/SeedInferredAuthImplicit.Test/Unit/MockServer/BaseMockServerTest.cs +++ b/seed/csharp-sdk/inferred-auth-implicit/src/SeedInferredAuthImplicit.Test/Unit/MockServer/BaseMockServerTest.cs @@ -15,6 +15,43 @@ public class BaseMockServerTest protected static RequestOptions RequestOptions { get; set; } = new(); + private void MockInferredAuthEndpoint() + { + const string requestJson = """ + { + "client_id": "client_id", + "client_secret": "client_secret", + "audience": "https://api.example.com", + "grant_type": "client_credentials", + "scope": "scope" + } + """; + + const string mockResponse = """ + { + "access_token": "access_token", + "expires_in": 1, + "refresh_token": "refresh_token" + } + """; + + Server + .Given( + WireMock + .RequestBuilders.Request.Create() + .WithPath("/token") + .WithHeader("X-Api-Key", "X-Api-Key") + .UsingPost() + .WithBodyAsJson(requestJson) + ) + .RespondWith( + WireMock + .ResponseBuilders.Response.Create() + .WithStatusCode(200) + .WithBody(mockResponse) + ); + } + [OneTimeSetUp] public void GlobalSetup() { @@ -25,8 +62,13 @@ public void GlobalSetup() // Initialize the Client Client = new SeedInferredAuthImplicitClient( + "X-Api-Key", + "client_id", + "client_secret", + "scope", clientOptions: new ClientOptions { BaseUrl = Server.Urls[0], MaxRetries = 0 } ); + MockInferredAuthEndpoint(); } [OneTimeTearDown] diff --git a/seed/csharp-sdk/inferred-auth-implicit/src/SeedInferredAuthImplicit/Core/InferredAuthTokenProvider.cs b/seed/csharp-sdk/inferred-auth-implicit/src/SeedInferredAuthImplicit/Core/InferredAuthTokenProvider.cs new file mode 100644 index 000000000000..266afef013a5 --- /dev/null +++ b/seed/csharp-sdk/inferred-auth-implicit/src/SeedInferredAuthImplicit/Core/InferredAuthTokenProvider.cs @@ -0,0 +1,83 @@ +using SeedInferredAuthImplicit; + +namespace SeedInferredAuthImplicit.Core; + +internal partial class InferredAuthTokenProvider +{ + private const double BufferInMinutes = 2; + + private AuthClient _client; + + private IDictionary? _cachedHeaders; + + private DateTime? _expiresAt; + + private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1); + + private string _xApiKey; + + private string _clientId; + + private string _clientSecret; + + private string? _scope; + + internal InferredAuthTokenProvider( + string xApiKey, + string clientId, + string clientSecret, + string? scope, + AuthClient client + ) + { + _xApiKey = xApiKey; + _clientId = clientId; + _clientSecret = clientSecret; + _scope = scope; + _client = client; + } + + internal async Task> GetAuthHeadersAsync() + { + if (_cachedHeaders == null || DateTime.UtcNow >= _expiresAt) + { + await _lock.WaitAsync().ConfigureAwait(false); + try + { + if (_cachedHeaders == null || DateTime.UtcNow >= _expiresAt) + { + try + { + var tokenResponse = await _client + .GetTokenWithClientCredentialsAsync( + new GetTokenRequest + { + XApiKey = _xApiKey, + ClientId = _clientId, + ClientSecret = _clientSecret, + Scope = _scope, + } + ) + .ConfigureAwait(false); + _cachedHeaders = new Dictionary(); + _cachedHeaders["Authorization"] = $"Bearer {tokenResponse.AccessToken}"; + _expiresAt = DateTime + .UtcNow.AddSeconds(tokenResponse.ExpiresIn) + .AddMinutes(-BufferInMinutes); + } + catch + { + _cachedHeaders = null; + _expiresAt = null; + throw; + } + } + } + finally + { + _lock.Release(); + } + } + return _cachedHeaders; + } +} diff --git a/seed/csharp-sdk/inferred-auth-implicit/src/SeedInferredAuthImplicit/SeedInferredAuthImplicitClient.cs b/seed/csharp-sdk/inferred-auth-implicit/src/SeedInferredAuthImplicit/SeedInferredAuthImplicitClient.cs index 0925e9424563..2fdec21de1c3 100644 --- a/seed/csharp-sdk/inferred-auth-implicit/src/SeedInferredAuthImplicit/SeedInferredAuthImplicitClient.cs +++ b/seed/csharp-sdk/inferred-auth-implicit/src/SeedInferredAuthImplicit/SeedInferredAuthImplicitClient.cs @@ -8,7 +8,13 @@ public partial class SeedInferredAuthImplicitClient { private readonly RawClient _client; - public SeedInferredAuthImplicitClient(ClientOptions? clientOptions = null) + public SeedInferredAuthImplicitClient( + string xApiKey, + string clientId, + string clientSecret, + string? scope = null, + ClientOptions? clientOptions = null + ) { var defaultHeaders = new Headers( new Dictionary() @@ -27,6 +33,19 @@ public SeedInferredAuthImplicitClient(ClientOptions? clientOptions = null) clientOptions.Headers[header.Key] = header.Value; } } + var inferredAuthProvider = new InferredAuthTokenProvider( + xApiKey, + clientId, + clientSecret, + scope, + new AuthClient(new RawClient(clientOptions.Clone())) + ); + clientOptions.Headers["Authorization"] = + new Func>(async () => + (await inferredAuthProvider.GetAuthHeadersAsync().ConfigureAwait(false)) + .First() + .Value + ); _client = new RawClient(clientOptions); Auth = new AuthClient(_client); NestedNoAuth = new NestedNoAuthClient(_client); diff --git a/seed/csharp-sdk/oauth-client-credentials-mandatory-auth/src/SeedOauthClientCredentialsMandatoryAuth/SeedOauthClientCredentialsMandatoryAuthClient.cs b/seed/csharp-sdk/oauth-client-credentials-mandatory-auth/src/SeedOauthClientCredentialsMandatoryAuth/SeedOauthClientCredentialsMandatoryAuthClient.cs index bcaecc14d201..84397505d6c0 100644 --- a/seed/csharp-sdk/oauth-client-credentials-mandatory-auth/src/SeedOauthClientCredentialsMandatoryAuth/SeedOauthClientCredentialsMandatoryAuthClient.cs +++ b/seed/csharp-sdk/oauth-client-credentials-mandatory-auth/src/SeedOauthClientCredentialsMandatoryAuth/SeedOauthClientCredentialsMandatoryAuthClient.cs @@ -37,10 +37,8 @@ public SeedOauthClientCredentialsMandatoryAuthClient( ); clientOptions.Headers["Authorization"] = new Func>(async () => - { - var result = await tokenProvider.GetAccessTokenAsync().ConfigureAwait(false); - return result; - }); + await tokenProvider.GetAccessTokenAsync().ConfigureAwait(false) + ); _client = new RawClient(clientOptions); Auth = new AuthClient(_client); Nested = new NestedClient(_client); diff --git a/seed/csharp-sdk/oauth-client-credentials-with-variables/src/SeedOauthClientCredentialsWithVariables/SeedOauthClientCredentialsWithVariablesClient.cs b/seed/csharp-sdk/oauth-client-credentials-with-variables/src/SeedOauthClientCredentialsWithVariables/SeedOauthClientCredentialsWithVariablesClient.cs index 498748a85119..1018e846a692 100644 --- a/seed/csharp-sdk/oauth-client-credentials-with-variables/src/SeedOauthClientCredentialsWithVariables/SeedOauthClientCredentialsWithVariablesClient.cs +++ b/seed/csharp-sdk/oauth-client-credentials-with-variables/src/SeedOauthClientCredentialsWithVariables/SeedOauthClientCredentialsWithVariablesClient.cs @@ -38,10 +38,8 @@ public SeedOauthClientCredentialsWithVariablesClient( ); clientOptions.Headers["Authorization"] = new Func>(async () => - { - var result = await tokenProvider.GetAccessTokenAsync().ConfigureAwait(false); - return result; - }); + await tokenProvider.GetAccessTokenAsync().ConfigureAwait(false) + ); _client = new RawClient(clientOptions); Auth = new AuthClient(_client); NestedNoAuth = new NestedNoAuthClient(_client); diff --git a/seed/csharp-sdk/seed.yml b/seed/csharp-sdk/seed.yml index 5df1e9fc5bc4..c9031c4cedd1 100644 --- a/seed/csharp-sdk/seed.yml +++ b/seed/csharp-sdk/seed.yml @@ -1,5 +1,5 @@ displayName: C# SDK -irVersion: v61 +irVersion: v62 image: fernapi/fern-csharp-sdk changelogLocation: ../../generators/csharp/sdk/versions.yml diff --git a/seed/csharp-sdk/websocket-inferred-auth/README.md b/seed/csharp-sdk/websocket-inferred-auth/README.md index 05f8772a92e7..0422bc4561bd 100644 --- a/seed/csharp-sdk/websocket-inferred-auth/README.md +++ b/seed/csharp-sdk/websocket-inferred-auth/README.md @@ -38,7 +38,7 @@ Instantiate and use the client with the following: ```csharp using SeedWebsocketAuth; -var client = new SeedWebsocketAuthClient(); +var client = new SeedWebsocketAuthClient("X-Api-Key", "client_id", "client_secret", "scope"); await client.Auth.GetTokenWithClientCredentialsAsync( new GetTokenRequest { diff --git a/seed/csharp-sdk/websocket-inferred-auth/snippet.json b/seed/csharp-sdk/websocket-inferred-auth/snippet.json index ea794c9e3c59..552856ff2dc5 100644 --- a/seed/csharp-sdk/websocket-inferred-auth/snippet.json +++ b/seed/csharp-sdk/websocket-inferred-auth/snippet.json @@ -10,7 +10,7 @@ }, "snippet": { "type": "csharp", - "client": "using SeedWebsocketAuth;\n\nvar client = new SeedWebsocketAuthClient();\nawait client.Auth.GetTokenWithClientCredentialsAsync(\n new GetTokenRequest\n {\n XApiKey = \"X-Api-Key\",\n ClientId = \"client_id\",\n ClientSecret = \"client_secret\",\n Audience = \"https://api.example.com\",\n GrantType = \"client_credentials\",\n Scope = \"scope\",\n }\n);\n" + "client": "using SeedWebsocketAuth;\n\nvar client = new SeedWebsocketAuthClient(\"X-Api-Key\", \"client_id\", \"client_secret\", \"scope\");\nawait client.Auth.GetTokenWithClientCredentialsAsync(\n new GetTokenRequest\n {\n XApiKey = \"X-Api-Key\",\n ClientId = \"client_id\",\n ClientSecret = \"client_secret\",\n Audience = \"https://api.example.com\",\n GrantType = \"client_credentials\",\n Scope = \"scope\",\n }\n);\n" } }, { @@ -22,7 +22,7 @@ }, "snippet": { "type": "csharp", - "client": "using SeedWebsocketAuth;\n\nvar client = new SeedWebsocketAuthClient();\nawait client.Auth.RefreshTokenAsync(\n new RefreshTokenRequest\n {\n XApiKey = \"X-Api-Key\",\n ClientId = \"client_id\",\n ClientSecret = \"client_secret\",\n RefreshToken = \"refresh_token\",\n Audience = \"https://api.example.com\",\n GrantType = \"refresh_token\",\n Scope = \"scope\",\n }\n);\n" + "client": "using SeedWebsocketAuth;\n\nvar client = new SeedWebsocketAuthClient(\"X-Api-Key\", \"client_id\", \"client_secret\", \"scope\");\nawait client.Auth.RefreshTokenAsync(\n new RefreshTokenRequest\n {\n XApiKey = \"X-Api-Key\",\n ClientId = \"client_id\",\n ClientSecret = \"client_secret\",\n RefreshToken = \"refresh_token\",\n Audience = \"https://api.example.com\",\n GrantType = \"refresh_token\",\n Scope = \"scope\",\n }\n);\n" } } ] diff --git a/seed/csharp-sdk/websocket-inferred-auth/src/SeedApi.DynamicSnippets/Example0.cs b/seed/csharp-sdk/websocket-inferred-auth/src/SeedApi.DynamicSnippets/Example0.cs index 406d67af327a..3cdd1e6f2909 100644 --- a/seed/csharp-sdk/websocket-inferred-auth/src/SeedApi.DynamicSnippets/Example0.cs +++ b/seed/csharp-sdk/websocket-inferred-auth/src/SeedApi.DynamicSnippets/Example0.cs @@ -6,6 +6,10 @@ public class Example0 { public async Task Do() { var client = new SeedWebsocketAuthClient( + xApiKey: "X-Api-Key", + clientId: "client_id", + clientSecret: "client_secret", + scope: "scope", clientOptions: new ClientOptions { BaseUrl = "https://api.fern.com" } diff --git a/seed/csharp-sdk/websocket-inferred-auth/src/SeedApi.DynamicSnippets/Example1.cs b/seed/csharp-sdk/websocket-inferred-auth/src/SeedApi.DynamicSnippets/Example1.cs index f584aa57c20e..f1c1c3f467e9 100644 --- a/seed/csharp-sdk/websocket-inferred-auth/src/SeedApi.DynamicSnippets/Example1.cs +++ b/seed/csharp-sdk/websocket-inferred-auth/src/SeedApi.DynamicSnippets/Example1.cs @@ -6,6 +6,10 @@ public class Example1 { public async Task Do() { var client = new SeedWebsocketAuthClient( + xApiKey: "X-Api-Key", + clientId: "client_id", + clientSecret: "client_secret", + scope: "scope", clientOptions: new ClientOptions { BaseUrl = "https://api.fern.com" } diff --git a/seed/csharp-sdk/websocket-inferred-auth/src/SeedWebsocketAuth.Test/Unit/MockServer/BaseMockServerTest.cs b/seed/csharp-sdk/websocket-inferred-auth/src/SeedWebsocketAuth.Test/Unit/MockServer/BaseMockServerTest.cs index ce3182c7d284..c67f36ccfa73 100644 --- a/seed/csharp-sdk/websocket-inferred-auth/src/SeedWebsocketAuth.Test/Unit/MockServer/BaseMockServerTest.cs +++ b/seed/csharp-sdk/websocket-inferred-auth/src/SeedWebsocketAuth.Test/Unit/MockServer/BaseMockServerTest.cs @@ -15,6 +15,42 @@ public class BaseMockServerTest protected static RequestOptions RequestOptions { get; set; } = new(); + private void MockInferredAuthEndpoint() + { + const string requestJson = """ + { + "client_id": "client_id", + "client_secret": "client_secret", + "audience": "https://api.example.com", + "grant_type": "client_credentials", + "scope": "scope" + } + """; + + const string mockResponse = """ + { + "access_token": "access_token", + "refresh_token": "refresh_token" + } + """; + + Server + .Given( + WireMock + .RequestBuilders.Request.Create() + .WithPath("/token") + .WithHeader("X-Api-Key", "X-Api-Key") + .UsingPost() + .WithBodyAsJson(requestJson) + ) + .RespondWith( + WireMock + .ResponseBuilders.Response.Create() + .WithStatusCode(200) + .WithBody(mockResponse) + ); + } + [OneTimeSetUp] public void GlobalSetup() { @@ -25,8 +61,13 @@ public void GlobalSetup() // Initialize the Client Client = new SeedWebsocketAuthClient( + "X-Api-Key", + "client_id", + "client_secret", + "scope", clientOptions: new ClientOptions { BaseUrl = Server.Urls[0], MaxRetries = 0 } ); + MockInferredAuthEndpoint(); } [OneTimeTearDown] diff --git a/seed/csharp-sdk/websocket-inferred-auth/src/SeedWebsocketAuth/Core/InferredAuthTokenProvider.cs b/seed/csharp-sdk/websocket-inferred-auth/src/SeedWebsocketAuth/Core/InferredAuthTokenProvider.cs new file mode 100644 index 000000000000..fa9933408854 --- /dev/null +++ b/seed/csharp-sdk/websocket-inferred-auth/src/SeedWebsocketAuth/Core/InferredAuthTokenProvider.cs @@ -0,0 +1,49 @@ +using SeedWebsocketAuth; + +namespace SeedWebsocketAuth.Core; + +internal partial class InferredAuthTokenProvider +{ + private AuthClient _client; + + private string _xApiKey; + + private string _clientId; + + private string _clientSecret; + + private string? _scope; + + internal InferredAuthTokenProvider( + string xApiKey, + string clientId, + string clientSecret, + string? scope, + AuthClient client + ) + { + _xApiKey = xApiKey; + _clientId = clientId; + _clientSecret = clientSecret; + _scope = scope; + _client = client; + } + + internal async Task> GetAuthHeadersAsync() + { + var tokenResponse = await _client + .GetTokenWithClientCredentialsAsync( + new GetTokenRequest + { + XApiKey = _xApiKey, + ClientId = _clientId, + ClientSecret = _clientSecret, + Scope = _scope, + } + ) + .ConfigureAwait(false); + var headers = new Dictionary(); + headers["Authorization"] = $"Bearer {tokenResponse.AccessToken}"; + return headers; + } +} diff --git a/seed/csharp-sdk/websocket-inferred-auth/src/SeedWebsocketAuth/SeedWebsocketAuthClient.cs b/seed/csharp-sdk/websocket-inferred-auth/src/SeedWebsocketAuth/SeedWebsocketAuthClient.cs index e88ddd26f882..b269ff8d42e9 100644 --- a/seed/csharp-sdk/websocket-inferred-auth/src/SeedWebsocketAuth/SeedWebsocketAuthClient.cs +++ b/seed/csharp-sdk/websocket-inferred-auth/src/SeedWebsocketAuth/SeedWebsocketAuthClient.cs @@ -6,7 +6,13 @@ public partial class SeedWebsocketAuthClient { private readonly RawClient _client; - public SeedWebsocketAuthClient(ClientOptions? clientOptions = null) + public SeedWebsocketAuthClient( + string xApiKey, + string clientId, + string clientSecret, + string? scope = null, + ClientOptions? clientOptions = null + ) { var defaultHeaders = new Headers( new Dictionary() @@ -25,6 +31,19 @@ public SeedWebsocketAuthClient(ClientOptions? clientOptions = null) clientOptions.Headers[header.Key] = header.Value; } } + var inferredAuthProvider = new InferredAuthTokenProvider( + xApiKey, + clientId, + clientSecret, + scope, + new AuthClient(new RawClient(clientOptions.Clone())) + ); + clientOptions.Headers["Authorization"] = + new Func>(async () => + (await inferredAuthProvider.GetAuthHeadersAsync().ConfigureAwait(false)) + .First() + .Value + ); _client = new RawClient(clientOptions); Auth = new AuthClient(_client); } diff --git a/seed/go-fiber/idempotency-headers/.github/workflows/ci.yml b/seed/go-fiber/idempotency-headers/.github/workflows/ci.yml new file mode 100644 index 000000000000..56310d69624b --- /dev/null +++ b/seed/go-fiber/idempotency-headers/.github/workflows/ci.yml @@ -0,0 +1,35 @@ +name: ci + +on: [push] + +jobs: + compile: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up go + uses: actions/setup-go@v4 + + - name: Compile + run: go build ./... + test: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up go + uses: actions/setup-go@v4 + + - name: Setup wiremock server + run: | + if [ -f wiremock/docker-compose.test.yml ]; then docker compose -f wiremock/docker-compose.test.yml down && docker compose -f wiremock/docker-compose.test.yml up -d; fi + + - name: Test + run: go test ./... + + - name: Teardown wiremock server + run: | + if [ -f wiremock/docker-compose.test.yml ]; then docker compose -f wiremock/docker-compose.test.yml down; fi diff --git a/seed/go-fiber/idempotency-headers/internal/explicit_fields.go b/seed/go-fiber/idempotency-headers/internal/explicit_fields.go new file mode 100644 index 000000000000..4bdf34fc2b7c --- /dev/null +++ b/seed/go-fiber/idempotency-headers/internal/explicit_fields.go @@ -0,0 +1,116 @@ +package internal + +import ( + "math/big" + "reflect" + "strings" +) + +// HandleExplicitFields processes a struct to remove `omitempty` from +// fields that have been explicitly set (as indicated by their corresponding bit in explicitFields). +// Note that `marshaler` should be an embedded struct to avoid infinite recursion. +// Returns an interface{} that can be passed to json.Marshal. +func HandleExplicitFields(marshaler interface{}, explicitFields *big.Int) interface{} { + val := reflect.ValueOf(marshaler) + typ := reflect.TypeOf(marshaler) + + // Handle pointer types + if val.Kind() == reflect.Ptr { + if val.IsNil() { + return nil + } + val = val.Elem() + typ = typ.Elem() + } + + // Only handle struct types + if val.Kind() != reflect.Struct { + return marshaler + } + + // Handle embedded struct pattern + var sourceVal reflect.Value + var sourceType reflect.Type + + // Check if this is an embedded struct pattern + if typ.NumField() == 1 && typ.Field(0).Anonymous { + // This is likely an embedded struct, get the embedded value + embeddedField := val.Field(0) + sourceVal = embeddedField + sourceType = embeddedField.Type() + } else { + // Regular struct + sourceVal = val + sourceType = typ + } + + // If no explicit fields set, use standard marshaling + if explicitFields == nil || explicitFields.Sign() == 0 { + return marshaler + } + + // Create a new struct type with modified tags + fields := make([]reflect.StructField, 0, sourceType.NumField()) + + for i := 0; i < sourceType.NumField(); i++ { + field := sourceType.Field(i) + + // Skip unexported fields and the explicitFields field itself + if !field.IsExported() || field.Name == "explicitFields" { + continue + } + + // Check if this field has been explicitly set + fieldBit := big.NewInt(1) + fieldBit.Lsh(fieldBit, uint(i)) + if big.NewInt(0).And(explicitFields, fieldBit).Sign() != 0 { + // Remove omitempty from the json tag + tag := field.Tag.Get("json") + if tag != "" && tag != "-" { + // Parse the json tag, remove omitempty from options + parts := strings.Split(tag, ",") + if len(parts) > 1 { + var newParts []string + newParts = append(newParts, parts[0]) // Keep the field name + for _, part := range parts[1:] { + if strings.TrimSpace(part) != "omitempty" { + newParts = append(newParts, part) + } + } + tag = strings.Join(newParts, ",") + } + + // Reconstruct the struct tag + newTag := `json:"` + tag + `"` + if urlTag := field.Tag.Get("url"); urlTag != "" { + newTag += ` url:"` + urlTag + `"` + } + + field.Tag = reflect.StructTag(newTag) + } + } + + fields = append(fields, field) + } + + // Create new struct type with modified tags + newType := reflect.StructOf(fields) + newVal := reflect.New(newType).Elem() + + // Copy field values from original struct to new struct + fieldIndex := 0 + for i := 0; i < sourceType.NumField(); i++ { + originalField := sourceType.Field(i) + + // Skip unexported fields and the explicitFields field itself + if !originalField.IsExported() || originalField.Name == "explicitFields" { + continue + } + + originalValue := sourceVal.Field(i) + newVal.Field(fieldIndex).Set(originalValue) + fieldIndex++ + } + + return newVal.Interface() +} diff --git a/seed/go-fiber/idempotency-headers/internal/explicit_fields_test.go b/seed/go-fiber/idempotency-headers/internal/explicit_fields_test.go new file mode 100644 index 000000000000..d3ec507de14b --- /dev/null +++ b/seed/go-fiber/idempotency-headers/internal/explicit_fields_test.go @@ -0,0 +1,497 @@ +package internal + +import ( + "encoding/json" + "math/big" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type testExplicitFieldsStruct struct { + Name *string `json:"name,omitempty"` + Code *string `json:"code,omitempty"` + Count *int `json:"count,omitempty"` + Enabled *bool `json:"enabled,omitempty"` + Tags []string `json:"tags,omitempty"` + //lint:ignore unused this field is intentionally unused for testing + unexported string `json:"-"` + explicitFields *big.Int `json:"-"` +} + +var ( + testFieldName = big.NewInt(1 << 0) + testFieldCode = big.NewInt(1 << 1) + testFieldCount = big.NewInt(1 << 2) + testFieldEnabled = big.NewInt(1 << 3) + testFieldTags = big.NewInt(1 << 4) +) + +func (t *testExplicitFieldsStruct) require(field *big.Int) { + if t.explicitFields == nil { + t.explicitFields = big.NewInt(0) + } + t.explicitFields.Or(t.explicitFields, field) +} + +func (t *testExplicitFieldsStruct) SetName(name *string) { + t.Name = name + t.require(testFieldName) +} + +func (t *testExplicitFieldsStruct) SetCode(code *string) { + t.Code = code + t.require(testFieldCode) +} + +func (t *testExplicitFieldsStruct) SetCount(count *int) { + t.Count = count + t.require(testFieldCount) +} + +func (t *testExplicitFieldsStruct) SetEnabled(enabled *bool) { + t.Enabled = enabled + t.require(testFieldEnabled) +} + +func (t *testExplicitFieldsStruct) SetTags(tags []string) { + t.Tags = tags + t.require(testFieldTags) +} + +func (t *testExplicitFieldsStruct) MarshalJSON() ([]byte, error) { + type embed testExplicitFieldsStruct + var marshaler = struct { + embed + }{ + embed: embed(*t), + } + return json.Marshal(HandleExplicitFields(marshaler, t.explicitFields)) +} + +type testStructWithoutExplicitFields struct { + Name *string `json:"name,omitempty"` + Code *string `json:"code,omitempty"` +} + +func TestHandleExplicitFields(t *testing.T) { + tests := []struct { + desc string + giveInput interface{} + wantBytes []byte + wantError string + }{ + { + desc: "nil input", + giveInput: nil, + wantBytes: []byte(`null`), + }, + { + desc: "non-struct input", + giveInput: "string", + wantBytes: []byte(`"string"`), + }, + { + desc: "slice input", + giveInput: []string{"a", "b"}, + wantBytes: []byte(`["a","b"]`), + }, + { + desc: "map input", + giveInput: map[string]interface{}{"key": "value"}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "struct without explicitFields field", + giveInput: &testStructWithoutExplicitFields{ + Name: stringPtr("test"), + Code: nil, + }, + wantBytes: []byte(`{"name":"test"}`), + }, + { + desc: "struct with no explicit fields set", + giveInput: &testExplicitFieldsStruct{ + Name: stringPtr("test"), + Code: nil, + }, + wantBytes: []byte(`{"name":"test"}`), + }, + { + desc: "struct with explicit nil field", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{ + Name: stringPtr("test"), + } + s.SetCode(nil) + return s + }(), + wantBytes: []byte(`{"name":"test","code":null}`), + }, + { + desc: "struct with explicit non-nil field", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{} + s.SetName(stringPtr("explicit")) + s.SetCode(stringPtr("also-explicit")) + return s + }(), + wantBytes: []byte(`{"name":"explicit","code":"also-explicit"}`), + }, + { + desc: "struct with mixed explicit and implicit fields", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{ + Name: stringPtr("implicit"), + Count: intPtr(42), + } + s.SetCode(nil) // explicit nil + return s + }(), + wantBytes: []byte(`{"name":"implicit","code":null,"count":42}`), + }, + { + desc: "struct with multiple explicit nil fields", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{ + Name: stringPtr("test"), + } + s.SetCode(nil) + s.SetCount(nil) + return s + }(), + wantBytes: []byte(`{"name":"test","code":null,"count":null}`), + }, + { + desc: "struct with slice field", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{ + Tags: []string{"tag1", "tag2"}, + } + s.SetTags(nil) // explicit nil slice + return s + }(), + wantBytes: []byte(`{"tags":null}`), + }, + { + desc: "struct with boolean field", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{} + s.SetEnabled(boolPtr(false)) // explicit false + return s + }(), + wantBytes: []byte(`{"enabled":false}`), + }, + { + desc: "struct with all fields explicit", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{} + s.SetName(stringPtr("test")) + s.SetCode(nil) + s.SetCount(intPtr(0)) + s.SetEnabled(boolPtr(false)) + s.SetTags([]string{}) + return s + }(), + wantBytes: []byte(`{"name":"test","code":null,"count":0,"enabled":false,"tags":[]}`), + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + var explicitFields *big.Int + if s, ok := tt.giveInput.(*testExplicitFieldsStruct); ok { + explicitFields = s.explicitFields + } + bytes, err := json.Marshal(HandleExplicitFields(tt.giveInput, explicitFields)) + if tt.wantError != "" { + require.EqualError(t, err, tt.wantError) + assert.Nil(t, tt.wantBytes) + return + } + require.NoError(t, err) + assert.JSONEq(t, string(tt.wantBytes), string(bytes)) + + // Verify it's valid JSON + var value interface{} + require.NoError(t, json.Unmarshal(bytes, &value)) + }) + } +} + +func TestHandleExplicitFieldsCustomMarshaler(t *testing.T) { + t.Run("custom marshaler with explicit fields", func(t *testing.T) { + s := &testExplicitFieldsStruct{} + s.SetName(nil) + s.SetCode(stringPtr("test-code")) + + bytes, err := s.MarshalJSON() + require.NoError(t, err) + assert.JSONEq(t, `{"name":null,"code":"test-code"}`, string(bytes)) + }) + + t.Run("custom marshaler with no explicit fields", func(t *testing.T) { + s := &testExplicitFieldsStruct{ + Name: stringPtr("implicit"), + Code: stringPtr("also-implicit"), + } + + bytes, err := s.MarshalJSON() + require.NoError(t, err) + assert.JSONEq(t, `{"name":"implicit","code":"also-implicit"}`, string(bytes)) + }) +} + +func TestHandleExplicitFieldsPointerHandling(t *testing.T) { + t.Run("nil pointer", func(t *testing.T) { + var s *testExplicitFieldsStruct + bytes, err := json.Marshal(HandleExplicitFields(s, nil)) + require.NoError(t, err) + assert.Equal(t, []byte(`null`), bytes) + }) + + t.Run("pointer to struct", func(t *testing.T) { + s := &testExplicitFieldsStruct{} + s.SetName(nil) + + bytes, err := json.Marshal(HandleExplicitFields(s, s.explicitFields)) + require.NoError(t, err) + assert.JSONEq(t, `{"name":null}`, string(bytes)) + }) +} + +func TestHandleExplicitFieldsEmbeddedStruct(t *testing.T) { + t.Run("embedded struct with explicit fields", func(t *testing.T) { + // Create a struct similar to what MarshalJSON creates + s := &testExplicitFieldsStruct{} + s.SetName(nil) + s.SetCode(stringPtr("test-code")) + + type embed testExplicitFieldsStruct + var marshaler = struct { + embed + }{ + embed: embed(*s), + } + + bytes, err := json.Marshal(HandleExplicitFields(marshaler, s.explicitFields)) + require.NoError(t, err) + // Should include both explicit fields (name as null, code as "test-code") + assert.JSONEq(t, `{"name":null,"code":"test-code"}`, string(bytes)) + }) + + t.Run("embedded struct with no explicit fields", func(t *testing.T) { + s := &testExplicitFieldsStruct{ + Name: stringPtr("implicit"), + Code: stringPtr("also-implicit"), + } + + type embed testExplicitFieldsStruct + var marshaler = struct { + embed + }{ + embed: embed(*s), + } + + bytes, err := json.Marshal(HandleExplicitFields(marshaler, s.explicitFields)) + require.NoError(t, err) + // Should only include non-nil fields (omitempty behavior) + assert.JSONEq(t, `{"name":"implicit","code":"also-implicit"}`, string(bytes)) + }) + + t.Run("embedded struct with mixed fields", func(t *testing.T) { + s := &testExplicitFieldsStruct{ + Count: intPtr(42), // implicit field + } + s.SetName(nil) // explicit nil + s.SetCode(stringPtr("explicit")) // explicit value + + type embed testExplicitFieldsStruct + var marshaler = struct { + embed + }{ + embed: embed(*s), + } + + bytes, err := json.Marshal(HandleExplicitFields(marshaler, s.explicitFields)) + require.NoError(t, err) + // Should include explicit null, explicit value, and implicit value + assert.JSONEq(t, `{"name":null,"code":"explicit","count":42}`, string(bytes)) + }) +} + +func TestHandleExplicitFieldsTagHandling(t *testing.T) { + type testStructWithComplexTags struct { + Field1 *string `json:"field1,omitempty" url:"field1,omitempty"` + Field2 *string `json:"field2,omitempty,string" url:"field2"` + Field3 *string `json:"-"` + Field4 *string `json:"field4"` + explicitFields *big.Int `json:"-"` + } + + s := &testStructWithComplexTags{ + Field1: stringPtr("test1"), + Field4: stringPtr("test4"), + explicitFields: big.NewInt(1), // Only first field is explicit + } + + bytes, err := json.Marshal(HandleExplicitFields(s, s.explicitFields)) + require.NoError(t, err) + + // Field1 should have omitempty removed, Field2 should keep omitempty, Field4 should be included + assert.JSONEq(t, `{"field1":"test1","field4":"test4"}`, string(bytes)) +} + +// Test types for nested struct explicit fields testing +type testNestedStruct struct { + NestedName *string `json:"nested_name,omitempty"` + NestedCode *string `json:"nested_code,omitempty"` + explicitFields *big.Int `json:"-"` +} + +type testParentStruct struct { + ParentName *string `json:"parent_name,omitempty"` + Nested *testNestedStruct `json:"nested,omitempty"` + explicitFields *big.Int `json:"-"` +} + +var ( + nestedFieldName = big.NewInt(1 << 0) + nestedFieldCode = big.NewInt(1 << 1) +) + +var ( + parentFieldName = big.NewInt(1 << 0) + parentFieldNested = big.NewInt(1 << 1) +) + +func (n *testNestedStruct) require(field *big.Int) { + if n.explicitFields == nil { + n.explicitFields = big.NewInt(0) + } + n.explicitFields.Or(n.explicitFields, field) +} + +func (n *testNestedStruct) SetNestedName(name *string) { + n.NestedName = name + n.require(nestedFieldName) +} + +func (n *testNestedStruct) SetNestedCode(code *string) { + n.NestedCode = code + n.require(nestedFieldCode) +} + +func (n *testNestedStruct) MarshalJSON() ([]byte, error) { + type embed testNestedStruct + var marshaler = struct { + embed + }{ + embed: embed(*n), + } + return json.Marshal(HandleExplicitFields(marshaler, n.explicitFields)) +} + +func (p *testParentStruct) require(field *big.Int) { + if p.explicitFields == nil { + p.explicitFields = big.NewInt(0) + } + p.explicitFields.Or(p.explicitFields, field) +} + +func (p *testParentStruct) SetParentName(name *string) { + p.ParentName = name + p.require(parentFieldName) +} + +func (p *testParentStruct) SetNested(nested *testNestedStruct) { + p.Nested = nested + p.require(parentFieldNested) +} + +func (p *testParentStruct) MarshalJSON() ([]byte, error) { + type embed testParentStruct + var marshaler = struct { + embed + }{ + embed: embed(*p), + } + return json.Marshal(HandleExplicitFields(marshaler, p.explicitFields)) +} + +func TestHandleExplicitFieldsNestedStruct(t *testing.T) { + tests := []struct { + desc string + setupFunc func() *testParentStruct + wantBytes []byte + }{ + { + desc: "nested struct with explicit nil in nested object", + setupFunc: func() *testParentStruct { + nested := &testNestedStruct{ + NestedName: stringPtr("implicit-nested"), + } + nested.SetNestedCode(nil) // explicit nil + + return &testParentStruct{ + ParentName: stringPtr("implicit-parent"), + Nested: nested, + } + }, + wantBytes: []byte(`{"parent_name":"implicit-parent","nested":{"nested_name":"implicit-nested","nested_code":null}}`), + }, + { + desc: "parent with explicit nil nested struct", + setupFunc: func() *testParentStruct { + parent := &testParentStruct{ + ParentName: stringPtr("implicit-parent"), + } + parent.SetNested(nil) // explicit nil nested struct + return parent + }, + wantBytes: []byte(`{"parent_name":"implicit-parent","nested":null}`), + }, + { + desc: "all explicit fields in nested structure", + setupFunc: func() *testParentStruct { + nested := &testNestedStruct{} + nested.SetNestedName(stringPtr("explicit-nested")) + nested.SetNestedCode(nil) // explicit nil + + parent := &testParentStruct{} + parent.SetParentName(nil) // explicit nil + parent.SetNested(nested) // explicit nested struct + + return parent + }, + wantBytes: []byte(`{"parent_name":null,"nested":{"nested_name":"explicit-nested","nested_code":null}}`), + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + parent := tt.setupFunc() + bytes, err := parent.MarshalJSON() + require.NoError(t, err) + assert.JSONEq(t, string(tt.wantBytes), string(bytes)) + + // Verify it's valid JSON + var value interface{} + require.NoError(t, json.Unmarshal(bytes, &value)) + }) + } +} + +// Helper functions +func stringPtr(s string) *string { + return &s +} + +func intPtr(i int) *int { + return &i +} + +func boolPtr(b bool) *bool { + return &b +} diff --git a/seed/go-fiber/idempotency-headers/internal/extra_properties.go b/seed/go-fiber/idempotency-headers/internal/extra_properties.go new file mode 100644 index 000000000000..540c3fd89eeb --- /dev/null +++ b/seed/go-fiber/idempotency-headers/internal/extra_properties.go @@ -0,0 +1,141 @@ +package internal + +import ( + "bytes" + "encoding/json" + "fmt" + "reflect" + "strings" +) + +// MarshalJSONWithExtraProperty marshals the given value to JSON, including the extra property. +func MarshalJSONWithExtraProperty(marshaler interface{}, key string, value interface{}) ([]byte, error) { + return MarshalJSONWithExtraProperties(marshaler, map[string]interface{}{key: value}) +} + +// MarshalJSONWithExtraProperties marshals the given value to JSON, including any extra properties. +func MarshalJSONWithExtraProperties(marshaler interface{}, extraProperties map[string]interface{}) ([]byte, error) { + bytes, err := json.Marshal(marshaler) + if err != nil { + return nil, err + } + if len(extraProperties) == 0 { + return bytes, nil + } + keys, err := getKeys(marshaler) + if err != nil { + return nil, err + } + for _, key := range keys { + if _, ok := extraProperties[key]; ok { + return nil, fmt.Errorf("cannot add extra property %q because it is already defined on the type", key) + } + } + extraBytes, err := json.Marshal(extraProperties) + if err != nil { + return nil, err + } + if isEmptyJSON(bytes) { + if isEmptyJSON(extraBytes) { + return bytes, nil + } + return extraBytes, nil + } + result := bytes[:len(bytes)-1] + result = append(result, ',') + result = append(result, extraBytes[1:len(extraBytes)-1]...) + result = append(result, '}') + return result, nil +} + +// ExtractExtraProperties extracts any extra properties from the given value. +func ExtractExtraProperties(bytes []byte, value interface{}, exclude ...string) (map[string]interface{}, error) { + val := reflect.ValueOf(value) + for val.Kind() == reflect.Ptr { + if val.IsNil() { + return nil, fmt.Errorf("value must be non-nil to extract extra properties") + } + val = val.Elem() + } + if err := json.Unmarshal(bytes, &value); err != nil { + return nil, err + } + var extraProperties map[string]interface{} + if err := json.Unmarshal(bytes, &extraProperties); err != nil { + return nil, err + } + for i := 0; i < val.Type().NumField(); i++ { + key := jsonKey(val.Type().Field(i)) + if key == "" || key == "-" { + continue + } + delete(extraProperties, key) + } + for _, key := range exclude { + delete(extraProperties, key) + } + if len(extraProperties) == 0 { + return nil, nil + } + return extraProperties, nil +} + +// getKeys returns the keys associated with the given value. The value must be a +// a struct or a map with string keys. +func getKeys(value interface{}) ([]string, error) { + val := reflect.ValueOf(value) + if val.Kind() == reflect.Ptr { + val = val.Elem() + } + if !val.IsValid() { + return nil, nil + } + switch val.Kind() { + case reflect.Struct: + return getKeysForStructType(val.Type()), nil + case reflect.Map: + var keys []string + if val.Type().Key().Kind() != reflect.String { + return nil, fmt.Errorf("cannot extract keys from %T; only structs and maps with string keys are supported", value) + } + for _, key := range val.MapKeys() { + keys = append(keys, key.String()) + } + return keys, nil + default: + return nil, fmt.Errorf("cannot extract keys from %T; only structs and maps with string keys are supported", value) + } +} + +// getKeysForStructType returns all the keys associated with the given struct type, +// visiting embedded fields recursively. +func getKeysForStructType(structType reflect.Type) []string { + if structType.Kind() == reflect.Pointer { + structType = structType.Elem() + } + if structType.Kind() != reflect.Struct { + return nil + } + var keys []string + for i := 0; i < structType.NumField(); i++ { + field := structType.Field(i) + if field.Anonymous { + keys = append(keys, getKeysForStructType(field.Type)...) + continue + } + keys = append(keys, jsonKey(field)) + } + return keys +} + +// jsonKey returns the JSON key from the struct tag of the given field, +// excluding the omitempty flag (if any). +func jsonKey(field reflect.StructField) string { + return strings.TrimSuffix(field.Tag.Get("json"), ",omitempty") +} + +// isEmptyJSON returns true if the given data is empty, the empty JSON object, or +// an explicit null. +func isEmptyJSON(data []byte) bool { + return len(data) <= 2 || bytes.Equal(data, []byte("null")) +} diff --git a/seed/go-fiber/idempotency-headers/internal/extra_properties_test.go b/seed/go-fiber/idempotency-headers/internal/extra_properties_test.go new file mode 100644 index 000000000000..aa2510ee5121 --- /dev/null +++ b/seed/go-fiber/idempotency-headers/internal/extra_properties_test.go @@ -0,0 +1,228 @@ +package internal + +import ( + "encoding/json" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type testMarshaler struct { + Name string `json:"name"` + BirthDate time.Time `json:"birthDate"` + CreatedAt time.Time `json:"created_at"` +} + +func (t *testMarshaler) MarshalJSON() ([]byte, error) { + type embed testMarshaler + var marshaler = struct { + embed + BirthDate string `json:"birthDate"` + CreatedAt string `json:"created_at"` + }{ + embed: embed(*t), + BirthDate: t.BirthDate.Format("2006-01-02"), + CreatedAt: t.CreatedAt.Format(time.RFC3339), + } + return MarshalJSONWithExtraProperty(marshaler, "type", "test") +} + +func TestMarshalJSONWithExtraProperties(t *testing.T) { + tests := []struct { + desc string + giveMarshaler interface{} + giveExtraProperties map[string]interface{} + wantBytes []byte + wantError string + }{ + { + desc: "invalid type", + giveMarshaler: []string{"invalid"}, + giveExtraProperties: map[string]interface{}{"key": "overwrite"}, + wantError: `cannot extract keys from []string; only structs and maps with string keys are supported`, + }, + { + desc: "invalid key type", + giveMarshaler: map[int]interface{}{42: "value"}, + giveExtraProperties: map[string]interface{}{"key": "overwrite"}, + wantError: `cannot extract keys from map[int]interface {}; only structs and maps with string keys are supported`, + }, + { + desc: "invalid map overwrite", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{"key": "overwrite"}, + wantError: `cannot add extra property "key" because it is already defined on the type`, + }, + { + desc: "invalid struct overwrite", + giveMarshaler: new(testMarshaler), + giveExtraProperties: map[string]interface{}{"birthDate": "2000-01-01"}, + wantError: `cannot add extra property "birthDate" because it is already defined on the type`, + }, + { + desc: "invalid struct overwrite embedded type", + giveMarshaler: new(testMarshaler), + giveExtraProperties: map[string]interface{}{"name": "bob"}, + wantError: `cannot add extra property "name" because it is already defined on the type`, + }, + { + desc: "nil", + giveMarshaler: nil, + giveExtraProperties: nil, + wantBytes: []byte(`null`), + }, + { + desc: "empty", + giveMarshaler: map[string]interface{}{}, + giveExtraProperties: map[string]interface{}{}, + wantBytes: []byte(`{}`), + }, + { + desc: "no extra properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "only extra properties", + giveMarshaler: map[string]interface{}{}, + giveExtraProperties: map[string]interface{}{"key": "value"}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "single extra property", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{"extra": "property"}, + wantBytes: []byte(`{"key":"value","extra":"property"}`), + }, + { + desc: "multiple extra properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{"one": 1, "two": 2}, + wantBytes: []byte(`{"key":"value","one":1,"two":2}`), + }, + { + desc: "nested properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{ + "user": map[string]interface{}{ + "age": 42, + "name": "alice", + }, + }, + wantBytes: []byte(`{"key":"value","user":{"age":42,"name":"alice"}}`), + }, + { + desc: "multiple nested properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{ + "metadata": map[string]interface{}{ + "ip": "127.0.0.1", + }, + "user": map[string]interface{}{ + "age": 42, + "name": "alice", + }, + }, + wantBytes: []byte(`{"key":"value","metadata":{"ip":"127.0.0.1"},"user":{"age":42,"name":"alice"}}`), + }, + { + desc: "custom marshaler", + giveMarshaler: &testMarshaler{ + Name: "alice", + BirthDate: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), + CreatedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC), + }, + giveExtraProperties: map[string]interface{}{ + "extra": "property", + }, + wantBytes: []byte(`{"name":"alice","birthDate":"2000-01-01","created_at":"2024-01-01T00:00:00Z","type":"test","extra":"property"}`), + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + bytes, err := MarshalJSONWithExtraProperties(tt.giveMarshaler, tt.giveExtraProperties) + if tt.wantError != "" { + require.EqualError(t, err, tt.wantError) + assert.Nil(t, tt.wantBytes) + return + } + require.NoError(t, err) + assert.Equal(t, tt.wantBytes, bytes) + + value := make(map[string]interface{}) + require.NoError(t, json.Unmarshal(bytes, &value)) + }) + } +} + +func TestExtractExtraProperties(t *testing.T) { + t.Run("none", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice"}`), value) + require.NoError(t, err) + assert.Nil(t, extraProperties) + }) + + t.Run("non-nil pointer", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]interface{}{"age": float64(42)}, extraProperties) + }) + + t.Run("nil pointer", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + var value *user + _, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + assert.EqualError(t, err, "value must be non-nil to extract extra properties") + }) + + t.Run("non-zero value", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]interface{}{"age": float64(42)}, extraProperties) + }) + + t.Run("zero value", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + var value user + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]interface{}{"age": float64(42)}, extraProperties) + }) + + t.Run("exclude", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value, "age") + require.NoError(t, err) + assert.Nil(t, extraProperties) + }) +} diff --git a/seed/go-fiber/idempotency-headers/internal/stringer.go b/seed/go-fiber/idempotency-headers/internal/stringer.go new file mode 100644 index 000000000000..312801851e0e --- /dev/null +++ b/seed/go-fiber/idempotency-headers/internal/stringer.go @@ -0,0 +1,13 @@ +package internal + +import "encoding/json" + +// StringifyJSON returns a pretty JSON string representation of +// the given value. +func StringifyJSON(value interface{}) (string, error) { + bytes, err := json.MarshalIndent(value, "", " ") + if err != nil { + return "", err + } + return string(bytes), nil +} diff --git a/seed/go-fiber/idempotency-headers/internal/time.go b/seed/go-fiber/idempotency-headers/internal/time.go new file mode 100644 index 000000000000..ab0e269fade3 --- /dev/null +++ b/seed/go-fiber/idempotency-headers/internal/time.go @@ -0,0 +1,137 @@ +package internal + +import ( + "encoding/json" + "time" +) + +const dateFormat = "2006-01-02" + +// DateTime wraps time.Time and adapts its JSON representation +// to conform to a RFC3339 date (e.g. 2006-01-02). +// +// Ref: https://ijmacd.github.io/rfc3339-iso8601 +type Date struct { + t *time.Time +} + +// NewDate returns a new *Date. If the given time.Time +// is nil, nil will be returned. +func NewDate(t time.Time) *Date { + return &Date{t: &t} +} + +// NewOptionalDate returns a new *Date. If the given time.Time +// is nil, nil will be returned. +func NewOptionalDate(t *time.Time) *Date { + if t == nil { + return nil + } + return &Date{t: t} +} + +// Time returns the Date's underlying time, if any. If the +// date is nil, the zero value is returned. +func (d *Date) Time() time.Time { + if d == nil || d.t == nil { + return time.Time{} + } + return *d.t +} + +// TimePtr returns a pointer to the Date's underlying time.Time, if any. +func (d *Date) TimePtr() *time.Time { + if d == nil || d.t == nil { + return nil + } + if d.t.IsZero() { + return nil + } + return d.t +} + +func (d *Date) MarshalJSON() ([]byte, error) { + if d == nil || d.t == nil { + return nil, nil + } + return json.Marshal(d.t.Format(dateFormat)) +} + +func (d *Date) UnmarshalJSON(data []byte) error { + var raw string + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + + parsedTime, err := time.Parse(dateFormat, raw) + if err != nil { + return err + } + + *d = Date{t: &parsedTime} + return nil +} + +// DateTime wraps time.Time and adapts its JSON representation +// to conform to a RFC3339 date-time (e.g. 2017-07-21T17:32:28Z). +// +// Ref: https://ijmacd.github.io/rfc3339-iso8601 +type DateTime struct { + t *time.Time +} + +// NewDateTime returns a new *DateTime. +func NewDateTime(t time.Time) *DateTime { + return &DateTime{t: &t} +} + +// NewOptionalDateTime returns a new *DateTime. If the given time.Time +// is nil, nil will be returned. +func NewOptionalDateTime(t *time.Time) *DateTime { + if t == nil { + return nil + } + return &DateTime{t: t} +} + +// Time returns the DateTime's underlying time, if any. If the +// date-time is nil, the zero value is returned. +func (d *DateTime) Time() time.Time { + if d == nil || d.t == nil { + return time.Time{} + } + return *d.t +} + +// TimePtr returns a pointer to the DateTime's underlying time.Time, if any. +func (d *DateTime) TimePtr() *time.Time { + if d == nil || d.t == nil { + return nil + } + if d.t.IsZero() { + return nil + } + return d.t +} + +func (d *DateTime) MarshalJSON() ([]byte, error) { + if d == nil || d.t == nil { + return nil, nil + } + return json.Marshal(d.t.Format(time.RFC3339)) +} + +func (d *DateTime) UnmarshalJSON(data []byte) error { + var raw string + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + + parsedTime, err := time.Parse(time.RFC3339, raw) + if err != nil { + return err + } + + *d = DateTime{t: &parsedTime} + return nil +} diff --git a/seed/go-fiber/inferred-auth-implicit-api-key/.github/workflows/ci.yml b/seed/go-fiber/inferred-auth-implicit-api-key/.github/workflows/ci.yml new file mode 100644 index 000000000000..56310d69624b --- /dev/null +++ b/seed/go-fiber/inferred-auth-implicit-api-key/.github/workflows/ci.yml @@ -0,0 +1,35 @@ +name: ci + +on: [push] + +jobs: + compile: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up go + uses: actions/setup-go@v4 + + - name: Compile + run: go build ./... + test: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up go + uses: actions/setup-go@v4 + + - name: Setup wiremock server + run: | + if [ -f wiremock/docker-compose.test.yml ]; then docker compose -f wiremock/docker-compose.test.yml down && docker compose -f wiremock/docker-compose.test.yml up -d; fi + + - name: Test + run: go test ./... + + - name: Teardown wiremock server + run: | + if [ -f wiremock/docker-compose.test.yml ]; then docker compose -f wiremock/docker-compose.test.yml down; fi diff --git a/seed/go-fiber/inferred-auth-implicit-api-key/auth.go b/seed/go-fiber/inferred-auth-implicit-api-key/auth.go new file mode 100644 index 000000000000..a39bb93947cc --- /dev/null +++ b/seed/go-fiber/inferred-auth-implicit-api-key/auth.go @@ -0,0 +1,134 @@ +// Code generated by Fern. DO NOT EDIT. + +package inferredauthimplicitapikey + +import ( + json "encoding/json" + fmt "fmt" + internal "github.com/inferred-auth-implicit-api-key/fern/internal" + big "math/big" +) + +type GetTokenRequest struct { + ApiKey string `header:"api_key"` +} + +// An auth token response. +var ( + tokenResponseFieldAccessToken = big.NewInt(1 << 0) + tokenResponseFieldTokenType = big.NewInt(1 << 1) + tokenResponseFieldExpiresIn = big.NewInt(1 << 2) + tokenResponseFieldScope = big.NewInt(1 << 3) +) + +type TokenResponse struct { + AccessToken string `json:"access_token" url:"access_token"` + TokenType string `json:"token_type" url:"token_type"` + ExpiresIn int `json:"expires_in" url:"expires_in"` + Scope *string `json:"scope,omitempty" url:"scope,omitempty"` + + // Private bitmask of fields set to an explicit value and therefore not to be omitted + explicitFields *big.Int `json:"-" url:"-"` + + extraProperties map[string]interface{} +} + +func (t *TokenResponse) GetAccessToken() string { + if t == nil { + return "" + } + return t.AccessToken +} + +func (t *TokenResponse) GetTokenType() string { + if t == nil { + return "" + } + return t.TokenType +} + +func (t *TokenResponse) GetExpiresIn() int { + if t == nil { + return 0 + } + return t.ExpiresIn +} + +func (t *TokenResponse) GetScope() *string { + if t == nil { + return nil + } + return t.Scope +} + +func (t *TokenResponse) GetExtraProperties() map[string]interface{} { + return t.extraProperties +} + +func (t *TokenResponse) require(field *big.Int) { + if t.explicitFields == nil { + t.explicitFields = big.NewInt(0) + } + t.explicitFields.Or(t.explicitFields, field) +} + +// SetAccessToken sets the AccessToken field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (t *TokenResponse) SetAccessToken(accessToken string) { + t.AccessToken = accessToken + t.require(tokenResponseFieldAccessToken) +} + +// SetTokenType sets the TokenType field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (t *TokenResponse) SetTokenType(tokenType string) { + t.TokenType = tokenType + t.require(tokenResponseFieldTokenType) +} + +// SetExpiresIn sets the ExpiresIn field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (t *TokenResponse) SetExpiresIn(expiresIn int) { + t.ExpiresIn = expiresIn + t.require(tokenResponseFieldExpiresIn) +} + +// SetScope sets the Scope field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (t *TokenResponse) SetScope(scope *string) { + t.Scope = scope + t.require(tokenResponseFieldScope) +} + +func (t *TokenResponse) UnmarshalJSON(data []byte) error { + type unmarshaler TokenResponse + var value unmarshaler + if err := json.Unmarshal(data, &value); err != nil { + return err + } + *t = TokenResponse(value) + extraProperties, err := internal.ExtractExtraProperties(data, *t) + if err != nil { + return err + } + t.extraProperties = extraProperties + return nil +} + +func (t *TokenResponse) MarshalJSON() ([]byte, error) { + type embed TokenResponse + var marshaler = struct { + embed + }{ + embed: embed(*t), + } + explicitMarshaler := internal.HandleExplicitFields(marshaler, t.explicitFields) + return json.Marshal(explicitMarshaler) +} + +func (t *TokenResponse) String() string { + if value, err := internal.StringifyJSON(t); err == nil { + return value + } + return fmt.Sprintf("%#v", t) +} diff --git a/seed/go-fiber/inferred-auth-implicit-api-key/go.mod b/seed/go-fiber/inferred-auth-implicit-api-key/go.mod new file mode 100644 index 000000000000..3f5edeade28a --- /dev/null +++ b/seed/go-fiber/inferred-auth-implicit-api-key/go.mod @@ -0,0 +1,11 @@ +module github.com/inferred-auth-implicit-api-key/fern + +go 1.18 + +require github.com/stretchr/testify v1.7.0 + +require ( + github.com/davecgh/go-spew v1.1.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/seed/go-fiber/inferred-auth-implicit-api-key/go.sum b/seed/go-fiber/inferred-auth-implicit-api-key/go.sum new file mode 100644 index 000000000000..fc3dd9e67e82 --- /dev/null +++ b/seed/go-fiber/inferred-auth-implicit-api-key/go.sum @@ -0,0 +1,12 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/seed/go-fiber/inferred-auth-implicit-api-key/internal/explicit_fields.go b/seed/go-fiber/inferred-auth-implicit-api-key/internal/explicit_fields.go new file mode 100644 index 000000000000..4bdf34fc2b7c --- /dev/null +++ b/seed/go-fiber/inferred-auth-implicit-api-key/internal/explicit_fields.go @@ -0,0 +1,116 @@ +package internal + +import ( + "math/big" + "reflect" + "strings" +) + +// HandleExplicitFields processes a struct to remove `omitempty` from +// fields that have been explicitly set (as indicated by their corresponding bit in explicitFields). +// Note that `marshaler` should be an embedded struct to avoid infinite recursion. +// Returns an interface{} that can be passed to json.Marshal. +func HandleExplicitFields(marshaler interface{}, explicitFields *big.Int) interface{} { + val := reflect.ValueOf(marshaler) + typ := reflect.TypeOf(marshaler) + + // Handle pointer types + if val.Kind() == reflect.Ptr { + if val.IsNil() { + return nil + } + val = val.Elem() + typ = typ.Elem() + } + + // Only handle struct types + if val.Kind() != reflect.Struct { + return marshaler + } + + // Handle embedded struct pattern + var sourceVal reflect.Value + var sourceType reflect.Type + + // Check if this is an embedded struct pattern + if typ.NumField() == 1 && typ.Field(0).Anonymous { + // This is likely an embedded struct, get the embedded value + embeddedField := val.Field(0) + sourceVal = embeddedField + sourceType = embeddedField.Type() + } else { + // Regular struct + sourceVal = val + sourceType = typ + } + + // If no explicit fields set, use standard marshaling + if explicitFields == nil || explicitFields.Sign() == 0 { + return marshaler + } + + // Create a new struct type with modified tags + fields := make([]reflect.StructField, 0, sourceType.NumField()) + + for i := 0; i < sourceType.NumField(); i++ { + field := sourceType.Field(i) + + // Skip unexported fields and the explicitFields field itself + if !field.IsExported() || field.Name == "explicitFields" { + continue + } + + // Check if this field has been explicitly set + fieldBit := big.NewInt(1) + fieldBit.Lsh(fieldBit, uint(i)) + if big.NewInt(0).And(explicitFields, fieldBit).Sign() != 0 { + // Remove omitempty from the json tag + tag := field.Tag.Get("json") + if tag != "" && tag != "-" { + // Parse the json tag, remove omitempty from options + parts := strings.Split(tag, ",") + if len(parts) > 1 { + var newParts []string + newParts = append(newParts, parts[0]) // Keep the field name + for _, part := range parts[1:] { + if strings.TrimSpace(part) != "omitempty" { + newParts = append(newParts, part) + } + } + tag = strings.Join(newParts, ",") + } + + // Reconstruct the struct tag + newTag := `json:"` + tag + `"` + if urlTag := field.Tag.Get("url"); urlTag != "" { + newTag += ` url:"` + urlTag + `"` + } + + field.Tag = reflect.StructTag(newTag) + } + } + + fields = append(fields, field) + } + + // Create new struct type with modified tags + newType := reflect.StructOf(fields) + newVal := reflect.New(newType).Elem() + + // Copy field values from original struct to new struct + fieldIndex := 0 + for i := 0; i < sourceType.NumField(); i++ { + originalField := sourceType.Field(i) + + // Skip unexported fields and the explicitFields field itself + if !originalField.IsExported() || originalField.Name == "explicitFields" { + continue + } + + originalValue := sourceVal.Field(i) + newVal.Field(fieldIndex).Set(originalValue) + fieldIndex++ + } + + return newVal.Interface() +} diff --git a/seed/go-fiber/inferred-auth-implicit-api-key/internal/explicit_fields_test.go b/seed/go-fiber/inferred-auth-implicit-api-key/internal/explicit_fields_test.go new file mode 100644 index 000000000000..d3ec507de14b --- /dev/null +++ b/seed/go-fiber/inferred-auth-implicit-api-key/internal/explicit_fields_test.go @@ -0,0 +1,497 @@ +package internal + +import ( + "encoding/json" + "math/big" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type testExplicitFieldsStruct struct { + Name *string `json:"name,omitempty"` + Code *string `json:"code,omitempty"` + Count *int `json:"count,omitempty"` + Enabled *bool `json:"enabled,omitempty"` + Tags []string `json:"tags,omitempty"` + //lint:ignore unused this field is intentionally unused for testing + unexported string `json:"-"` + explicitFields *big.Int `json:"-"` +} + +var ( + testFieldName = big.NewInt(1 << 0) + testFieldCode = big.NewInt(1 << 1) + testFieldCount = big.NewInt(1 << 2) + testFieldEnabled = big.NewInt(1 << 3) + testFieldTags = big.NewInt(1 << 4) +) + +func (t *testExplicitFieldsStruct) require(field *big.Int) { + if t.explicitFields == nil { + t.explicitFields = big.NewInt(0) + } + t.explicitFields.Or(t.explicitFields, field) +} + +func (t *testExplicitFieldsStruct) SetName(name *string) { + t.Name = name + t.require(testFieldName) +} + +func (t *testExplicitFieldsStruct) SetCode(code *string) { + t.Code = code + t.require(testFieldCode) +} + +func (t *testExplicitFieldsStruct) SetCount(count *int) { + t.Count = count + t.require(testFieldCount) +} + +func (t *testExplicitFieldsStruct) SetEnabled(enabled *bool) { + t.Enabled = enabled + t.require(testFieldEnabled) +} + +func (t *testExplicitFieldsStruct) SetTags(tags []string) { + t.Tags = tags + t.require(testFieldTags) +} + +func (t *testExplicitFieldsStruct) MarshalJSON() ([]byte, error) { + type embed testExplicitFieldsStruct + var marshaler = struct { + embed + }{ + embed: embed(*t), + } + return json.Marshal(HandleExplicitFields(marshaler, t.explicitFields)) +} + +type testStructWithoutExplicitFields struct { + Name *string `json:"name,omitempty"` + Code *string `json:"code,omitempty"` +} + +func TestHandleExplicitFields(t *testing.T) { + tests := []struct { + desc string + giveInput interface{} + wantBytes []byte + wantError string + }{ + { + desc: "nil input", + giveInput: nil, + wantBytes: []byte(`null`), + }, + { + desc: "non-struct input", + giveInput: "string", + wantBytes: []byte(`"string"`), + }, + { + desc: "slice input", + giveInput: []string{"a", "b"}, + wantBytes: []byte(`["a","b"]`), + }, + { + desc: "map input", + giveInput: map[string]interface{}{"key": "value"}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "struct without explicitFields field", + giveInput: &testStructWithoutExplicitFields{ + Name: stringPtr("test"), + Code: nil, + }, + wantBytes: []byte(`{"name":"test"}`), + }, + { + desc: "struct with no explicit fields set", + giveInput: &testExplicitFieldsStruct{ + Name: stringPtr("test"), + Code: nil, + }, + wantBytes: []byte(`{"name":"test"}`), + }, + { + desc: "struct with explicit nil field", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{ + Name: stringPtr("test"), + } + s.SetCode(nil) + return s + }(), + wantBytes: []byte(`{"name":"test","code":null}`), + }, + { + desc: "struct with explicit non-nil field", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{} + s.SetName(stringPtr("explicit")) + s.SetCode(stringPtr("also-explicit")) + return s + }(), + wantBytes: []byte(`{"name":"explicit","code":"also-explicit"}`), + }, + { + desc: "struct with mixed explicit and implicit fields", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{ + Name: stringPtr("implicit"), + Count: intPtr(42), + } + s.SetCode(nil) // explicit nil + return s + }(), + wantBytes: []byte(`{"name":"implicit","code":null,"count":42}`), + }, + { + desc: "struct with multiple explicit nil fields", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{ + Name: stringPtr("test"), + } + s.SetCode(nil) + s.SetCount(nil) + return s + }(), + wantBytes: []byte(`{"name":"test","code":null,"count":null}`), + }, + { + desc: "struct with slice field", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{ + Tags: []string{"tag1", "tag2"}, + } + s.SetTags(nil) // explicit nil slice + return s + }(), + wantBytes: []byte(`{"tags":null}`), + }, + { + desc: "struct with boolean field", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{} + s.SetEnabled(boolPtr(false)) // explicit false + return s + }(), + wantBytes: []byte(`{"enabled":false}`), + }, + { + desc: "struct with all fields explicit", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{} + s.SetName(stringPtr("test")) + s.SetCode(nil) + s.SetCount(intPtr(0)) + s.SetEnabled(boolPtr(false)) + s.SetTags([]string{}) + return s + }(), + wantBytes: []byte(`{"name":"test","code":null,"count":0,"enabled":false,"tags":[]}`), + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + var explicitFields *big.Int + if s, ok := tt.giveInput.(*testExplicitFieldsStruct); ok { + explicitFields = s.explicitFields + } + bytes, err := json.Marshal(HandleExplicitFields(tt.giveInput, explicitFields)) + if tt.wantError != "" { + require.EqualError(t, err, tt.wantError) + assert.Nil(t, tt.wantBytes) + return + } + require.NoError(t, err) + assert.JSONEq(t, string(tt.wantBytes), string(bytes)) + + // Verify it's valid JSON + var value interface{} + require.NoError(t, json.Unmarshal(bytes, &value)) + }) + } +} + +func TestHandleExplicitFieldsCustomMarshaler(t *testing.T) { + t.Run("custom marshaler with explicit fields", func(t *testing.T) { + s := &testExplicitFieldsStruct{} + s.SetName(nil) + s.SetCode(stringPtr("test-code")) + + bytes, err := s.MarshalJSON() + require.NoError(t, err) + assert.JSONEq(t, `{"name":null,"code":"test-code"}`, string(bytes)) + }) + + t.Run("custom marshaler with no explicit fields", func(t *testing.T) { + s := &testExplicitFieldsStruct{ + Name: stringPtr("implicit"), + Code: stringPtr("also-implicit"), + } + + bytes, err := s.MarshalJSON() + require.NoError(t, err) + assert.JSONEq(t, `{"name":"implicit","code":"also-implicit"}`, string(bytes)) + }) +} + +func TestHandleExplicitFieldsPointerHandling(t *testing.T) { + t.Run("nil pointer", func(t *testing.T) { + var s *testExplicitFieldsStruct + bytes, err := json.Marshal(HandleExplicitFields(s, nil)) + require.NoError(t, err) + assert.Equal(t, []byte(`null`), bytes) + }) + + t.Run("pointer to struct", func(t *testing.T) { + s := &testExplicitFieldsStruct{} + s.SetName(nil) + + bytes, err := json.Marshal(HandleExplicitFields(s, s.explicitFields)) + require.NoError(t, err) + assert.JSONEq(t, `{"name":null}`, string(bytes)) + }) +} + +func TestHandleExplicitFieldsEmbeddedStruct(t *testing.T) { + t.Run("embedded struct with explicit fields", func(t *testing.T) { + // Create a struct similar to what MarshalJSON creates + s := &testExplicitFieldsStruct{} + s.SetName(nil) + s.SetCode(stringPtr("test-code")) + + type embed testExplicitFieldsStruct + var marshaler = struct { + embed + }{ + embed: embed(*s), + } + + bytes, err := json.Marshal(HandleExplicitFields(marshaler, s.explicitFields)) + require.NoError(t, err) + // Should include both explicit fields (name as null, code as "test-code") + assert.JSONEq(t, `{"name":null,"code":"test-code"}`, string(bytes)) + }) + + t.Run("embedded struct with no explicit fields", func(t *testing.T) { + s := &testExplicitFieldsStruct{ + Name: stringPtr("implicit"), + Code: stringPtr("also-implicit"), + } + + type embed testExplicitFieldsStruct + var marshaler = struct { + embed + }{ + embed: embed(*s), + } + + bytes, err := json.Marshal(HandleExplicitFields(marshaler, s.explicitFields)) + require.NoError(t, err) + // Should only include non-nil fields (omitempty behavior) + assert.JSONEq(t, `{"name":"implicit","code":"also-implicit"}`, string(bytes)) + }) + + t.Run("embedded struct with mixed fields", func(t *testing.T) { + s := &testExplicitFieldsStruct{ + Count: intPtr(42), // implicit field + } + s.SetName(nil) // explicit nil + s.SetCode(stringPtr("explicit")) // explicit value + + type embed testExplicitFieldsStruct + var marshaler = struct { + embed + }{ + embed: embed(*s), + } + + bytes, err := json.Marshal(HandleExplicitFields(marshaler, s.explicitFields)) + require.NoError(t, err) + // Should include explicit null, explicit value, and implicit value + assert.JSONEq(t, `{"name":null,"code":"explicit","count":42}`, string(bytes)) + }) +} + +func TestHandleExplicitFieldsTagHandling(t *testing.T) { + type testStructWithComplexTags struct { + Field1 *string `json:"field1,omitempty" url:"field1,omitempty"` + Field2 *string `json:"field2,omitempty,string" url:"field2"` + Field3 *string `json:"-"` + Field4 *string `json:"field4"` + explicitFields *big.Int `json:"-"` + } + + s := &testStructWithComplexTags{ + Field1: stringPtr("test1"), + Field4: stringPtr("test4"), + explicitFields: big.NewInt(1), // Only first field is explicit + } + + bytes, err := json.Marshal(HandleExplicitFields(s, s.explicitFields)) + require.NoError(t, err) + + // Field1 should have omitempty removed, Field2 should keep omitempty, Field4 should be included + assert.JSONEq(t, `{"field1":"test1","field4":"test4"}`, string(bytes)) +} + +// Test types for nested struct explicit fields testing +type testNestedStruct struct { + NestedName *string `json:"nested_name,omitempty"` + NestedCode *string `json:"nested_code,omitempty"` + explicitFields *big.Int `json:"-"` +} + +type testParentStruct struct { + ParentName *string `json:"parent_name,omitempty"` + Nested *testNestedStruct `json:"nested,omitempty"` + explicitFields *big.Int `json:"-"` +} + +var ( + nestedFieldName = big.NewInt(1 << 0) + nestedFieldCode = big.NewInt(1 << 1) +) + +var ( + parentFieldName = big.NewInt(1 << 0) + parentFieldNested = big.NewInt(1 << 1) +) + +func (n *testNestedStruct) require(field *big.Int) { + if n.explicitFields == nil { + n.explicitFields = big.NewInt(0) + } + n.explicitFields.Or(n.explicitFields, field) +} + +func (n *testNestedStruct) SetNestedName(name *string) { + n.NestedName = name + n.require(nestedFieldName) +} + +func (n *testNestedStruct) SetNestedCode(code *string) { + n.NestedCode = code + n.require(nestedFieldCode) +} + +func (n *testNestedStruct) MarshalJSON() ([]byte, error) { + type embed testNestedStruct + var marshaler = struct { + embed + }{ + embed: embed(*n), + } + return json.Marshal(HandleExplicitFields(marshaler, n.explicitFields)) +} + +func (p *testParentStruct) require(field *big.Int) { + if p.explicitFields == nil { + p.explicitFields = big.NewInt(0) + } + p.explicitFields.Or(p.explicitFields, field) +} + +func (p *testParentStruct) SetParentName(name *string) { + p.ParentName = name + p.require(parentFieldName) +} + +func (p *testParentStruct) SetNested(nested *testNestedStruct) { + p.Nested = nested + p.require(parentFieldNested) +} + +func (p *testParentStruct) MarshalJSON() ([]byte, error) { + type embed testParentStruct + var marshaler = struct { + embed + }{ + embed: embed(*p), + } + return json.Marshal(HandleExplicitFields(marshaler, p.explicitFields)) +} + +func TestHandleExplicitFieldsNestedStruct(t *testing.T) { + tests := []struct { + desc string + setupFunc func() *testParentStruct + wantBytes []byte + }{ + { + desc: "nested struct with explicit nil in nested object", + setupFunc: func() *testParentStruct { + nested := &testNestedStruct{ + NestedName: stringPtr("implicit-nested"), + } + nested.SetNestedCode(nil) // explicit nil + + return &testParentStruct{ + ParentName: stringPtr("implicit-parent"), + Nested: nested, + } + }, + wantBytes: []byte(`{"parent_name":"implicit-parent","nested":{"nested_name":"implicit-nested","nested_code":null}}`), + }, + { + desc: "parent with explicit nil nested struct", + setupFunc: func() *testParentStruct { + parent := &testParentStruct{ + ParentName: stringPtr("implicit-parent"), + } + parent.SetNested(nil) // explicit nil nested struct + return parent + }, + wantBytes: []byte(`{"parent_name":"implicit-parent","nested":null}`), + }, + { + desc: "all explicit fields in nested structure", + setupFunc: func() *testParentStruct { + nested := &testNestedStruct{} + nested.SetNestedName(stringPtr("explicit-nested")) + nested.SetNestedCode(nil) // explicit nil + + parent := &testParentStruct{} + parent.SetParentName(nil) // explicit nil + parent.SetNested(nested) // explicit nested struct + + return parent + }, + wantBytes: []byte(`{"parent_name":null,"nested":{"nested_name":"explicit-nested","nested_code":null}}`), + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + parent := tt.setupFunc() + bytes, err := parent.MarshalJSON() + require.NoError(t, err) + assert.JSONEq(t, string(tt.wantBytes), string(bytes)) + + // Verify it's valid JSON + var value interface{} + require.NoError(t, json.Unmarshal(bytes, &value)) + }) + } +} + +// Helper functions +func stringPtr(s string) *string { + return &s +} + +func intPtr(i int) *int { + return &i +} + +func boolPtr(b bool) *bool { + return &b +} diff --git a/seed/go-fiber/inferred-auth-implicit-api-key/internal/extra_properties.go b/seed/go-fiber/inferred-auth-implicit-api-key/internal/extra_properties.go new file mode 100644 index 000000000000..540c3fd89eeb --- /dev/null +++ b/seed/go-fiber/inferred-auth-implicit-api-key/internal/extra_properties.go @@ -0,0 +1,141 @@ +package internal + +import ( + "bytes" + "encoding/json" + "fmt" + "reflect" + "strings" +) + +// MarshalJSONWithExtraProperty marshals the given value to JSON, including the extra property. +func MarshalJSONWithExtraProperty(marshaler interface{}, key string, value interface{}) ([]byte, error) { + return MarshalJSONWithExtraProperties(marshaler, map[string]interface{}{key: value}) +} + +// MarshalJSONWithExtraProperties marshals the given value to JSON, including any extra properties. +func MarshalJSONWithExtraProperties(marshaler interface{}, extraProperties map[string]interface{}) ([]byte, error) { + bytes, err := json.Marshal(marshaler) + if err != nil { + return nil, err + } + if len(extraProperties) == 0 { + return bytes, nil + } + keys, err := getKeys(marshaler) + if err != nil { + return nil, err + } + for _, key := range keys { + if _, ok := extraProperties[key]; ok { + return nil, fmt.Errorf("cannot add extra property %q because it is already defined on the type", key) + } + } + extraBytes, err := json.Marshal(extraProperties) + if err != nil { + return nil, err + } + if isEmptyJSON(bytes) { + if isEmptyJSON(extraBytes) { + return bytes, nil + } + return extraBytes, nil + } + result := bytes[:len(bytes)-1] + result = append(result, ',') + result = append(result, extraBytes[1:len(extraBytes)-1]...) + result = append(result, '}') + return result, nil +} + +// ExtractExtraProperties extracts any extra properties from the given value. +func ExtractExtraProperties(bytes []byte, value interface{}, exclude ...string) (map[string]interface{}, error) { + val := reflect.ValueOf(value) + for val.Kind() == reflect.Ptr { + if val.IsNil() { + return nil, fmt.Errorf("value must be non-nil to extract extra properties") + } + val = val.Elem() + } + if err := json.Unmarshal(bytes, &value); err != nil { + return nil, err + } + var extraProperties map[string]interface{} + if err := json.Unmarshal(bytes, &extraProperties); err != nil { + return nil, err + } + for i := 0; i < val.Type().NumField(); i++ { + key := jsonKey(val.Type().Field(i)) + if key == "" || key == "-" { + continue + } + delete(extraProperties, key) + } + for _, key := range exclude { + delete(extraProperties, key) + } + if len(extraProperties) == 0 { + return nil, nil + } + return extraProperties, nil +} + +// getKeys returns the keys associated with the given value. The value must be a +// a struct or a map with string keys. +func getKeys(value interface{}) ([]string, error) { + val := reflect.ValueOf(value) + if val.Kind() == reflect.Ptr { + val = val.Elem() + } + if !val.IsValid() { + return nil, nil + } + switch val.Kind() { + case reflect.Struct: + return getKeysForStructType(val.Type()), nil + case reflect.Map: + var keys []string + if val.Type().Key().Kind() != reflect.String { + return nil, fmt.Errorf("cannot extract keys from %T; only structs and maps with string keys are supported", value) + } + for _, key := range val.MapKeys() { + keys = append(keys, key.String()) + } + return keys, nil + default: + return nil, fmt.Errorf("cannot extract keys from %T; only structs and maps with string keys are supported", value) + } +} + +// getKeysForStructType returns all the keys associated with the given struct type, +// visiting embedded fields recursively. +func getKeysForStructType(structType reflect.Type) []string { + if structType.Kind() == reflect.Pointer { + structType = structType.Elem() + } + if structType.Kind() != reflect.Struct { + return nil + } + var keys []string + for i := 0; i < structType.NumField(); i++ { + field := structType.Field(i) + if field.Anonymous { + keys = append(keys, getKeysForStructType(field.Type)...) + continue + } + keys = append(keys, jsonKey(field)) + } + return keys +} + +// jsonKey returns the JSON key from the struct tag of the given field, +// excluding the omitempty flag (if any). +func jsonKey(field reflect.StructField) string { + return strings.TrimSuffix(field.Tag.Get("json"), ",omitempty") +} + +// isEmptyJSON returns true if the given data is empty, the empty JSON object, or +// an explicit null. +func isEmptyJSON(data []byte) bool { + return len(data) <= 2 || bytes.Equal(data, []byte("null")) +} diff --git a/seed/go-fiber/inferred-auth-implicit-api-key/internal/extra_properties_test.go b/seed/go-fiber/inferred-auth-implicit-api-key/internal/extra_properties_test.go new file mode 100644 index 000000000000..aa2510ee5121 --- /dev/null +++ b/seed/go-fiber/inferred-auth-implicit-api-key/internal/extra_properties_test.go @@ -0,0 +1,228 @@ +package internal + +import ( + "encoding/json" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type testMarshaler struct { + Name string `json:"name"` + BirthDate time.Time `json:"birthDate"` + CreatedAt time.Time `json:"created_at"` +} + +func (t *testMarshaler) MarshalJSON() ([]byte, error) { + type embed testMarshaler + var marshaler = struct { + embed + BirthDate string `json:"birthDate"` + CreatedAt string `json:"created_at"` + }{ + embed: embed(*t), + BirthDate: t.BirthDate.Format("2006-01-02"), + CreatedAt: t.CreatedAt.Format(time.RFC3339), + } + return MarshalJSONWithExtraProperty(marshaler, "type", "test") +} + +func TestMarshalJSONWithExtraProperties(t *testing.T) { + tests := []struct { + desc string + giveMarshaler interface{} + giveExtraProperties map[string]interface{} + wantBytes []byte + wantError string + }{ + { + desc: "invalid type", + giveMarshaler: []string{"invalid"}, + giveExtraProperties: map[string]interface{}{"key": "overwrite"}, + wantError: `cannot extract keys from []string; only structs and maps with string keys are supported`, + }, + { + desc: "invalid key type", + giveMarshaler: map[int]interface{}{42: "value"}, + giveExtraProperties: map[string]interface{}{"key": "overwrite"}, + wantError: `cannot extract keys from map[int]interface {}; only structs and maps with string keys are supported`, + }, + { + desc: "invalid map overwrite", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{"key": "overwrite"}, + wantError: `cannot add extra property "key" because it is already defined on the type`, + }, + { + desc: "invalid struct overwrite", + giveMarshaler: new(testMarshaler), + giveExtraProperties: map[string]interface{}{"birthDate": "2000-01-01"}, + wantError: `cannot add extra property "birthDate" because it is already defined on the type`, + }, + { + desc: "invalid struct overwrite embedded type", + giveMarshaler: new(testMarshaler), + giveExtraProperties: map[string]interface{}{"name": "bob"}, + wantError: `cannot add extra property "name" because it is already defined on the type`, + }, + { + desc: "nil", + giveMarshaler: nil, + giveExtraProperties: nil, + wantBytes: []byte(`null`), + }, + { + desc: "empty", + giveMarshaler: map[string]interface{}{}, + giveExtraProperties: map[string]interface{}{}, + wantBytes: []byte(`{}`), + }, + { + desc: "no extra properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "only extra properties", + giveMarshaler: map[string]interface{}{}, + giveExtraProperties: map[string]interface{}{"key": "value"}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "single extra property", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{"extra": "property"}, + wantBytes: []byte(`{"key":"value","extra":"property"}`), + }, + { + desc: "multiple extra properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{"one": 1, "two": 2}, + wantBytes: []byte(`{"key":"value","one":1,"two":2}`), + }, + { + desc: "nested properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{ + "user": map[string]interface{}{ + "age": 42, + "name": "alice", + }, + }, + wantBytes: []byte(`{"key":"value","user":{"age":42,"name":"alice"}}`), + }, + { + desc: "multiple nested properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{ + "metadata": map[string]interface{}{ + "ip": "127.0.0.1", + }, + "user": map[string]interface{}{ + "age": 42, + "name": "alice", + }, + }, + wantBytes: []byte(`{"key":"value","metadata":{"ip":"127.0.0.1"},"user":{"age":42,"name":"alice"}}`), + }, + { + desc: "custom marshaler", + giveMarshaler: &testMarshaler{ + Name: "alice", + BirthDate: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), + CreatedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC), + }, + giveExtraProperties: map[string]interface{}{ + "extra": "property", + }, + wantBytes: []byte(`{"name":"alice","birthDate":"2000-01-01","created_at":"2024-01-01T00:00:00Z","type":"test","extra":"property"}`), + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + bytes, err := MarshalJSONWithExtraProperties(tt.giveMarshaler, tt.giveExtraProperties) + if tt.wantError != "" { + require.EqualError(t, err, tt.wantError) + assert.Nil(t, tt.wantBytes) + return + } + require.NoError(t, err) + assert.Equal(t, tt.wantBytes, bytes) + + value := make(map[string]interface{}) + require.NoError(t, json.Unmarshal(bytes, &value)) + }) + } +} + +func TestExtractExtraProperties(t *testing.T) { + t.Run("none", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice"}`), value) + require.NoError(t, err) + assert.Nil(t, extraProperties) + }) + + t.Run("non-nil pointer", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]interface{}{"age": float64(42)}, extraProperties) + }) + + t.Run("nil pointer", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + var value *user + _, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + assert.EqualError(t, err, "value must be non-nil to extract extra properties") + }) + + t.Run("non-zero value", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]interface{}{"age": float64(42)}, extraProperties) + }) + + t.Run("zero value", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + var value user + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]interface{}{"age": float64(42)}, extraProperties) + }) + + t.Run("exclude", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value, "age") + require.NoError(t, err) + assert.Nil(t, extraProperties) + }) +} diff --git a/seed/go-fiber/inferred-auth-implicit-api-key/internal/stringer.go b/seed/go-fiber/inferred-auth-implicit-api-key/internal/stringer.go new file mode 100644 index 000000000000..312801851e0e --- /dev/null +++ b/seed/go-fiber/inferred-auth-implicit-api-key/internal/stringer.go @@ -0,0 +1,13 @@ +package internal + +import "encoding/json" + +// StringifyJSON returns a pretty JSON string representation of +// the given value. +func StringifyJSON(value interface{}) (string, error) { + bytes, err := json.MarshalIndent(value, "", " ") + if err != nil { + return "", err + } + return string(bytes), nil +} diff --git a/seed/go-fiber/inferred-auth-implicit-api-key/internal/time.go b/seed/go-fiber/inferred-auth-implicit-api-key/internal/time.go new file mode 100644 index 000000000000..ab0e269fade3 --- /dev/null +++ b/seed/go-fiber/inferred-auth-implicit-api-key/internal/time.go @@ -0,0 +1,137 @@ +package internal + +import ( + "encoding/json" + "time" +) + +const dateFormat = "2006-01-02" + +// DateTime wraps time.Time and adapts its JSON representation +// to conform to a RFC3339 date (e.g. 2006-01-02). +// +// Ref: https://ijmacd.github.io/rfc3339-iso8601 +type Date struct { + t *time.Time +} + +// NewDate returns a new *Date. If the given time.Time +// is nil, nil will be returned. +func NewDate(t time.Time) *Date { + return &Date{t: &t} +} + +// NewOptionalDate returns a new *Date. If the given time.Time +// is nil, nil will be returned. +func NewOptionalDate(t *time.Time) *Date { + if t == nil { + return nil + } + return &Date{t: t} +} + +// Time returns the Date's underlying time, if any. If the +// date is nil, the zero value is returned. +func (d *Date) Time() time.Time { + if d == nil || d.t == nil { + return time.Time{} + } + return *d.t +} + +// TimePtr returns a pointer to the Date's underlying time.Time, if any. +func (d *Date) TimePtr() *time.Time { + if d == nil || d.t == nil { + return nil + } + if d.t.IsZero() { + return nil + } + return d.t +} + +func (d *Date) MarshalJSON() ([]byte, error) { + if d == nil || d.t == nil { + return nil, nil + } + return json.Marshal(d.t.Format(dateFormat)) +} + +func (d *Date) UnmarshalJSON(data []byte) error { + var raw string + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + + parsedTime, err := time.Parse(dateFormat, raw) + if err != nil { + return err + } + + *d = Date{t: &parsedTime} + return nil +} + +// DateTime wraps time.Time and adapts its JSON representation +// to conform to a RFC3339 date-time (e.g. 2017-07-21T17:32:28Z). +// +// Ref: https://ijmacd.github.io/rfc3339-iso8601 +type DateTime struct { + t *time.Time +} + +// NewDateTime returns a new *DateTime. +func NewDateTime(t time.Time) *DateTime { + return &DateTime{t: &t} +} + +// NewOptionalDateTime returns a new *DateTime. If the given time.Time +// is nil, nil will be returned. +func NewOptionalDateTime(t *time.Time) *DateTime { + if t == nil { + return nil + } + return &DateTime{t: t} +} + +// Time returns the DateTime's underlying time, if any. If the +// date-time is nil, the zero value is returned. +func (d *DateTime) Time() time.Time { + if d == nil || d.t == nil { + return time.Time{} + } + return *d.t +} + +// TimePtr returns a pointer to the DateTime's underlying time.Time, if any. +func (d *DateTime) TimePtr() *time.Time { + if d == nil || d.t == nil { + return nil + } + if d.t.IsZero() { + return nil + } + return d.t +} + +func (d *DateTime) MarshalJSON() ([]byte, error) { + if d == nil || d.t == nil { + return nil, nil + } + return json.Marshal(d.t.Format(time.RFC3339)) +} + +func (d *DateTime) UnmarshalJSON(data []byte) error { + var raw string + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + + parsedTime, err := time.Parse(time.RFC3339, raw) + if err != nil { + return err + } + + *d = DateTime{t: &parsedTime} + return nil +} diff --git a/seed/go-fiber/inferred-auth-implicit-api-key/snippet.json b/seed/go-fiber/inferred-auth-implicit-api-key/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/go-fiber/inferred-auth-implicit-reference/.github/workflows/ci.yml b/seed/go-fiber/inferred-auth-implicit-reference/.github/workflows/ci.yml new file mode 100644 index 000000000000..56310d69624b --- /dev/null +++ b/seed/go-fiber/inferred-auth-implicit-reference/.github/workflows/ci.yml @@ -0,0 +1,35 @@ +name: ci + +on: [push] + +jobs: + compile: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up go + uses: actions/setup-go@v4 + + - name: Compile + run: go build ./... + test: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up go + uses: actions/setup-go@v4 + + - name: Setup wiremock server + run: | + if [ -f wiremock/docker-compose.test.yml ]; then docker compose -f wiremock/docker-compose.test.yml down && docker compose -f wiremock/docker-compose.test.yml up -d; fi + + - name: Test + run: go test ./... + + - name: Teardown wiremock server + run: | + if [ -f wiremock/docker-compose.test.yml ]; then docker compose -f wiremock/docker-compose.test.yml down; fi diff --git a/seed/go-fiber/inferred-auth-implicit-reference/auth.go b/seed/go-fiber/inferred-auth-implicit-reference/auth.go new file mode 100644 index 000000000000..c861f4e512c9 --- /dev/null +++ b/seed/go-fiber/inferred-auth-implicit-reference/auth.go @@ -0,0 +1,394 @@ +// Code generated by Fern. DO NOT EDIT. + +package inferredauthimplicit + +import ( + json "encoding/json" + fmt "fmt" + internal "github.com/inferred-auth-implicit-reference/fern/internal" + big "math/big" +) + +// A request to obtain an OAuth token. +var ( + getTokenRequestFieldClientId = big.NewInt(1 << 0) + getTokenRequestFieldClientSecret = big.NewInt(1 << 1) + getTokenRequestFieldScope = big.NewInt(1 << 2) +) + +type GetTokenRequest struct { + ClientId string `json:"client_id" url:"client_id"` + ClientSecret string `json:"client_secret" url:"client_secret"` + Scope *string `json:"scope,omitempty" url:"scope,omitempty"` + + // Private bitmask of fields set to an explicit value and therefore not to be omitted + explicitFields *big.Int `json:"-" url:"-"` + audience string + grantType string + + extraProperties map[string]interface{} +} + +func (g *GetTokenRequest) GetClientId() string { + if g == nil { + return "" + } + return g.ClientId +} + +func (g *GetTokenRequest) GetClientSecret() string { + if g == nil { + return "" + } + return g.ClientSecret +} + +func (g *GetTokenRequest) GetScope() *string { + if g == nil { + return nil + } + return g.Scope +} + +func (g *GetTokenRequest) Audience() string { + return g.audience +} + +func (g *GetTokenRequest) GrantType() string { + return g.grantType +} + +func (g *GetTokenRequest) GetExtraProperties() map[string]interface{} { + return g.extraProperties +} + +func (g *GetTokenRequest) require(field *big.Int) { + if g.explicitFields == nil { + g.explicitFields = big.NewInt(0) + } + g.explicitFields.Or(g.explicitFields, field) +} + +// SetClientId sets the ClientId field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (g *GetTokenRequest) SetClientId(clientId string) { + g.ClientId = clientId + g.require(getTokenRequestFieldClientId) +} + +// SetClientSecret sets the ClientSecret field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (g *GetTokenRequest) SetClientSecret(clientSecret string) { + g.ClientSecret = clientSecret + g.require(getTokenRequestFieldClientSecret) +} + +// SetScope sets the Scope field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (g *GetTokenRequest) SetScope(scope *string) { + g.Scope = scope + g.require(getTokenRequestFieldScope) +} + +func (g *GetTokenRequest) UnmarshalJSON(data []byte) error { + type embed GetTokenRequest + var unmarshaler = struct { + embed + Audience string `json:"audience"` + GrantType string `json:"grant_type"` + }{ + embed: embed(*g), + } + if err := json.Unmarshal(data, &unmarshaler); err != nil { + return err + } + *g = GetTokenRequest(unmarshaler.embed) + if unmarshaler.Audience != "https://api.example.com" { + return fmt.Errorf("unexpected value for literal on type %T; expected %v got %v", g, "https://api.example.com", unmarshaler.Audience) + } + g.audience = unmarshaler.Audience + if unmarshaler.GrantType != "client_credentials" { + return fmt.Errorf("unexpected value for literal on type %T; expected %v got %v", g, "client_credentials", unmarshaler.GrantType) + } + g.grantType = unmarshaler.GrantType + extraProperties, err := internal.ExtractExtraProperties(data, *g, "audience", "grant_type") + if err != nil { + return err + } + g.extraProperties = extraProperties + return nil +} + +func (g *GetTokenRequest) MarshalJSON() ([]byte, error) { + type embed GetTokenRequest + var marshaler = struct { + embed + Audience string `json:"audience"` + GrantType string `json:"grant_type"` + }{ + embed: embed(*g), + Audience: "https://api.example.com", + GrantType: "client_credentials", + } + explicitMarshaler := internal.HandleExplicitFields(marshaler, g.explicitFields) + return json.Marshal(explicitMarshaler) +} + +func (g *GetTokenRequest) String() string { + if value, err := internal.StringifyJSON(g); err == nil { + return value + } + return fmt.Sprintf("%#v", g) +} + +// A request to refresh an OAuth token. +var ( + refreshTokenRequestFieldClientId = big.NewInt(1 << 0) + refreshTokenRequestFieldClientSecret = big.NewInt(1 << 1) + refreshTokenRequestFieldRefreshToken = big.NewInt(1 << 2) + refreshTokenRequestFieldScope = big.NewInt(1 << 3) +) + +type RefreshTokenRequest struct { + ClientId string `json:"client_id" url:"client_id"` + ClientSecret string `json:"client_secret" url:"client_secret"` + RefreshToken string `json:"refresh_token" url:"refresh_token"` + Scope *string `json:"scope,omitempty" url:"scope,omitempty"` + + // Private bitmask of fields set to an explicit value and therefore not to be omitted + explicitFields *big.Int `json:"-" url:"-"` + audience string + grantType string + + extraProperties map[string]interface{} +} + +func (r *RefreshTokenRequest) GetClientId() string { + if r == nil { + return "" + } + return r.ClientId +} + +func (r *RefreshTokenRequest) GetClientSecret() string { + if r == nil { + return "" + } + return r.ClientSecret +} + +func (r *RefreshTokenRequest) GetRefreshToken() string { + if r == nil { + return "" + } + return r.RefreshToken +} + +func (r *RefreshTokenRequest) GetScope() *string { + if r == nil { + return nil + } + return r.Scope +} + +func (r *RefreshTokenRequest) Audience() string { + return r.audience +} + +func (r *RefreshTokenRequest) GrantType() string { + return r.grantType +} + +func (r *RefreshTokenRequest) GetExtraProperties() map[string]interface{} { + return r.extraProperties +} + +func (r *RefreshTokenRequest) require(field *big.Int) { + if r.explicitFields == nil { + r.explicitFields = big.NewInt(0) + } + r.explicitFields.Or(r.explicitFields, field) +} + +// SetClientId sets the ClientId field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (r *RefreshTokenRequest) SetClientId(clientId string) { + r.ClientId = clientId + r.require(refreshTokenRequestFieldClientId) +} + +// SetClientSecret sets the ClientSecret field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (r *RefreshTokenRequest) SetClientSecret(clientSecret string) { + r.ClientSecret = clientSecret + r.require(refreshTokenRequestFieldClientSecret) +} + +// SetRefreshToken sets the RefreshToken field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (r *RefreshTokenRequest) SetRefreshToken(refreshToken string) { + r.RefreshToken = refreshToken + r.require(refreshTokenRequestFieldRefreshToken) +} + +// SetScope sets the Scope field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (r *RefreshTokenRequest) SetScope(scope *string) { + r.Scope = scope + r.require(refreshTokenRequestFieldScope) +} + +func (r *RefreshTokenRequest) UnmarshalJSON(data []byte) error { + type embed RefreshTokenRequest + var unmarshaler = struct { + embed + Audience string `json:"audience"` + GrantType string `json:"grant_type"` + }{ + embed: embed(*r), + } + if err := json.Unmarshal(data, &unmarshaler); err != nil { + return err + } + *r = RefreshTokenRequest(unmarshaler.embed) + if unmarshaler.Audience != "https://api.example.com" { + return fmt.Errorf("unexpected value for literal on type %T; expected %v got %v", r, "https://api.example.com", unmarshaler.Audience) + } + r.audience = unmarshaler.Audience + if unmarshaler.GrantType != "refresh_token" { + return fmt.Errorf("unexpected value for literal on type %T; expected %v got %v", r, "refresh_token", unmarshaler.GrantType) + } + r.grantType = unmarshaler.GrantType + extraProperties, err := internal.ExtractExtraProperties(data, *r, "audience", "grant_type") + if err != nil { + return err + } + r.extraProperties = extraProperties + return nil +} + +func (r *RefreshTokenRequest) MarshalJSON() ([]byte, error) { + type embed RefreshTokenRequest + var marshaler = struct { + embed + Audience string `json:"audience"` + GrantType string `json:"grant_type"` + }{ + embed: embed(*r), + Audience: "https://api.example.com", + GrantType: "refresh_token", + } + explicitMarshaler := internal.HandleExplicitFields(marshaler, r.explicitFields) + return json.Marshal(explicitMarshaler) +} + +func (r *RefreshTokenRequest) String() string { + if value, err := internal.StringifyJSON(r); err == nil { + return value + } + return fmt.Sprintf("%#v", r) +} + +// An OAuth token response. +var ( + tokenResponseFieldAccessToken = big.NewInt(1 << 0) + tokenResponseFieldExpiresIn = big.NewInt(1 << 1) + tokenResponseFieldRefreshToken = big.NewInt(1 << 2) +) + +type TokenResponse struct { + AccessToken string `json:"access_token" url:"access_token"` + ExpiresIn int `json:"expires_in" url:"expires_in"` + RefreshToken *string `json:"refresh_token,omitempty" url:"refresh_token,omitempty"` + + // Private bitmask of fields set to an explicit value and therefore not to be omitted + explicitFields *big.Int `json:"-" url:"-"` + + extraProperties map[string]interface{} +} + +func (t *TokenResponse) GetAccessToken() string { + if t == nil { + return "" + } + return t.AccessToken +} + +func (t *TokenResponse) GetExpiresIn() int { + if t == nil { + return 0 + } + return t.ExpiresIn +} + +func (t *TokenResponse) GetRefreshToken() *string { + if t == nil { + return nil + } + return t.RefreshToken +} + +func (t *TokenResponse) GetExtraProperties() map[string]interface{} { + return t.extraProperties +} + +func (t *TokenResponse) require(field *big.Int) { + if t.explicitFields == nil { + t.explicitFields = big.NewInt(0) + } + t.explicitFields.Or(t.explicitFields, field) +} + +// SetAccessToken sets the AccessToken field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (t *TokenResponse) SetAccessToken(accessToken string) { + t.AccessToken = accessToken + t.require(tokenResponseFieldAccessToken) +} + +// SetExpiresIn sets the ExpiresIn field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (t *TokenResponse) SetExpiresIn(expiresIn int) { + t.ExpiresIn = expiresIn + t.require(tokenResponseFieldExpiresIn) +} + +// SetRefreshToken sets the RefreshToken field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (t *TokenResponse) SetRefreshToken(refreshToken *string) { + t.RefreshToken = refreshToken + t.require(tokenResponseFieldRefreshToken) +} + +func (t *TokenResponse) UnmarshalJSON(data []byte) error { + type unmarshaler TokenResponse + var value unmarshaler + if err := json.Unmarshal(data, &value); err != nil { + return err + } + *t = TokenResponse(value) + extraProperties, err := internal.ExtractExtraProperties(data, *t) + if err != nil { + return err + } + t.extraProperties = extraProperties + return nil +} + +func (t *TokenResponse) MarshalJSON() ([]byte, error) { + type embed TokenResponse + var marshaler = struct { + embed + }{ + embed: embed(*t), + } + explicitMarshaler := internal.HandleExplicitFields(marshaler, t.explicitFields) + return json.Marshal(explicitMarshaler) +} + +func (t *TokenResponse) String() string { + if value, err := internal.StringifyJSON(t); err == nil { + return value + } + return fmt.Sprintf("%#v", t) +} diff --git a/seed/go-fiber/inferred-auth-implicit-reference/go.mod b/seed/go-fiber/inferred-auth-implicit-reference/go.mod new file mode 100644 index 000000000000..af97eba1b3fb --- /dev/null +++ b/seed/go-fiber/inferred-auth-implicit-reference/go.mod @@ -0,0 +1,11 @@ +module github.com/inferred-auth-implicit-reference/fern + +go 1.18 + +require github.com/stretchr/testify v1.7.0 + +require ( + github.com/davecgh/go-spew v1.1.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/seed/go-fiber/inferred-auth-implicit-reference/go.sum b/seed/go-fiber/inferred-auth-implicit-reference/go.sum new file mode 100644 index 000000000000..fc3dd9e67e82 --- /dev/null +++ b/seed/go-fiber/inferred-auth-implicit-reference/go.sum @@ -0,0 +1,12 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/seed/go-fiber/inferred-auth-implicit-reference/internal/explicit_fields.go b/seed/go-fiber/inferred-auth-implicit-reference/internal/explicit_fields.go new file mode 100644 index 000000000000..4bdf34fc2b7c --- /dev/null +++ b/seed/go-fiber/inferred-auth-implicit-reference/internal/explicit_fields.go @@ -0,0 +1,116 @@ +package internal + +import ( + "math/big" + "reflect" + "strings" +) + +// HandleExplicitFields processes a struct to remove `omitempty` from +// fields that have been explicitly set (as indicated by their corresponding bit in explicitFields). +// Note that `marshaler` should be an embedded struct to avoid infinite recursion. +// Returns an interface{} that can be passed to json.Marshal. +func HandleExplicitFields(marshaler interface{}, explicitFields *big.Int) interface{} { + val := reflect.ValueOf(marshaler) + typ := reflect.TypeOf(marshaler) + + // Handle pointer types + if val.Kind() == reflect.Ptr { + if val.IsNil() { + return nil + } + val = val.Elem() + typ = typ.Elem() + } + + // Only handle struct types + if val.Kind() != reflect.Struct { + return marshaler + } + + // Handle embedded struct pattern + var sourceVal reflect.Value + var sourceType reflect.Type + + // Check if this is an embedded struct pattern + if typ.NumField() == 1 && typ.Field(0).Anonymous { + // This is likely an embedded struct, get the embedded value + embeddedField := val.Field(0) + sourceVal = embeddedField + sourceType = embeddedField.Type() + } else { + // Regular struct + sourceVal = val + sourceType = typ + } + + // If no explicit fields set, use standard marshaling + if explicitFields == nil || explicitFields.Sign() == 0 { + return marshaler + } + + // Create a new struct type with modified tags + fields := make([]reflect.StructField, 0, sourceType.NumField()) + + for i := 0; i < sourceType.NumField(); i++ { + field := sourceType.Field(i) + + // Skip unexported fields and the explicitFields field itself + if !field.IsExported() || field.Name == "explicitFields" { + continue + } + + // Check if this field has been explicitly set + fieldBit := big.NewInt(1) + fieldBit.Lsh(fieldBit, uint(i)) + if big.NewInt(0).And(explicitFields, fieldBit).Sign() != 0 { + // Remove omitempty from the json tag + tag := field.Tag.Get("json") + if tag != "" && tag != "-" { + // Parse the json tag, remove omitempty from options + parts := strings.Split(tag, ",") + if len(parts) > 1 { + var newParts []string + newParts = append(newParts, parts[0]) // Keep the field name + for _, part := range parts[1:] { + if strings.TrimSpace(part) != "omitempty" { + newParts = append(newParts, part) + } + } + tag = strings.Join(newParts, ",") + } + + // Reconstruct the struct tag + newTag := `json:"` + tag + `"` + if urlTag := field.Tag.Get("url"); urlTag != "" { + newTag += ` url:"` + urlTag + `"` + } + + field.Tag = reflect.StructTag(newTag) + } + } + + fields = append(fields, field) + } + + // Create new struct type with modified tags + newType := reflect.StructOf(fields) + newVal := reflect.New(newType).Elem() + + // Copy field values from original struct to new struct + fieldIndex := 0 + for i := 0; i < sourceType.NumField(); i++ { + originalField := sourceType.Field(i) + + // Skip unexported fields and the explicitFields field itself + if !originalField.IsExported() || originalField.Name == "explicitFields" { + continue + } + + originalValue := sourceVal.Field(i) + newVal.Field(fieldIndex).Set(originalValue) + fieldIndex++ + } + + return newVal.Interface() +} diff --git a/seed/go-fiber/inferred-auth-implicit-reference/internal/explicit_fields_test.go b/seed/go-fiber/inferred-auth-implicit-reference/internal/explicit_fields_test.go new file mode 100644 index 000000000000..d3ec507de14b --- /dev/null +++ b/seed/go-fiber/inferred-auth-implicit-reference/internal/explicit_fields_test.go @@ -0,0 +1,497 @@ +package internal + +import ( + "encoding/json" + "math/big" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type testExplicitFieldsStruct struct { + Name *string `json:"name,omitempty"` + Code *string `json:"code,omitempty"` + Count *int `json:"count,omitempty"` + Enabled *bool `json:"enabled,omitempty"` + Tags []string `json:"tags,omitempty"` + //lint:ignore unused this field is intentionally unused for testing + unexported string `json:"-"` + explicitFields *big.Int `json:"-"` +} + +var ( + testFieldName = big.NewInt(1 << 0) + testFieldCode = big.NewInt(1 << 1) + testFieldCount = big.NewInt(1 << 2) + testFieldEnabled = big.NewInt(1 << 3) + testFieldTags = big.NewInt(1 << 4) +) + +func (t *testExplicitFieldsStruct) require(field *big.Int) { + if t.explicitFields == nil { + t.explicitFields = big.NewInt(0) + } + t.explicitFields.Or(t.explicitFields, field) +} + +func (t *testExplicitFieldsStruct) SetName(name *string) { + t.Name = name + t.require(testFieldName) +} + +func (t *testExplicitFieldsStruct) SetCode(code *string) { + t.Code = code + t.require(testFieldCode) +} + +func (t *testExplicitFieldsStruct) SetCount(count *int) { + t.Count = count + t.require(testFieldCount) +} + +func (t *testExplicitFieldsStruct) SetEnabled(enabled *bool) { + t.Enabled = enabled + t.require(testFieldEnabled) +} + +func (t *testExplicitFieldsStruct) SetTags(tags []string) { + t.Tags = tags + t.require(testFieldTags) +} + +func (t *testExplicitFieldsStruct) MarshalJSON() ([]byte, error) { + type embed testExplicitFieldsStruct + var marshaler = struct { + embed + }{ + embed: embed(*t), + } + return json.Marshal(HandleExplicitFields(marshaler, t.explicitFields)) +} + +type testStructWithoutExplicitFields struct { + Name *string `json:"name,omitempty"` + Code *string `json:"code,omitempty"` +} + +func TestHandleExplicitFields(t *testing.T) { + tests := []struct { + desc string + giveInput interface{} + wantBytes []byte + wantError string + }{ + { + desc: "nil input", + giveInput: nil, + wantBytes: []byte(`null`), + }, + { + desc: "non-struct input", + giveInput: "string", + wantBytes: []byte(`"string"`), + }, + { + desc: "slice input", + giveInput: []string{"a", "b"}, + wantBytes: []byte(`["a","b"]`), + }, + { + desc: "map input", + giveInput: map[string]interface{}{"key": "value"}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "struct without explicitFields field", + giveInput: &testStructWithoutExplicitFields{ + Name: stringPtr("test"), + Code: nil, + }, + wantBytes: []byte(`{"name":"test"}`), + }, + { + desc: "struct with no explicit fields set", + giveInput: &testExplicitFieldsStruct{ + Name: stringPtr("test"), + Code: nil, + }, + wantBytes: []byte(`{"name":"test"}`), + }, + { + desc: "struct with explicit nil field", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{ + Name: stringPtr("test"), + } + s.SetCode(nil) + return s + }(), + wantBytes: []byte(`{"name":"test","code":null}`), + }, + { + desc: "struct with explicit non-nil field", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{} + s.SetName(stringPtr("explicit")) + s.SetCode(stringPtr("also-explicit")) + return s + }(), + wantBytes: []byte(`{"name":"explicit","code":"also-explicit"}`), + }, + { + desc: "struct with mixed explicit and implicit fields", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{ + Name: stringPtr("implicit"), + Count: intPtr(42), + } + s.SetCode(nil) // explicit nil + return s + }(), + wantBytes: []byte(`{"name":"implicit","code":null,"count":42}`), + }, + { + desc: "struct with multiple explicit nil fields", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{ + Name: stringPtr("test"), + } + s.SetCode(nil) + s.SetCount(nil) + return s + }(), + wantBytes: []byte(`{"name":"test","code":null,"count":null}`), + }, + { + desc: "struct with slice field", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{ + Tags: []string{"tag1", "tag2"}, + } + s.SetTags(nil) // explicit nil slice + return s + }(), + wantBytes: []byte(`{"tags":null}`), + }, + { + desc: "struct with boolean field", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{} + s.SetEnabled(boolPtr(false)) // explicit false + return s + }(), + wantBytes: []byte(`{"enabled":false}`), + }, + { + desc: "struct with all fields explicit", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{} + s.SetName(stringPtr("test")) + s.SetCode(nil) + s.SetCount(intPtr(0)) + s.SetEnabled(boolPtr(false)) + s.SetTags([]string{}) + return s + }(), + wantBytes: []byte(`{"name":"test","code":null,"count":0,"enabled":false,"tags":[]}`), + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + var explicitFields *big.Int + if s, ok := tt.giveInput.(*testExplicitFieldsStruct); ok { + explicitFields = s.explicitFields + } + bytes, err := json.Marshal(HandleExplicitFields(tt.giveInput, explicitFields)) + if tt.wantError != "" { + require.EqualError(t, err, tt.wantError) + assert.Nil(t, tt.wantBytes) + return + } + require.NoError(t, err) + assert.JSONEq(t, string(tt.wantBytes), string(bytes)) + + // Verify it's valid JSON + var value interface{} + require.NoError(t, json.Unmarshal(bytes, &value)) + }) + } +} + +func TestHandleExplicitFieldsCustomMarshaler(t *testing.T) { + t.Run("custom marshaler with explicit fields", func(t *testing.T) { + s := &testExplicitFieldsStruct{} + s.SetName(nil) + s.SetCode(stringPtr("test-code")) + + bytes, err := s.MarshalJSON() + require.NoError(t, err) + assert.JSONEq(t, `{"name":null,"code":"test-code"}`, string(bytes)) + }) + + t.Run("custom marshaler with no explicit fields", func(t *testing.T) { + s := &testExplicitFieldsStruct{ + Name: stringPtr("implicit"), + Code: stringPtr("also-implicit"), + } + + bytes, err := s.MarshalJSON() + require.NoError(t, err) + assert.JSONEq(t, `{"name":"implicit","code":"also-implicit"}`, string(bytes)) + }) +} + +func TestHandleExplicitFieldsPointerHandling(t *testing.T) { + t.Run("nil pointer", func(t *testing.T) { + var s *testExplicitFieldsStruct + bytes, err := json.Marshal(HandleExplicitFields(s, nil)) + require.NoError(t, err) + assert.Equal(t, []byte(`null`), bytes) + }) + + t.Run("pointer to struct", func(t *testing.T) { + s := &testExplicitFieldsStruct{} + s.SetName(nil) + + bytes, err := json.Marshal(HandleExplicitFields(s, s.explicitFields)) + require.NoError(t, err) + assert.JSONEq(t, `{"name":null}`, string(bytes)) + }) +} + +func TestHandleExplicitFieldsEmbeddedStruct(t *testing.T) { + t.Run("embedded struct with explicit fields", func(t *testing.T) { + // Create a struct similar to what MarshalJSON creates + s := &testExplicitFieldsStruct{} + s.SetName(nil) + s.SetCode(stringPtr("test-code")) + + type embed testExplicitFieldsStruct + var marshaler = struct { + embed + }{ + embed: embed(*s), + } + + bytes, err := json.Marshal(HandleExplicitFields(marshaler, s.explicitFields)) + require.NoError(t, err) + // Should include both explicit fields (name as null, code as "test-code") + assert.JSONEq(t, `{"name":null,"code":"test-code"}`, string(bytes)) + }) + + t.Run("embedded struct with no explicit fields", func(t *testing.T) { + s := &testExplicitFieldsStruct{ + Name: stringPtr("implicit"), + Code: stringPtr("also-implicit"), + } + + type embed testExplicitFieldsStruct + var marshaler = struct { + embed + }{ + embed: embed(*s), + } + + bytes, err := json.Marshal(HandleExplicitFields(marshaler, s.explicitFields)) + require.NoError(t, err) + // Should only include non-nil fields (omitempty behavior) + assert.JSONEq(t, `{"name":"implicit","code":"also-implicit"}`, string(bytes)) + }) + + t.Run("embedded struct with mixed fields", func(t *testing.T) { + s := &testExplicitFieldsStruct{ + Count: intPtr(42), // implicit field + } + s.SetName(nil) // explicit nil + s.SetCode(stringPtr("explicit")) // explicit value + + type embed testExplicitFieldsStruct + var marshaler = struct { + embed + }{ + embed: embed(*s), + } + + bytes, err := json.Marshal(HandleExplicitFields(marshaler, s.explicitFields)) + require.NoError(t, err) + // Should include explicit null, explicit value, and implicit value + assert.JSONEq(t, `{"name":null,"code":"explicit","count":42}`, string(bytes)) + }) +} + +func TestHandleExplicitFieldsTagHandling(t *testing.T) { + type testStructWithComplexTags struct { + Field1 *string `json:"field1,omitempty" url:"field1,omitempty"` + Field2 *string `json:"field2,omitempty,string" url:"field2"` + Field3 *string `json:"-"` + Field4 *string `json:"field4"` + explicitFields *big.Int `json:"-"` + } + + s := &testStructWithComplexTags{ + Field1: stringPtr("test1"), + Field4: stringPtr("test4"), + explicitFields: big.NewInt(1), // Only first field is explicit + } + + bytes, err := json.Marshal(HandleExplicitFields(s, s.explicitFields)) + require.NoError(t, err) + + // Field1 should have omitempty removed, Field2 should keep omitempty, Field4 should be included + assert.JSONEq(t, `{"field1":"test1","field4":"test4"}`, string(bytes)) +} + +// Test types for nested struct explicit fields testing +type testNestedStruct struct { + NestedName *string `json:"nested_name,omitempty"` + NestedCode *string `json:"nested_code,omitempty"` + explicitFields *big.Int `json:"-"` +} + +type testParentStruct struct { + ParentName *string `json:"parent_name,omitempty"` + Nested *testNestedStruct `json:"nested,omitempty"` + explicitFields *big.Int `json:"-"` +} + +var ( + nestedFieldName = big.NewInt(1 << 0) + nestedFieldCode = big.NewInt(1 << 1) +) + +var ( + parentFieldName = big.NewInt(1 << 0) + parentFieldNested = big.NewInt(1 << 1) +) + +func (n *testNestedStruct) require(field *big.Int) { + if n.explicitFields == nil { + n.explicitFields = big.NewInt(0) + } + n.explicitFields.Or(n.explicitFields, field) +} + +func (n *testNestedStruct) SetNestedName(name *string) { + n.NestedName = name + n.require(nestedFieldName) +} + +func (n *testNestedStruct) SetNestedCode(code *string) { + n.NestedCode = code + n.require(nestedFieldCode) +} + +func (n *testNestedStruct) MarshalJSON() ([]byte, error) { + type embed testNestedStruct + var marshaler = struct { + embed + }{ + embed: embed(*n), + } + return json.Marshal(HandleExplicitFields(marshaler, n.explicitFields)) +} + +func (p *testParentStruct) require(field *big.Int) { + if p.explicitFields == nil { + p.explicitFields = big.NewInt(0) + } + p.explicitFields.Or(p.explicitFields, field) +} + +func (p *testParentStruct) SetParentName(name *string) { + p.ParentName = name + p.require(parentFieldName) +} + +func (p *testParentStruct) SetNested(nested *testNestedStruct) { + p.Nested = nested + p.require(parentFieldNested) +} + +func (p *testParentStruct) MarshalJSON() ([]byte, error) { + type embed testParentStruct + var marshaler = struct { + embed + }{ + embed: embed(*p), + } + return json.Marshal(HandleExplicitFields(marshaler, p.explicitFields)) +} + +func TestHandleExplicitFieldsNestedStruct(t *testing.T) { + tests := []struct { + desc string + setupFunc func() *testParentStruct + wantBytes []byte + }{ + { + desc: "nested struct with explicit nil in nested object", + setupFunc: func() *testParentStruct { + nested := &testNestedStruct{ + NestedName: stringPtr("implicit-nested"), + } + nested.SetNestedCode(nil) // explicit nil + + return &testParentStruct{ + ParentName: stringPtr("implicit-parent"), + Nested: nested, + } + }, + wantBytes: []byte(`{"parent_name":"implicit-parent","nested":{"nested_name":"implicit-nested","nested_code":null}}`), + }, + { + desc: "parent with explicit nil nested struct", + setupFunc: func() *testParentStruct { + parent := &testParentStruct{ + ParentName: stringPtr("implicit-parent"), + } + parent.SetNested(nil) // explicit nil nested struct + return parent + }, + wantBytes: []byte(`{"parent_name":"implicit-parent","nested":null}`), + }, + { + desc: "all explicit fields in nested structure", + setupFunc: func() *testParentStruct { + nested := &testNestedStruct{} + nested.SetNestedName(stringPtr("explicit-nested")) + nested.SetNestedCode(nil) // explicit nil + + parent := &testParentStruct{} + parent.SetParentName(nil) // explicit nil + parent.SetNested(nested) // explicit nested struct + + return parent + }, + wantBytes: []byte(`{"parent_name":null,"nested":{"nested_name":"explicit-nested","nested_code":null}}`), + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + parent := tt.setupFunc() + bytes, err := parent.MarshalJSON() + require.NoError(t, err) + assert.JSONEq(t, string(tt.wantBytes), string(bytes)) + + // Verify it's valid JSON + var value interface{} + require.NoError(t, json.Unmarshal(bytes, &value)) + }) + } +} + +// Helper functions +func stringPtr(s string) *string { + return &s +} + +func intPtr(i int) *int { + return &i +} + +func boolPtr(b bool) *bool { + return &b +} diff --git a/seed/go-fiber/inferred-auth-implicit-reference/internal/extra_properties.go b/seed/go-fiber/inferred-auth-implicit-reference/internal/extra_properties.go new file mode 100644 index 000000000000..540c3fd89eeb --- /dev/null +++ b/seed/go-fiber/inferred-auth-implicit-reference/internal/extra_properties.go @@ -0,0 +1,141 @@ +package internal + +import ( + "bytes" + "encoding/json" + "fmt" + "reflect" + "strings" +) + +// MarshalJSONWithExtraProperty marshals the given value to JSON, including the extra property. +func MarshalJSONWithExtraProperty(marshaler interface{}, key string, value interface{}) ([]byte, error) { + return MarshalJSONWithExtraProperties(marshaler, map[string]interface{}{key: value}) +} + +// MarshalJSONWithExtraProperties marshals the given value to JSON, including any extra properties. +func MarshalJSONWithExtraProperties(marshaler interface{}, extraProperties map[string]interface{}) ([]byte, error) { + bytes, err := json.Marshal(marshaler) + if err != nil { + return nil, err + } + if len(extraProperties) == 0 { + return bytes, nil + } + keys, err := getKeys(marshaler) + if err != nil { + return nil, err + } + for _, key := range keys { + if _, ok := extraProperties[key]; ok { + return nil, fmt.Errorf("cannot add extra property %q because it is already defined on the type", key) + } + } + extraBytes, err := json.Marshal(extraProperties) + if err != nil { + return nil, err + } + if isEmptyJSON(bytes) { + if isEmptyJSON(extraBytes) { + return bytes, nil + } + return extraBytes, nil + } + result := bytes[:len(bytes)-1] + result = append(result, ',') + result = append(result, extraBytes[1:len(extraBytes)-1]...) + result = append(result, '}') + return result, nil +} + +// ExtractExtraProperties extracts any extra properties from the given value. +func ExtractExtraProperties(bytes []byte, value interface{}, exclude ...string) (map[string]interface{}, error) { + val := reflect.ValueOf(value) + for val.Kind() == reflect.Ptr { + if val.IsNil() { + return nil, fmt.Errorf("value must be non-nil to extract extra properties") + } + val = val.Elem() + } + if err := json.Unmarshal(bytes, &value); err != nil { + return nil, err + } + var extraProperties map[string]interface{} + if err := json.Unmarshal(bytes, &extraProperties); err != nil { + return nil, err + } + for i := 0; i < val.Type().NumField(); i++ { + key := jsonKey(val.Type().Field(i)) + if key == "" || key == "-" { + continue + } + delete(extraProperties, key) + } + for _, key := range exclude { + delete(extraProperties, key) + } + if len(extraProperties) == 0 { + return nil, nil + } + return extraProperties, nil +} + +// getKeys returns the keys associated with the given value. The value must be a +// a struct or a map with string keys. +func getKeys(value interface{}) ([]string, error) { + val := reflect.ValueOf(value) + if val.Kind() == reflect.Ptr { + val = val.Elem() + } + if !val.IsValid() { + return nil, nil + } + switch val.Kind() { + case reflect.Struct: + return getKeysForStructType(val.Type()), nil + case reflect.Map: + var keys []string + if val.Type().Key().Kind() != reflect.String { + return nil, fmt.Errorf("cannot extract keys from %T; only structs and maps with string keys are supported", value) + } + for _, key := range val.MapKeys() { + keys = append(keys, key.String()) + } + return keys, nil + default: + return nil, fmt.Errorf("cannot extract keys from %T; only structs and maps with string keys are supported", value) + } +} + +// getKeysForStructType returns all the keys associated with the given struct type, +// visiting embedded fields recursively. +func getKeysForStructType(structType reflect.Type) []string { + if structType.Kind() == reflect.Pointer { + structType = structType.Elem() + } + if structType.Kind() != reflect.Struct { + return nil + } + var keys []string + for i := 0; i < structType.NumField(); i++ { + field := structType.Field(i) + if field.Anonymous { + keys = append(keys, getKeysForStructType(field.Type)...) + continue + } + keys = append(keys, jsonKey(field)) + } + return keys +} + +// jsonKey returns the JSON key from the struct tag of the given field, +// excluding the omitempty flag (if any). +func jsonKey(field reflect.StructField) string { + return strings.TrimSuffix(field.Tag.Get("json"), ",omitempty") +} + +// isEmptyJSON returns true if the given data is empty, the empty JSON object, or +// an explicit null. +func isEmptyJSON(data []byte) bool { + return len(data) <= 2 || bytes.Equal(data, []byte("null")) +} diff --git a/seed/go-fiber/inferred-auth-implicit-reference/internal/extra_properties_test.go b/seed/go-fiber/inferred-auth-implicit-reference/internal/extra_properties_test.go new file mode 100644 index 000000000000..aa2510ee5121 --- /dev/null +++ b/seed/go-fiber/inferred-auth-implicit-reference/internal/extra_properties_test.go @@ -0,0 +1,228 @@ +package internal + +import ( + "encoding/json" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type testMarshaler struct { + Name string `json:"name"` + BirthDate time.Time `json:"birthDate"` + CreatedAt time.Time `json:"created_at"` +} + +func (t *testMarshaler) MarshalJSON() ([]byte, error) { + type embed testMarshaler + var marshaler = struct { + embed + BirthDate string `json:"birthDate"` + CreatedAt string `json:"created_at"` + }{ + embed: embed(*t), + BirthDate: t.BirthDate.Format("2006-01-02"), + CreatedAt: t.CreatedAt.Format(time.RFC3339), + } + return MarshalJSONWithExtraProperty(marshaler, "type", "test") +} + +func TestMarshalJSONWithExtraProperties(t *testing.T) { + tests := []struct { + desc string + giveMarshaler interface{} + giveExtraProperties map[string]interface{} + wantBytes []byte + wantError string + }{ + { + desc: "invalid type", + giveMarshaler: []string{"invalid"}, + giveExtraProperties: map[string]interface{}{"key": "overwrite"}, + wantError: `cannot extract keys from []string; only structs and maps with string keys are supported`, + }, + { + desc: "invalid key type", + giveMarshaler: map[int]interface{}{42: "value"}, + giveExtraProperties: map[string]interface{}{"key": "overwrite"}, + wantError: `cannot extract keys from map[int]interface {}; only structs and maps with string keys are supported`, + }, + { + desc: "invalid map overwrite", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{"key": "overwrite"}, + wantError: `cannot add extra property "key" because it is already defined on the type`, + }, + { + desc: "invalid struct overwrite", + giveMarshaler: new(testMarshaler), + giveExtraProperties: map[string]interface{}{"birthDate": "2000-01-01"}, + wantError: `cannot add extra property "birthDate" because it is already defined on the type`, + }, + { + desc: "invalid struct overwrite embedded type", + giveMarshaler: new(testMarshaler), + giveExtraProperties: map[string]interface{}{"name": "bob"}, + wantError: `cannot add extra property "name" because it is already defined on the type`, + }, + { + desc: "nil", + giveMarshaler: nil, + giveExtraProperties: nil, + wantBytes: []byte(`null`), + }, + { + desc: "empty", + giveMarshaler: map[string]interface{}{}, + giveExtraProperties: map[string]interface{}{}, + wantBytes: []byte(`{}`), + }, + { + desc: "no extra properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "only extra properties", + giveMarshaler: map[string]interface{}{}, + giveExtraProperties: map[string]interface{}{"key": "value"}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "single extra property", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{"extra": "property"}, + wantBytes: []byte(`{"key":"value","extra":"property"}`), + }, + { + desc: "multiple extra properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{"one": 1, "two": 2}, + wantBytes: []byte(`{"key":"value","one":1,"two":2}`), + }, + { + desc: "nested properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{ + "user": map[string]interface{}{ + "age": 42, + "name": "alice", + }, + }, + wantBytes: []byte(`{"key":"value","user":{"age":42,"name":"alice"}}`), + }, + { + desc: "multiple nested properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{ + "metadata": map[string]interface{}{ + "ip": "127.0.0.1", + }, + "user": map[string]interface{}{ + "age": 42, + "name": "alice", + }, + }, + wantBytes: []byte(`{"key":"value","metadata":{"ip":"127.0.0.1"},"user":{"age":42,"name":"alice"}}`), + }, + { + desc: "custom marshaler", + giveMarshaler: &testMarshaler{ + Name: "alice", + BirthDate: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), + CreatedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC), + }, + giveExtraProperties: map[string]interface{}{ + "extra": "property", + }, + wantBytes: []byte(`{"name":"alice","birthDate":"2000-01-01","created_at":"2024-01-01T00:00:00Z","type":"test","extra":"property"}`), + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + bytes, err := MarshalJSONWithExtraProperties(tt.giveMarshaler, tt.giveExtraProperties) + if tt.wantError != "" { + require.EqualError(t, err, tt.wantError) + assert.Nil(t, tt.wantBytes) + return + } + require.NoError(t, err) + assert.Equal(t, tt.wantBytes, bytes) + + value := make(map[string]interface{}) + require.NoError(t, json.Unmarshal(bytes, &value)) + }) + } +} + +func TestExtractExtraProperties(t *testing.T) { + t.Run("none", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice"}`), value) + require.NoError(t, err) + assert.Nil(t, extraProperties) + }) + + t.Run("non-nil pointer", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]interface{}{"age": float64(42)}, extraProperties) + }) + + t.Run("nil pointer", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + var value *user + _, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + assert.EqualError(t, err, "value must be non-nil to extract extra properties") + }) + + t.Run("non-zero value", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]interface{}{"age": float64(42)}, extraProperties) + }) + + t.Run("zero value", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + var value user + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]interface{}{"age": float64(42)}, extraProperties) + }) + + t.Run("exclude", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value, "age") + require.NoError(t, err) + assert.Nil(t, extraProperties) + }) +} diff --git a/seed/go-fiber/inferred-auth-implicit-reference/internal/stringer.go b/seed/go-fiber/inferred-auth-implicit-reference/internal/stringer.go new file mode 100644 index 000000000000..312801851e0e --- /dev/null +++ b/seed/go-fiber/inferred-auth-implicit-reference/internal/stringer.go @@ -0,0 +1,13 @@ +package internal + +import "encoding/json" + +// StringifyJSON returns a pretty JSON string representation of +// the given value. +func StringifyJSON(value interface{}) (string, error) { + bytes, err := json.MarshalIndent(value, "", " ") + if err != nil { + return "", err + } + return string(bytes), nil +} diff --git a/seed/go-fiber/inferred-auth-implicit-reference/internal/time.go b/seed/go-fiber/inferred-auth-implicit-reference/internal/time.go new file mode 100644 index 000000000000..ab0e269fade3 --- /dev/null +++ b/seed/go-fiber/inferred-auth-implicit-reference/internal/time.go @@ -0,0 +1,137 @@ +package internal + +import ( + "encoding/json" + "time" +) + +const dateFormat = "2006-01-02" + +// DateTime wraps time.Time and adapts its JSON representation +// to conform to a RFC3339 date (e.g. 2006-01-02). +// +// Ref: https://ijmacd.github.io/rfc3339-iso8601 +type Date struct { + t *time.Time +} + +// NewDate returns a new *Date. If the given time.Time +// is nil, nil will be returned. +func NewDate(t time.Time) *Date { + return &Date{t: &t} +} + +// NewOptionalDate returns a new *Date. If the given time.Time +// is nil, nil will be returned. +func NewOptionalDate(t *time.Time) *Date { + if t == nil { + return nil + } + return &Date{t: t} +} + +// Time returns the Date's underlying time, if any. If the +// date is nil, the zero value is returned. +func (d *Date) Time() time.Time { + if d == nil || d.t == nil { + return time.Time{} + } + return *d.t +} + +// TimePtr returns a pointer to the Date's underlying time.Time, if any. +func (d *Date) TimePtr() *time.Time { + if d == nil || d.t == nil { + return nil + } + if d.t.IsZero() { + return nil + } + return d.t +} + +func (d *Date) MarshalJSON() ([]byte, error) { + if d == nil || d.t == nil { + return nil, nil + } + return json.Marshal(d.t.Format(dateFormat)) +} + +func (d *Date) UnmarshalJSON(data []byte) error { + var raw string + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + + parsedTime, err := time.Parse(dateFormat, raw) + if err != nil { + return err + } + + *d = Date{t: &parsedTime} + return nil +} + +// DateTime wraps time.Time and adapts its JSON representation +// to conform to a RFC3339 date-time (e.g. 2017-07-21T17:32:28Z). +// +// Ref: https://ijmacd.github.io/rfc3339-iso8601 +type DateTime struct { + t *time.Time +} + +// NewDateTime returns a new *DateTime. +func NewDateTime(t time.Time) *DateTime { + return &DateTime{t: &t} +} + +// NewOptionalDateTime returns a new *DateTime. If the given time.Time +// is nil, nil will be returned. +func NewOptionalDateTime(t *time.Time) *DateTime { + if t == nil { + return nil + } + return &DateTime{t: t} +} + +// Time returns the DateTime's underlying time, if any. If the +// date-time is nil, the zero value is returned. +func (d *DateTime) Time() time.Time { + if d == nil || d.t == nil { + return time.Time{} + } + return *d.t +} + +// TimePtr returns a pointer to the DateTime's underlying time.Time, if any. +func (d *DateTime) TimePtr() *time.Time { + if d == nil || d.t == nil { + return nil + } + if d.t.IsZero() { + return nil + } + return d.t +} + +func (d *DateTime) MarshalJSON() ([]byte, error) { + if d == nil || d.t == nil { + return nil, nil + } + return json.Marshal(d.t.Format(time.RFC3339)) +} + +func (d *DateTime) UnmarshalJSON(data []byte) error { + var raw string + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + + parsedTime, err := time.Parse(time.RFC3339, raw) + if err != nil { + return err + } + + *d = DateTime{t: &parsedTime} + return nil +} diff --git a/seed/go-fiber/inferred-auth-implicit-reference/snippet.json b/seed/go-fiber/inferred-auth-implicit-reference/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/go-fiber/nullable-allof-extends/.github/workflows/ci.yml b/seed/go-fiber/nullable-allof-extends/.github/workflows/ci.yml new file mode 100644 index 000000000000..56310d69624b --- /dev/null +++ b/seed/go-fiber/nullable-allof-extends/.github/workflows/ci.yml @@ -0,0 +1,35 @@ +name: ci + +on: [push] + +jobs: + compile: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up go + uses: actions/setup-go@v4 + + - name: Compile + run: go build ./... + test: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up go + uses: actions/setup-go@v4 + + - name: Setup wiremock server + run: | + if [ -f wiremock/docker-compose.test.yml ]; then docker compose -f wiremock/docker-compose.test.yml down && docker compose -f wiremock/docker-compose.test.yml up -d; fi + + - name: Test + run: go test ./... + + - name: Teardown wiremock server + run: | + if [ -f wiremock/docker-compose.test.yml ]; then docker compose -f wiremock/docker-compose.test.yml down; fi diff --git a/seed/go-fiber/nullable-allof-extends/go.mod b/seed/go-fiber/nullable-allof-extends/go.mod new file mode 100644 index 000000000000..8f6c7a769f71 --- /dev/null +++ b/seed/go-fiber/nullable-allof-extends/go.mod @@ -0,0 +1,11 @@ +module github.com/nullable-allof-extends/fern + +go 1.18 + +require github.com/stretchr/testify v1.7.0 + +require ( + github.com/davecgh/go-spew v1.1.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/seed/go-fiber/nullable-allof-extends/go.sum b/seed/go-fiber/nullable-allof-extends/go.sum new file mode 100644 index 000000000000..fc3dd9e67e82 --- /dev/null +++ b/seed/go-fiber/nullable-allof-extends/go.sum @@ -0,0 +1,12 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/seed/go-fiber/nullable-allof-extends/internal/explicit_fields.go b/seed/go-fiber/nullable-allof-extends/internal/explicit_fields.go new file mode 100644 index 000000000000..4bdf34fc2b7c --- /dev/null +++ b/seed/go-fiber/nullable-allof-extends/internal/explicit_fields.go @@ -0,0 +1,116 @@ +package internal + +import ( + "math/big" + "reflect" + "strings" +) + +// HandleExplicitFields processes a struct to remove `omitempty` from +// fields that have been explicitly set (as indicated by their corresponding bit in explicitFields). +// Note that `marshaler` should be an embedded struct to avoid infinite recursion. +// Returns an interface{} that can be passed to json.Marshal. +func HandleExplicitFields(marshaler interface{}, explicitFields *big.Int) interface{} { + val := reflect.ValueOf(marshaler) + typ := reflect.TypeOf(marshaler) + + // Handle pointer types + if val.Kind() == reflect.Ptr { + if val.IsNil() { + return nil + } + val = val.Elem() + typ = typ.Elem() + } + + // Only handle struct types + if val.Kind() != reflect.Struct { + return marshaler + } + + // Handle embedded struct pattern + var sourceVal reflect.Value + var sourceType reflect.Type + + // Check if this is an embedded struct pattern + if typ.NumField() == 1 && typ.Field(0).Anonymous { + // This is likely an embedded struct, get the embedded value + embeddedField := val.Field(0) + sourceVal = embeddedField + sourceType = embeddedField.Type() + } else { + // Regular struct + sourceVal = val + sourceType = typ + } + + // If no explicit fields set, use standard marshaling + if explicitFields == nil || explicitFields.Sign() == 0 { + return marshaler + } + + // Create a new struct type with modified tags + fields := make([]reflect.StructField, 0, sourceType.NumField()) + + for i := 0; i < sourceType.NumField(); i++ { + field := sourceType.Field(i) + + // Skip unexported fields and the explicitFields field itself + if !field.IsExported() || field.Name == "explicitFields" { + continue + } + + // Check if this field has been explicitly set + fieldBit := big.NewInt(1) + fieldBit.Lsh(fieldBit, uint(i)) + if big.NewInt(0).And(explicitFields, fieldBit).Sign() != 0 { + // Remove omitempty from the json tag + tag := field.Tag.Get("json") + if tag != "" && tag != "-" { + // Parse the json tag, remove omitempty from options + parts := strings.Split(tag, ",") + if len(parts) > 1 { + var newParts []string + newParts = append(newParts, parts[0]) // Keep the field name + for _, part := range parts[1:] { + if strings.TrimSpace(part) != "omitempty" { + newParts = append(newParts, part) + } + } + tag = strings.Join(newParts, ",") + } + + // Reconstruct the struct tag + newTag := `json:"` + tag + `"` + if urlTag := field.Tag.Get("url"); urlTag != "" { + newTag += ` url:"` + urlTag + `"` + } + + field.Tag = reflect.StructTag(newTag) + } + } + + fields = append(fields, field) + } + + // Create new struct type with modified tags + newType := reflect.StructOf(fields) + newVal := reflect.New(newType).Elem() + + // Copy field values from original struct to new struct + fieldIndex := 0 + for i := 0; i < sourceType.NumField(); i++ { + originalField := sourceType.Field(i) + + // Skip unexported fields and the explicitFields field itself + if !originalField.IsExported() || originalField.Name == "explicitFields" { + continue + } + + originalValue := sourceVal.Field(i) + newVal.Field(fieldIndex).Set(originalValue) + fieldIndex++ + } + + return newVal.Interface() +} diff --git a/seed/go-fiber/nullable-allof-extends/internal/explicit_fields_test.go b/seed/go-fiber/nullable-allof-extends/internal/explicit_fields_test.go new file mode 100644 index 000000000000..d3ec507de14b --- /dev/null +++ b/seed/go-fiber/nullable-allof-extends/internal/explicit_fields_test.go @@ -0,0 +1,497 @@ +package internal + +import ( + "encoding/json" + "math/big" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type testExplicitFieldsStruct struct { + Name *string `json:"name,omitempty"` + Code *string `json:"code,omitempty"` + Count *int `json:"count,omitempty"` + Enabled *bool `json:"enabled,omitempty"` + Tags []string `json:"tags,omitempty"` + //lint:ignore unused this field is intentionally unused for testing + unexported string `json:"-"` + explicitFields *big.Int `json:"-"` +} + +var ( + testFieldName = big.NewInt(1 << 0) + testFieldCode = big.NewInt(1 << 1) + testFieldCount = big.NewInt(1 << 2) + testFieldEnabled = big.NewInt(1 << 3) + testFieldTags = big.NewInt(1 << 4) +) + +func (t *testExplicitFieldsStruct) require(field *big.Int) { + if t.explicitFields == nil { + t.explicitFields = big.NewInt(0) + } + t.explicitFields.Or(t.explicitFields, field) +} + +func (t *testExplicitFieldsStruct) SetName(name *string) { + t.Name = name + t.require(testFieldName) +} + +func (t *testExplicitFieldsStruct) SetCode(code *string) { + t.Code = code + t.require(testFieldCode) +} + +func (t *testExplicitFieldsStruct) SetCount(count *int) { + t.Count = count + t.require(testFieldCount) +} + +func (t *testExplicitFieldsStruct) SetEnabled(enabled *bool) { + t.Enabled = enabled + t.require(testFieldEnabled) +} + +func (t *testExplicitFieldsStruct) SetTags(tags []string) { + t.Tags = tags + t.require(testFieldTags) +} + +func (t *testExplicitFieldsStruct) MarshalJSON() ([]byte, error) { + type embed testExplicitFieldsStruct + var marshaler = struct { + embed + }{ + embed: embed(*t), + } + return json.Marshal(HandleExplicitFields(marshaler, t.explicitFields)) +} + +type testStructWithoutExplicitFields struct { + Name *string `json:"name,omitempty"` + Code *string `json:"code,omitempty"` +} + +func TestHandleExplicitFields(t *testing.T) { + tests := []struct { + desc string + giveInput interface{} + wantBytes []byte + wantError string + }{ + { + desc: "nil input", + giveInput: nil, + wantBytes: []byte(`null`), + }, + { + desc: "non-struct input", + giveInput: "string", + wantBytes: []byte(`"string"`), + }, + { + desc: "slice input", + giveInput: []string{"a", "b"}, + wantBytes: []byte(`["a","b"]`), + }, + { + desc: "map input", + giveInput: map[string]interface{}{"key": "value"}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "struct without explicitFields field", + giveInput: &testStructWithoutExplicitFields{ + Name: stringPtr("test"), + Code: nil, + }, + wantBytes: []byte(`{"name":"test"}`), + }, + { + desc: "struct with no explicit fields set", + giveInput: &testExplicitFieldsStruct{ + Name: stringPtr("test"), + Code: nil, + }, + wantBytes: []byte(`{"name":"test"}`), + }, + { + desc: "struct with explicit nil field", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{ + Name: stringPtr("test"), + } + s.SetCode(nil) + return s + }(), + wantBytes: []byte(`{"name":"test","code":null}`), + }, + { + desc: "struct with explicit non-nil field", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{} + s.SetName(stringPtr("explicit")) + s.SetCode(stringPtr("also-explicit")) + return s + }(), + wantBytes: []byte(`{"name":"explicit","code":"also-explicit"}`), + }, + { + desc: "struct with mixed explicit and implicit fields", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{ + Name: stringPtr("implicit"), + Count: intPtr(42), + } + s.SetCode(nil) // explicit nil + return s + }(), + wantBytes: []byte(`{"name":"implicit","code":null,"count":42}`), + }, + { + desc: "struct with multiple explicit nil fields", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{ + Name: stringPtr("test"), + } + s.SetCode(nil) + s.SetCount(nil) + return s + }(), + wantBytes: []byte(`{"name":"test","code":null,"count":null}`), + }, + { + desc: "struct with slice field", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{ + Tags: []string{"tag1", "tag2"}, + } + s.SetTags(nil) // explicit nil slice + return s + }(), + wantBytes: []byte(`{"tags":null}`), + }, + { + desc: "struct with boolean field", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{} + s.SetEnabled(boolPtr(false)) // explicit false + return s + }(), + wantBytes: []byte(`{"enabled":false}`), + }, + { + desc: "struct with all fields explicit", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{} + s.SetName(stringPtr("test")) + s.SetCode(nil) + s.SetCount(intPtr(0)) + s.SetEnabled(boolPtr(false)) + s.SetTags([]string{}) + return s + }(), + wantBytes: []byte(`{"name":"test","code":null,"count":0,"enabled":false,"tags":[]}`), + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + var explicitFields *big.Int + if s, ok := tt.giveInput.(*testExplicitFieldsStruct); ok { + explicitFields = s.explicitFields + } + bytes, err := json.Marshal(HandleExplicitFields(tt.giveInput, explicitFields)) + if tt.wantError != "" { + require.EqualError(t, err, tt.wantError) + assert.Nil(t, tt.wantBytes) + return + } + require.NoError(t, err) + assert.JSONEq(t, string(tt.wantBytes), string(bytes)) + + // Verify it's valid JSON + var value interface{} + require.NoError(t, json.Unmarshal(bytes, &value)) + }) + } +} + +func TestHandleExplicitFieldsCustomMarshaler(t *testing.T) { + t.Run("custom marshaler with explicit fields", func(t *testing.T) { + s := &testExplicitFieldsStruct{} + s.SetName(nil) + s.SetCode(stringPtr("test-code")) + + bytes, err := s.MarshalJSON() + require.NoError(t, err) + assert.JSONEq(t, `{"name":null,"code":"test-code"}`, string(bytes)) + }) + + t.Run("custom marshaler with no explicit fields", func(t *testing.T) { + s := &testExplicitFieldsStruct{ + Name: stringPtr("implicit"), + Code: stringPtr("also-implicit"), + } + + bytes, err := s.MarshalJSON() + require.NoError(t, err) + assert.JSONEq(t, `{"name":"implicit","code":"also-implicit"}`, string(bytes)) + }) +} + +func TestHandleExplicitFieldsPointerHandling(t *testing.T) { + t.Run("nil pointer", func(t *testing.T) { + var s *testExplicitFieldsStruct + bytes, err := json.Marshal(HandleExplicitFields(s, nil)) + require.NoError(t, err) + assert.Equal(t, []byte(`null`), bytes) + }) + + t.Run("pointer to struct", func(t *testing.T) { + s := &testExplicitFieldsStruct{} + s.SetName(nil) + + bytes, err := json.Marshal(HandleExplicitFields(s, s.explicitFields)) + require.NoError(t, err) + assert.JSONEq(t, `{"name":null}`, string(bytes)) + }) +} + +func TestHandleExplicitFieldsEmbeddedStruct(t *testing.T) { + t.Run("embedded struct with explicit fields", func(t *testing.T) { + // Create a struct similar to what MarshalJSON creates + s := &testExplicitFieldsStruct{} + s.SetName(nil) + s.SetCode(stringPtr("test-code")) + + type embed testExplicitFieldsStruct + var marshaler = struct { + embed + }{ + embed: embed(*s), + } + + bytes, err := json.Marshal(HandleExplicitFields(marshaler, s.explicitFields)) + require.NoError(t, err) + // Should include both explicit fields (name as null, code as "test-code") + assert.JSONEq(t, `{"name":null,"code":"test-code"}`, string(bytes)) + }) + + t.Run("embedded struct with no explicit fields", func(t *testing.T) { + s := &testExplicitFieldsStruct{ + Name: stringPtr("implicit"), + Code: stringPtr("also-implicit"), + } + + type embed testExplicitFieldsStruct + var marshaler = struct { + embed + }{ + embed: embed(*s), + } + + bytes, err := json.Marshal(HandleExplicitFields(marshaler, s.explicitFields)) + require.NoError(t, err) + // Should only include non-nil fields (omitempty behavior) + assert.JSONEq(t, `{"name":"implicit","code":"also-implicit"}`, string(bytes)) + }) + + t.Run("embedded struct with mixed fields", func(t *testing.T) { + s := &testExplicitFieldsStruct{ + Count: intPtr(42), // implicit field + } + s.SetName(nil) // explicit nil + s.SetCode(stringPtr("explicit")) // explicit value + + type embed testExplicitFieldsStruct + var marshaler = struct { + embed + }{ + embed: embed(*s), + } + + bytes, err := json.Marshal(HandleExplicitFields(marshaler, s.explicitFields)) + require.NoError(t, err) + // Should include explicit null, explicit value, and implicit value + assert.JSONEq(t, `{"name":null,"code":"explicit","count":42}`, string(bytes)) + }) +} + +func TestHandleExplicitFieldsTagHandling(t *testing.T) { + type testStructWithComplexTags struct { + Field1 *string `json:"field1,omitempty" url:"field1,omitempty"` + Field2 *string `json:"field2,omitempty,string" url:"field2"` + Field3 *string `json:"-"` + Field4 *string `json:"field4"` + explicitFields *big.Int `json:"-"` + } + + s := &testStructWithComplexTags{ + Field1: stringPtr("test1"), + Field4: stringPtr("test4"), + explicitFields: big.NewInt(1), // Only first field is explicit + } + + bytes, err := json.Marshal(HandleExplicitFields(s, s.explicitFields)) + require.NoError(t, err) + + // Field1 should have omitempty removed, Field2 should keep omitempty, Field4 should be included + assert.JSONEq(t, `{"field1":"test1","field4":"test4"}`, string(bytes)) +} + +// Test types for nested struct explicit fields testing +type testNestedStruct struct { + NestedName *string `json:"nested_name,omitempty"` + NestedCode *string `json:"nested_code,omitempty"` + explicitFields *big.Int `json:"-"` +} + +type testParentStruct struct { + ParentName *string `json:"parent_name,omitempty"` + Nested *testNestedStruct `json:"nested,omitempty"` + explicitFields *big.Int `json:"-"` +} + +var ( + nestedFieldName = big.NewInt(1 << 0) + nestedFieldCode = big.NewInt(1 << 1) +) + +var ( + parentFieldName = big.NewInt(1 << 0) + parentFieldNested = big.NewInt(1 << 1) +) + +func (n *testNestedStruct) require(field *big.Int) { + if n.explicitFields == nil { + n.explicitFields = big.NewInt(0) + } + n.explicitFields.Or(n.explicitFields, field) +} + +func (n *testNestedStruct) SetNestedName(name *string) { + n.NestedName = name + n.require(nestedFieldName) +} + +func (n *testNestedStruct) SetNestedCode(code *string) { + n.NestedCode = code + n.require(nestedFieldCode) +} + +func (n *testNestedStruct) MarshalJSON() ([]byte, error) { + type embed testNestedStruct + var marshaler = struct { + embed + }{ + embed: embed(*n), + } + return json.Marshal(HandleExplicitFields(marshaler, n.explicitFields)) +} + +func (p *testParentStruct) require(field *big.Int) { + if p.explicitFields == nil { + p.explicitFields = big.NewInt(0) + } + p.explicitFields.Or(p.explicitFields, field) +} + +func (p *testParentStruct) SetParentName(name *string) { + p.ParentName = name + p.require(parentFieldName) +} + +func (p *testParentStruct) SetNested(nested *testNestedStruct) { + p.Nested = nested + p.require(parentFieldNested) +} + +func (p *testParentStruct) MarshalJSON() ([]byte, error) { + type embed testParentStruct + var marshaler = struct { + embed + }{ + embed: embed(*p), + } + return json.Marshal(HandleExplicitFields(marshaler, p.explicitFields)) +} + +func TestHandleExplicitFieldsNestedStruct(t *testing.T) { + tests := []struct { + desc string + setupFunc func() *testParentStruct + wantBytes []byte + }{ + { + desc: "nested struct with explicit nil in nested object", + setupFunc: func() *testParentStruct { + nested := &testNestedStruct{ + NestedName: stringPtr("implicit-nested"), + } + nested.SetNestedCode(nil) // explicit nil + + return &testParentStruct{ + ParentName: stringPtr("implicit-parent"), + Nested: nested, + } + }, + wantBytes: []byte(`{"parent_name":"implicit-parent","nested":{"nested_name":"implicit-nested","nested_code":null}}`), + }, + { + desc: "parent with explicit nil nested struct", + setupFunc: func() *testParentStruct { + parent := &testParentStruct{ + ParentName: stringPtr("implicit-parent"), + } + parent.SetNested(nil) // explicit nil nested struct + return parent + }, + wantBytes: []byte(`{"parent_name":"implicit-parent","nested":null}`), + }, + { + desc: "all explicit fields in nested structure", + setupFunc: func() *testParentStruct { + nested := &testNestedStruct{} + nested.SetNestedName(stringPtr("explicit-nested")) + nested.SetNestedCode(nil) // explicit nil + + parent := &testParentStruct{} + parent.SetParentName(nil) // explicit nil + parent.SetNested(nested) // explicit nested struct + + return parent + }, + wantBytes: []byte(`{"parent_name":null,"nested":{"nested_name":"explicit-nested","nested_code":null}}`), + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + parent := tt.setupFunc() + bytes, err := parent.MarshalJSON() + require.NoError(t, err) + assert.JSONEq(t, string(tt.wantBytes), string(bytes)) + + // Verify it's valid JSON + var value interface{} + require.NoError(t, json.Unmarshal(bytes, &value)) + }) + } +} + +// Helper functions +func stringPtr(s string) *string { + return &s +} + +func intPtr(i int) *int { + return &i +} + +func boolPtr(b bool) *bool { + return &b +} diff --git a/seed/go-fiber/nullable-allof-extends/internal/extra_properties.go b/seed/go-fiber/nullable-allof-extends/internal/extra_properties.go new file mode 100644 index 000000000000..540c3fd89eeb --- /dev/null +++ b/seed/go-fiber/nullable-allof-extends/internal/extra_properties.go @@ -0,0 +1,141 @@ +package internal + +import ( + "bytes" + "encoding/json" + "fmt" + "reflect" + "strings" +) + +// MarshalJSONWithExtraProperty marshals the given value to JSON, including the extra property. +func MarshalJSONWithExtraProperty(marshaler interface{}, key string, value interface{}) ([]byte, error) { + return MarshalJSONWithExtraProperties(marshaler, map[string]interface{}{key: value}) +} + +// MarshalJSONWithExtraProperties marshals the given value to JSON, including any extra properties. +func MarshalJSONWithExtraProperties(marshaler interface{}, extraProperties map[string]interface{}) ([]byte, error) { + bytes, err := json.Marshal(marshaler) + if err != nil { + return nil, err + } + if len(extraProperties) == 0 { + return bytes, nil + } + keys, err := getKeys(marshaler) + if err != nil { + return nil, err + } + for _, key := range keys { + if _, ok := extraProperties[key]; ok { + return nil, fmt.Errorf("cannot add extra property %q because it is already defined on the type", key) + } + } + extraBytes, err := json.Marshal(extraProperties) + if err != nil { + return nil, err + } + if isEmptyJSON(bytes) { + if isEmptyJSON(extraBytes) { + return bytes, nil + } + return extraBytes, nil + } + result := bytes[:len(bytes)-1] + result = append(result, ',') + result = append(result, extraBytes[1:len(extraBytes)-1]...) + result = append(result, '}') + return result, nil +} + +// ExtractExtraProperties extracts any extra properties from the given value. +func ExtractExtraProperties(bytes []byte, value interface{}, exclude ...string) (map[string]interface{}, error) { + val := reflect.ValueOf(value) + for val.Kind() == reflect.Ptr { + if val.IsNil() { + return nil, fmt.Errorf("value must be non-nil to extract extra properties") + } + val = val.Elem() + } + if err := json.Unmarshal(bytes, &value); err != nil { + return nil, err + } + var extraProperties map[string]interface{} + if err := json.Unmarshal(bytes, &extraProperties); err != nil { + return nil, err + } + for i := 0; i < val.Type().NumField(); i++ { + key := jsonKey(val.Type().Field(i)) + if key == "" || key == "-" { + continue + } + delete(extraProperties, key) + } + for _, key := range exclude { + delete(extraProperties, key) + } + if len(extraProperties) == 0 { + return nil, nil + } + return extraProperties, nil +} + +// getKeys returns the keys associated with the given value. The value must be a +// a struct or a map with string keys. +func getKeys(value interface{}) ([]string, error) { + val := reflect.ValueOf(value) + if val.Kind() == reflect.Ptr { + val = val.Elem() + } + if !val.IsValid() { + return nil, nil + } + switch val.Kind() { + case reflect.Struct: + return getKeysForStructType(val.Type()), nil + case reflect.Map: + var keys []string + if val.Type().Key().Kind() != reflect.String { + return nil, fmt.Errorf("cannot extract keys from %T; only structs and maps with string keys are supported", value) + } + for _, key := range val.MapKeys() { + keys = append(keys, key.String()) + } + return keys, nil + default: + return nil, fmt.Errorf("cannot extract keys from %T; only structs and maps with string keys are supported", value) + } +} + +// getKeysForStructType returns all the keys associated with the given struct type, +// visiting embedded fields recursively. +func getKeysForStructType(structType reflect.Type) []string { + if structType.Kind() == reflect.Pointer { + structType = structType.Elem() + } + if structType.Kind() != reflect.Struct { + return nil + } + var keys []string + for i := 0; i < structType.NumField(); i++ { + field := structType.Field(i) + if field.Anonymous { + keys = append(keys, getKeysForStructType(field.Type)...) + continue + } + keys = append(keys, jsonKey(field)) + } + return keys +} + +// jsonKey returns the JSON key from the struct tag of the given field, +// excluding the omitempty flag (if any). +func jsonKey(field reflect.StructField) string { + return strings.TrimSuffix(field.Tag.Get("json"), ",omitempty") +} + +// isEmptyJSON returns true if the given data is empty, the empty JSON object, or +// an explicit null. +func isEmptyJSON(data []byte) bool { + return len(data) <= 2 || bytes.Equal(data, []byte("null")) +} diff --git a/seed/go-fiber/nullable-allof-extends/internal/extra_properties_test.go b/seed/go-fiber/nullable-allof-extends/internal/extra_properties_test.go new file mode 100644 index 000000000000..aa2510ee5121 --- /dev/null +++ b/seed/go-fiber/nullable-allof-extends/internal/extra_properties_test.go @@ -0,0 +1,228 @@ +package internal + +import ( + "encoding/json" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type testMarshaler struct { + Name string `json:"name"` + BirthDate time.Time `json:"birthDate"` + CreatedAt time.Time `json:"created_at"` +} + +func (t *testMarshaler) MarshalJSON() ([]byte, error) { + type embed testMarshaler + var marshaler = struct { + embed + BirthDate string `json:"birthDate"` + CreatedAt string `json:"created_at"` + }{ + embed: embed(*t), + BirthDate: t.BirthDate.Format("2006-01-02"), + CreatedAt: t.CreatedAt.Format(time.RFC3339), + } + return MarshalJSONWithExtraProperty(marshaler, "type", "test") +} + +func TestMarshalJSONWithExtraProperties(t *testing.T) { + tests := []struct { + desc string + giveMarshaler interface{} + giveExtraProperties map[string]interface{} + wantBytes []byte + wantError string + }{ + { + desc: "invalid type", + giveMarshaler: []string{"invalid"}, + giveExtraProperties: map[string]interface{}{"key": "overwrite"}, + wantError: `cannot extract keys from []string; only structs and maps with string keys are supported`, + }, + { + desc: "invalid key type", + giveMarshaler: map[int]interface{}{42: "value"}, + giveExtraProperties: map[string]interface{}{"key": "overwrite"}, + wantError: `cannot extract keys from map[int]interface {}; only structs and maps with string keys are supported`, + }, + { + desc: "invalid map overwrite", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{"key": "overwrite"}, + wantError: `cannot add extra property "key" because it is already defined on the type`, + }, + { + desc: "invalid struct overwrite", + giveMarshaler: new(testMarshaler), + giveExtraProperties: map[string]interface{}{"birthDate": "2000-01-01"}, + wantError: `cannot add extra property "birthDate" because it is already defined on the type`, + }, + { + desc: "invalid struct overwrite embedded type", + giveMarshaler: new(testMarshaler), + giveExtraProperties: map[string]interface{}{"name": "bob"}, + wantError: `cannot add extra property "name" because it is already defined on the type`, + }, + { + desc: "nil", + giveMarshaler: nil, + giveExtraProperties: nil, + wantBytes: []byte(`null`), + }, + { + desc: "empty", + giveMarshaler: map[string]interface{}{}, + giveExtraProperties: map[string]interface{}{}, + wantBytes: []byte(`{}`), + }, + { + desc: "no extra properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "only extra properties", + giveMarshaler: map[string]interface{}{}, + giveExtraProperties: map[string]interface{}{"key": "value"}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "single extra property", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{"extra": "property"}, + wantBytes: []byte(`{"key":"value","extra":"property"}`), + }, + { + desc: "multiple extra properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{"one": 1, "two": 2}, + wantBytes: []byte(`{"key":"value","one":1,"two":2}`), + }, + { + desc: "nested properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{ + "user": map[string]interface{}{ + "age": 42, + "name": "alice", + }, + }, + wantBytes: []byte(`{"key":"value","user":{"age":42,"name":"alice"}}`), + }, + { + desc: "multiple nested properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{ + "metadata": map[string]interface{}{ + "ip": "127.0.0.1", + }, + "user": map[string]interface{}{ + "age": 42, + "name": "alice", + }, + }, + wantBytes: []byte(`{"key":"value","metadata":{"ip":"127.0.0.1"},"user":{"age":42,"name":"alice"}}`), + }, + { + desc: "custom marshaler", + giveMarshaler: &testMarshaler{ + Name: "alice", + BirthDate: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), + CreatedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC), + }, + giveExtraProperties: map[string]interface{}{ + "extra": "property", + }, + wantBytes: []byte(`{"name":"alice","birthDate":"2000-01-01","created_at":"2024-01-01T00:00:00Z","type":"test","extra":"property"}`), + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + bytes, err := MarshalJSONWithExtraProperties(tt.giveMarshaler, tt.giveExtraProperties) + if tt.wantError != "" { + require.EqualError(t, err, tt.wantError) + assert.Nil(t, tt.wantBytes) + return + } + require.NoError(t, err) + assert.Equal(t, tt.wantBytes, bytes) + + value := make(map[string]interface{}) + require.NoError(t, json.Unmarshal(bytes, &value)) + }) + } +} + +func TestExtractExtraProperties(t *testing.T) { + t.Run("none", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice"}`), value) + require.NoError(t, err) + assert.Nil(t, extraProperties) + }) + + t.Run("non-nil pointer", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]interface{}{"age": float64(42)}, extraProperties) + }) + + t.Run("nil pointer", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + var value *user + _, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + assert.EqualError(t, err, "value must be non-nil to extract extra properties") + }) + + t.Run("non-zero value", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]interface{}{"age": float64(42)}, extraProperties) + }) + + t.Run("zero value", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + var value user + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]interface{}{"age": float64(42)}, extraProperties) + }) + + t.Run("exclude", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value, "age") + require.NoError(t, err) + assert.Nil(t, extraProperties) + }) +} diff --git a/seed/go-fiber/nullable-allof-extends/internal/stringer.go b/seed/go-fiber/nullable-allof-extends/internal/stringer.go new file mode 100644 index 000000000000..312801851e0e --- /dev/null +++ b/seed/go-fiber/nullable-allof-extends/internal/stringer.go @@ -0,0 +1,13 @@ +package internal + +import "encoding/json" + +// StringifyJSON returns a pretty JSON string representation of +// the given value. +func StringifyJSON(value interface{}) (string, error) { + bytes, err := json.MarshalIndent(value, "", " ") + if err != nil { + return "", err + } + return string(bytes), nil +} diff --git a/seed/go-fiber/nullable-allof-extends/internal/time.go b/seed/go-fiber/nullable-allof-extends/internal/time.go new file mode 100644 index 000000000000..ab0e269fade3 --- /dev/null +++ b/seed/go-fiber/nullable-allof-extends/internal/time.go @@ -0,0 +1,137 @@ +package internal + +import ( + "encoding/json" + "time" +) + +const dateFormat = "2006-01-02" + +// DateTime wraps time.Time and adapts its JSON representation +// to conform to a RFC3339 date (e.g. 2006-01-02). +// +// Ref: https://ijmacd.github.io/rfc3339-iso8601 +type Date struct { + t *time.Time +} + +// NewDate returns a new *Date. If the given time.Time +// is nil, nil will be returned. +func NewDate(t time.Time) *Date { + return &Date{t: &t} +} + +// NewOptionalDate returns a new *Date. If the given time.Time +// is nil, nil will be returned. +func NewOptionalDate(t *time.Time) *Date { + if t == nil { + return nil + } + return &Date{t: t} +} + +// Time returns the Date's underlying time, if any. If the +// date is nil, the zero value is returned. +func (d *Date) Time() time.Time { + if d == nil || d.t == nil { + return time.Time{} + } + return *d.t +} + +// TimePtr returns a pointer to the Date's underlying time.Time, if any. +func (d *Date) TimePtr() *time.Time { + if d == nil || d.t == nil { + return nil + } + if d.t.IsZero() { + return nil + } + return d.t +} + +func (d *Date) MarshalJSON() ([]byte, error) { + if d == nil || d.t == nil { + return nil, nil + } + return json.Marshal(d.t.Format(dateFormat)) +} + +func (d *Date) UnmarshalJSON(data []byte) error { + var raw string + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + + parsedTime, err := time.Parse(dateFormat, raw) + if err != nil { + return err + } + + *d = Date{t: &parsedTime} + return nil +} + +// DateTime wraps time.Time and adapts its JSON representation +// to conform to a RFC3339 date-time (e.g. 2017-07-21T17:32:28Z). +// +// Ref: https://ijmacd.github.io/rfc3339-iso8601 +type DateTime struct { + t *time.Time +} + +// NewDateTime returns a new *DateTime. +func NewDateTime(t time.Time) *DateTime { + return &DateTime{t: &t} +} + +// NewOptionalDateTime returns a new *DateTime. If the given time.Time +// is nil, nil will be returned. +func NewOptionalDateTime(t *time.Time) *DateTime { + if t == nil { + return nil + } + return &DateTime{t: t} +} + +// Time returns the DateTime's underlying time, if any. If the +// date-time is nil, the zero value is returned. +func (d *DateTime) Time() time.Time { + if d == nil || d.t == nil { + return time.Time{} + } + return *d.t +} + +// TimePtr returns a pointer to the DateTime's underlying time.Time, if any. +func (d *DateTime) TimePtr() *time.Time { + if d == nil || d.t == nil { + return nil + } + if d.t.IsZero() { + return nil + } + return d.t +} + +func (d *DateTime) MarshalJSON() ([]byte, error) { + if d == nil || d.t == nil { + return nil, nil + } + return json.Marshal(d.t.Format(time.RFC3339)) +} + +func (d *DateTime) UnmarshalJSON(data []byte) error { + var raw string + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + + parsedTime, err := time.Parse(time.RFC3339, raw) + if err != nil { + return err + } + + *d = DateTime{t: &parsedTime} + return nil +} diff --git a/seed/go-fiber/nullable-allof-extends/snippet.json b/seed/go-fiber/nullable-allof-extends/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/go-fiber/nullable-allof-extends/types.go b/seed/go-fiber/nullable-allof-extends/types.go new file mode 100644 index 000000000000..25df40152cdd --- /dev/null +++ b/seed/go-fiber/nullable-allof-extends/types.go @@ -0,0 +1,242 @@ +// Code generated by Fern. DO NOT EDIT. + +package api + +import ( + json "encoding/json" + fmt "fmt" + internal "github.com/nullable-allof-extends/fern/internal" + big "math/big" +) + +// A standard object with no nullable issues. +var ( + normalObjectFieldNormalField = big.NewInt(1 << 0) +) + +type NormalObject struct { + NormalField *string `json:"normalField,omitempty" url:"normalField,omitempty"` + + // Private bitmask of fields set to an explicit value and therefore not to be omitted + explicitFields *big.Int `json:"-" url:"-"` + + extraProperties map[string]interface{} +} + +func (n *NormalObject) GetNormalField() *string { + if n == nil { + return nil + } + return n.NormalField +} + +func (n *NormalObject) GetExtraProperties() map[string]interface{} { + return n.extraProperties +} + +func (n *NormalObject) require(field *big.Int) { + if n.explicitFields == nil { + n.explicitFields = big.NewInt(0) + } + n.explicitFields.Or(n.explicitFields, field) +} + +// SetNormalField sets the NormalField field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (n *NormalObject) SetNormalField(normalField *string) { + n.NormalField = normalField + n.require(normalObjectFieldNormalField) +} + +func (n *NormalObject) UnmarshalJSON(data []byte) error { + type unmarshaler NormalObject + var value unmarshaler + if err := json.Unmarshal(data, &value); err != nil { + return err + } + *n = NormalObject(value) + extraProperties, err := internal.ExtractExtraProperties(data, *n) + if err != nil { + return err + } + n.extraProperties = extraProperties + return nil +} + +func (n *NormalObject) MarshalJSON() ([]byte, error) { + type embed NormalObject + var marshaler = struct { + embed + }{ + embed: embed(*n), + } + explicitMarshaler := internal.HandleExplicitFields(marshaler, n.explicitFields) + return json.Marshal(explicitMarshaler) +} + +func (n *NormalObject) String() string { + if value, err := internal.StringifyJSON(n); err == nil { + return value + } + return fmt.Sprintf("%#v", n) +} + +// This schema has nullable:true at the top level. +var ( + nullableObjectFieldNullableField = big.NewInt(1 << 0) +) + +type NullableObject struct { + NullableField *string `json:"nullableField,omitempty" url:"nullableField,omitempty"` + + // Private bitmask of fields set to an explicit value and therefore not to be omitted + explicitFields *big.Int `json:"-" url:"-"` + + extraProperties map[string]interface{} +} + +func (n *NullableObject) GetNullableField() *string { + if n == nil { + return nil + } + return n.NullableField +} + +func (n *NullableObject) GetExtraProperties() map[string]interface{} { + return n.extraProperties +} + +func (n *NullableObject) require(field *big.Int) { + if n.explicitFields == nil { + n.explicitFields = big.NewInt(0) + } + n.explicitFields.Or(n.explicitFields, field) +} + +// SetNullableField sets the NullableField field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (n *NullableObject) SetNullableField(nullableField *string) { + n.NullableField = nullableField + n.require(nullableObjectFieldNullableField) +} + +func (n *NullableObject) UnmarshalJSON(data []byte) error { + type unmarshaler NullableObject + var value unmarshaler + if err := json.Unmarshal(data, &value); err != nil { + return err + } + *n = NullableObject(value) + extraProperties, err := internal.ExtractExtraProperties(data, *n) + if err != nil { + return err + } + n.extraProperties = extraProperties + return nil +} + +func (n *NullableObject) MarshalJSON() ([]byte, error) { + type embed NullableObject + var marshaler = struct { + embed + }{ + embed: embed(*n), + } + explicitMarshaler := internal.HandleExplicitFields(marshaler, n.explicitFields) + return json.Marshal(explicitMarshaler) +} + +func (n *NullableObject) String() string { + if value, err := internal.StringifyJSON(n); err == nil { + return value + } + return fmt.Sprintf("%#v", n) +} + +// Object inheriting from a nullable schema via allOf. +var ( + rootObjectFieldNormalField = big.NewInt(1 << 0) + rootObjectFieldNullableField = big.NewInt(1 << 1) +) + +type RootObject struct { + NormalField *string `json:"normalField,omitempty" url:"normalField,omitempty"` + NullableField *string `json:"nullableField,omitempty" url:"nullableField,omitempty"` + + // Private bitmask of fields set to an explicit value and therefore not to be omitted + explicitFields *big.Int `json:"-" url:"-"` + + extraProperties map[string]interface{} +} + +func (r *RootObject) GetNormalField() *string { + if r == nil { + return nil + } + return r.NormalField +} + +func (r *RootObject) GetNullableField() *string { + if r == nil { + return nil + } + return r.NullableField +} + +func (r *RootObject) GetExtraProperties() map[string]interface{} { + return r.extraProperties +} + +func (r *RootObject) require(field *big.Int) { + if r.explicitFields == nil { + r.explicitFields = big.NewInt(0) + } + r.explicitFields.Or(r.explicitFields, field) +} + +// SetNormalField sets the NormalField field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (r *RootObject) SetNormalField(normalField *string) { + r.NormalField = normalField + r.require(rootObjectFieldNormalField) +} + +// SetNullableField sets the NullableField field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (r *RootObject) SetNullableField(nullableField *string) { + r.NullableField = nullableField + r.require(rootObjectFieldNullableField) +} + +func (r *RootObject) UnmarshalJSON(data []byte) error { + type unmarshaler RootObject + var value unmarshaler + if err := json.Unmarshal(data, &value); err != nil { + return err + } + *r = RootObject(value) + extraProperties, err := internal.ExtractExtraProperties(data, *r) + if err != nil { + return err + } + r.extraProperties = extraProperties + return nil +} + +func (r *RootObject) MarshalJSON() ([]byte, error) { + type embed RootObject + var marshaler = struct { + embed + }{ + embed: embed(*r), + } + explicitMarshaler := internal.HandleExplicitFields(marshaler, r.explicitFields) + return json.Marshal(explicitMarshaler) +} + +func (r *RootObject) String() string { + if value, err := internal.StringifyJSON(r); err == nil { + return value + } + return fmt.Sprintf("%#v", r) +} diff --git a/seed/go-fiber/oauth-client-credentials-mandatory-auth/.github/workflows/ci.yml b/seed/go-fiber/oauth-client-credentials-mandatory-auth/.github/workflows/ci.yml new file mode 100644 index 000000000000..56310d69624b --- /dev/null +++ b/seed/go-fiber/oauth-client-credentials-mandatory-auth/.github/workflows/ci.yml @@ -0,0 +1,35 @@ +name: ci + +on: [push] + +jobs: + compile: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up go + uses: actions/setup-go@v4 + + - name: Compile + run: go build ./... + test: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up go + uses: actions/setup-go@v4 + + - name: Setup wiremock server + run: | + if [ -f wiremock/docker-compose.test.yml ]; then docker compose -f wiremock/docker-compose.test.yml down && docker compose -f wiremock/docker-compose.test.yml up -d; fi + + - name: Test + run: go test ./... + + - name: Teardown wiremock server + run: | + if [ -f wiremock/docker-compose.test.yml ]; then docker compose -f wiremock/docker-compose.test.yml down; fi diff --git a/seed/go-fiber/oauth-client-credentials-mandatory-auth/auth.go b/seed/go-fiber/oauth-client-credentials-mandatory-auth/auth.go new file mode 100644 index 000000000000..dc36b7059a04 --- /dev/null +++ b/seed/go-fiber/oauth-client-credentials-mandatory-auth/auth.go @@ -0,0 +1,199 @@ +// Code generated by Fern. DO NOT EDIT. + +package oauthclientcredentialsmandatoryauth + +import ( + json "encoding/json" + fmt "fmt" + internal "github.com/oauth-client-credentials-mandatory-auth/fern/internal" + big "math/big" +) + +type GetTokenRequest struct { + ClientId string `json:"client_id" url:"-"` + ClientSecret string `json:"client_secret" url:"-"` + Scope *string `json:"scope,omitempty" url:"-"` + audience string + grantType string +} + +func (g *GetTokenRequest) Audience() string { + return g.audience +} + +func (g *GetTokenRequest) GrantType() string { + return g.grantType +} + +func (g *GetTokenRequest) UnmarshalJSON(data []byte) error { + type unmarshaler GetTokenRequest + var body unmarshaler + if err := json.Unmarshal(data, &body); err != nil { + return err + } + *g = GetTokenRequest(body) + g.audience = "https://api.example.com" + g.grantType = "client_credentials" + return nil +} + +func (g *GetTokenRequest) MarshalJSON() ([]byte, error) { + type embed GetTokenRequest + var marshaler = struct { + embed + Audience string `json:"audience"` + GrantType string `json:"grant_type"` + }{ + embed: embed(*g), + Audience: "https://api.example.com", + GrantType: "client_credentials", + } + return json.Marshal(marshaler) +} + +type RefreshTokenRequest struct { + ClientId string `json:"client_id" url:"-"` + ClientSecret string `json:"client_secret" url:"-"` + RefreshToken string `json:"refresh_token" url:"-"` + Scope *string `json:"scope,omitempty" url:"-"` + audience string + grantType string +} + +func (r *RefreshTokenRequest) Audience() string { + return r.audience +} + +func (r *RefreshTokenRequest) GrantType() string { + return r.grantType +} + +func (r *RefreshTokenRequest) UnmarshalJSON(data []byte) error { + type unmarshaler RefreshTokenRequest + var body unmarshaler + if err := json.Unmarshal(data, &body); err != nil { + return err + } + *r = RefreshTokenRequest(body) + r.audience = "https://api.example.com" + r.grantType = "refresh_token" + return nil +} + +func (r *RefreshTokenRequest) MarshalJSON() ([]byte, error) { + type embed RefreshTokenRequest + var marshaler = struct { + embed + Audience string `json:"audience"` + GrantType string `json:"grant_type"` + }{ + embed: embed(*r), + Audience: "https://api.example.com", + GrantType: "refresh_token", + } + return json.Marshal(marshaler) +} + +// An OAuth token response. +var ( + tokenResponseFieldAccessToken = big.NewInt(1 << 0) + tokenResponseFieldExpiresIn = big.NewInt(1 << 1) + tokenResponseFieldRefreshToken = big.NewInt(1 << 2) +) + +type TokenResponse struct { + AccessToken string `json:"access_token" url:"access_token"` + ExpiresIn int `json:"expires_in" url:"expires_in"` + RefreshToken *string `json:"refresh_token,omitempty" url:"refresh_token,omitempty"` + + // Private bitmask of fields set to an explicit value and therefore not to be omitted + explicitFields *big.Int `json:"-" url:"-"` + + extraProperties map[string]interface{} +} + +func (t *TokenResponse) GetAccessToken() string { + if t == nil { + return "" + } + return t.AccessToken +} + +func (t *TokenResponse) GetExpiresIn() int { + if t == nil { + return 0 + } + return t.ExpiresIn +} + +func (t *TokenResponse) GetRefreshToken() *string { + if t == nil { + return nil + } + return t.RefreshToken +} + +func (t *TokenResponse) GetExtraProperties() map[string]interface{} { + return t.extraProperties +} + +func (t *TokenResponse) require(field *big.Int) { + if t.explicitFields == nil { + t.explicitFields = big.NewInt(0) + } + t.explicitFields.Or(t.explicitFields, field) +} + +// SetAccessToken sets the AccessToken field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (t *TokenResponse) SetAccessToken(accessToken string) { + t.AccessToken = accessToken + t.require(tokenResponseFieldAccessToken) +} + +// SetExpiresIn sets the ExpiresIn field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (t *TokenResponse) SetExpiresIn(expiresIn int) { + t.ExpiresIn = expiresIn + t.require(tokenResponseFieldExpiresIn) +} + +// SetRefreshToken sets the RefreshToken field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (t *TokenResponse) SetRefreshToken(refreshToken *string) { + t.RefreshToken = refreshToken + t.require(tokenResponseFieldRefreshToken) +} + +func (t *TokenResponse) UnmarshalJSON(data []byte) error { + type unmarshaler TokenResponse + var value unmarshaler + if err := json.Unmarshal(data, &value); err != nil { + return err + } + *t = TokenResponse(value) + extraProperties, err := internal.ExtractExtraProperties(data, *t) + if err != nil { + return err + } + t.extraProperties = extraProperties + return nil +} + +func (t *TokenResponse) MarshalJSON() ([]byte, error) { + type embed TokenResponse + var marshaler = struct { + embed + }{ + embed: embed(*t), + } + explicitMarshaler := internal.HandleExplicitFields(marshaler, t.explicitFields) + return json.Marshal(explicitMarshaler) +} + +func (t *TokenResponse) String() string { + if value, err := internal.StringifyJSON(t); err == nil { + return value + } + return fmt.Sprintf("%#v", t) +} diff --git a/seed/go-fiber/oauth-client-credentials-mandatory-auth/go.mod b/seed/go-fiber/oauth-client-credentials-mandatory-auth/go.mod new file mode 100644 index 000000000000..27f18cf79244 --- /dev/null +++ b/seed/go-fiber/oauth-client-credentials-mandatory-auth/go.mod @@ -0,0 +1,11 @@ +module github.com/oauth-client-credentials-mandatory-auth/fern + +go 1.18 + +require github.com/stretchr/testify v1.7.0 + +require ( + github.com/davecgh/go-spew v1.1.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/seed/go-fiber/oauth-client-credentials-mandatory-auth/go.sum b/seed/go-fiber/oauth-client-credentials-mandatory-auth/go.sum new file mode 100644 index 000000000000..fc3dd9e67e82 --- /dev/null +++ b/seed/go-fiber/oauth-client-credentials-mandatory-auth/go.sum @@ -0,0 +1,12 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/seed/go-fiber/oauth-client-credentials-mandatory-auth/internal/explicit_fields.go b/seed/go-fiber/oauth-client-credentials-mandatory-auth/internal/explicit_fields.go new file mode 100644 index 000000000000..4bdf34fc2b7c --- /dev/null +++ b/seed/go-fiber/oauth-client-credentials-mandatory-auth/internal/explicit_fields.go @@ -0,0 +1,116 @@ +package internal + +import ( + "math/big" + "reflect" + "strings" +) + +// HandleExplicitFields processes a struct to remove `omitempty` from +// fields that have been explicitly set (as indicated by their corresponding bit in explicitFields). +// Note that `marshaler` should be an embedded struct to avoid infinite recursion. +// Returns an interface{} that can be passed to json.Marshal. +func HandleExplicitFields(marshaler interface{}, explicitFields *big.Int) interface{} { + val := reflect.ValueOf(marshaler) + typ := reflect.TypeOf(marshaler) + + // Handle pointer types + if val.Kind() == reflect.Ptr { + if val.IsNil() { + return nil + } + val = val.Elem() + typ = typ.Elem() + } + + // Only handle struct types + if val.Kind() != reflect.Struct { + return marshaler + } + + // Handle embedded struct pattern + var sourceVal reflect.Value + var sourceType reflect.Type + + // Check if this is an embedded struct pattern + if typ.NumField() == 1 && typ.Field(0).Anonymous { + // This is likely an embedded struct, get the embedded value + embeddedField := val.Field(0) + sourceVal = embeddedField + sourceType = embeddedField.Type() + } else { + // Regular struct + sourceVal = val + sourceType = typ + } + + // If no explicit fields set, use standard marshaling + if explicitFields == nil || explicitFields.Sign() == 0 { + return marshaler + } + + // Create a new struct type with modified tags + fields := make([]reflect.StructField, 0, sourceType.NumField()) + + for i := 0; i < sourceType.NumField(); i++ { + field := sourceType.Field(i) + + // Skip unexported fields and the explicitFields field itself + if !field.IsExported() || field.Name == "explicitFields" { + continue + } + + // Check if this field has been explicitly set + fieldBit := big.NewInt(1) + fieldBit.Lsh(fieldBit, uint(i)) + if big.NewInt(0).And(explicitFields, fieldBit).Sign() != 0 { + // Remove omitempty from the json tag + tag := field.Tag.Get("json") + if tag != "" && tag != "-" { + // Parse the json tag, remove omitempty from options + parts := strings.Split(tag, ",") + if len(parts) > 1 { + var newParts []string + newParts = append(newParts, parts[0]) // Keep the field name + for _, part := range parts[1:] { + if strings.TrimSpace(part) != "omitempty" { + newParts = append(newParts, part) + } + } + tag = strings.Join(newParts, ",") + } + + // Reconstruct the struct tag + newTag := `json:"` + tag + `"` + if urlTag := field.Tag.Get("url"); urlTag != "" { + newTag += ` url:"` + urlTag + `"` + } + + field.Tag = reflect.StructTag(newTag) + } + } + + fields = append(fields, field) + } + + // Create new struct type with modified tags + newType := reflect.StructOf(fields) + newVal := reflect.New(newType).Elem() + + // Copy field values from original struct to new struct + fieldIndex := 0 + for i := 0; i < sourceType.NumField(); i++ { + originalField := sourceType.Field(i) + + // Skip unexported fields and the explicitFields field itself + if !originalField.IsExported() || originalField.Name == "explicitFields" { + continue + } + + originalValue := sourceVal.Field(i) + newVal.Field(fieldIndex).Set(originalValue) + fieldIndex++ + } + + return newVal.Interface() +} diff --git a/seed/go-fiber/oauth-client-credentials-mandatory-auth/internal/explicit_fields_test.go b/seed/go-fiber/oauth-client-credentials-mandatory-auth/internal/explicit_fields_test.go new file mode 100644 index 000000000000..d3ec507de14b --- /dev/null +++ b/seed/go-fiber/oauth-client-credentials-mandatory-auth/internal/explicit_fields_test.go @@ -0,0 +1,497 @@ +package internal + +import ( + "encoding/json" + "math/big" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type testExplicitFieldsStruct struct { + Name *string `json:"name,omitempty"` + Code *string `json:"code,omitempty"` + Count *int `json:"count,omitempty"` + Enabled *bool `json:"enabled,omitempty"` + Tags []string `json:"tags,omitempty"` + //lint:ignore unused this field is intentionally unused for testing + unexported string `json:"-"` + explicitFields *big.Int `json:"-"` +} + +var ( + testFieldName = big.NewInt(1 << 0) + testFieldCode = big.NewInt(1 << 1) + testFieldCount = big.NewInt(1 << 2) + testFieldEnabled = big.NewInt(1 << 3) + testFieldTags = big.NewInt(1 << 4) +) + +func (t *testExplicitFieldsStruct) require(field *big.Int) { + if t.explicitFields == nil { + t.explicitFields = big.NewInt(0) + } + t.explicitFields.Or(t.explicitFields, field) +} + +func (t *testExplicitFieldsStruct) SetName(name *string) { + t.Name = name + t.require(testFieldName) +} + +func (t *testExplicitFieldsStruct) SetCode(code *string) { + t.Code = code + t.require(testFieldCode) +} + +func (t *testExplicitFieldsStruct) SetCount(count *int) { + t.Count = count + t.require(testFieldCount) +} + +func (t *testExplicitFieldsStruct) SetEnabled(enabled *bool) { + t.Enabled = enabled + t.require(testFieldEnabled) +} + +func (t *testExplicitFieldsStruct) SetTags(tags []string) { + t.Tags = tags + t.require(testFieldTags) +} + +func (t *testExplicitFieldsStruct) MarshalJSON() ([]byte, error) { + type embed testExplicitFieldsStruct + var marshaler = struct { + embed + }{ + embed: embed(*t), + } + return json.Marshal(HandleExplicitFields(marshaler, t.explicitFields)) +} + +type testStructWithoutExplicitFields struct { + Name *string `json:"name,omitempty"` + Code *string `json:"code,omitempty"` +} + +func TestHandleExplicitFields(t *testing.T) { + tests := []struct { + desc string + giveInput interface{} + wantBytes []byte + wantError string + }{ + { + desc: "nil input", + giveInput: nil, + wantBytes: []byte(`null`), + }, + { + desc: "non-struct input", + giveInput: "string", + wantBytes: []byte(`"string"`), + }, + { + desc: "slice input", + giveInput: []string{"a", "b"}, + wantBytes: []byte(`["a","b"]`), + }, + { + desc: "map input", + giveInput: map[string]interface{}{"key": "value"}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "struct without explicitFields field", + giveInput: &testStructWithoutExplicitFields{ + Name: stringPtr("test"), + Code: nil, + }, + wantBytes: []byte(`{"name":"test"}`), + }, + { + desc: "struct with no explicit fields set", + giveInput: &testExplicitFieldsStruct{ + Name: stringPtr("test"), + Code: nil, + }, + wantBytes: []byte(`{"name":"test"}`), + }, + { + desc: "struct with explicit nil field", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{ + Name: stringPtr("test"), + } + s.SetCode(nil) + return s + }(), + wantBytes: []byte(`{"name":"test","code":null}`), + }, + { + desc: "struct with explicit non-nil field", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{} + s.SetName(stringPtr("explicit")) + s.SetCode(stringPtr("also-explicit")) + return s + }(), + wantBytes: []byte(`{"name":"explicit","code":"also-explicit"}`), + }, + { + desc: "struct with mixed explicit and implicit fields", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{ + Name: stringPtr("implicit"), + Count: intPtr(42), + } + s.SetCode(nil) // explicit nil + return s + }(), + wantBytes: []byte(`{"name":"implicit","code":null,"count":42}`), + }, + { + desc: "struct with multiple explicit nil fields", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{ + Name: stringPtr("test"), + } + s.SetCode(nil) + s.SetCount(nil) + return s + }(), + wantBytes: []byte(`{"name":"test","code":null,"count":null}`), + }, + { + desc: "struct with slice field", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{ + Tags: []string{"tag1", "tag2"}, + } + s.SetTags(nil) // explicit nil slice + return s + }(), + wantBytes: []byte(`{"tags":null}`), + }, + { + desc: "struct with boolean field", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{} + s.SetEnabled(boolPtr(false)) // explicit false + return s + }(), + wantBytes: []byte(`{"enabled":false}`), + }, + { + desc: "struct with all fields explicit", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{} + s.SetName(stringPtr("test")) + s.SetCode(nil) + s.SetCount(intPtr(0)) + s.SetEnabled(boolPtr(false)) + s.SetTags([]string{}) + return s + }(), + wantBytes: []byte(`{"name":"test","code":null,"count":0,"enabled":false,"tags":[]}`), + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + var explicitFields *big.Int + if s, ok := tt.giveInput.(*testExplicitFieldsStruct); ok { + explicitFields = s.explicitFields + } + bytes, err := json.Marshal(HandleExplicitFields(tt.giveInput, explicitFields)) + if tt.wantError != "" { + require.EqualError(t, err, tt.wantError) + assert.Nil(t, tt.wantBytes) + return + } + require.NoError(t, err) + assert.JSONEq(t, string(tt.wantBytes), string(bytes)) + + // Verify it's valid JSON + var value interface{} + require.NoError(t, json.Unmarshal(bytes, &value)) + }) + } +} + +func TestHandleExplicitFieldsCustomMarshaler(t *testing.T) { + t.Run("custom marshaler with explicit fields", func(t *testing.T) { + s := &testExplicitFieldsStruct{} + s.SetName(nil) + s.SetCode(stringPtr("test-code")) + + bytes, err := s.MarshalJSON() + require.NoError(t, err) + assert.JSONEq(t, `{"name":null,"code":"test-code"}`, string(bytes)) + }) + + t.Run("custom marshaler with no explicit fields", func(t *testing.T) { + s := &testExplicitFieldsStruct{ + Name: stringPtr("implicit"), + Code: stringPtr("also-implicit"), + } + + bytes, err := s.MarshalJSON() + require.NoError(t, err) + assert.JSONEq(t, `{"name":"implicit","code":"also-implicit"}`, string(bytes)) + }) +} + +func TestHandleExplicitFieldsPointerHandling(t *testing.T) { + t.Run("nil pointer", func(t *testing.T) { + var s *testExplicitFieldsStruct + bytes, err := json.Marshal(HandleExplicitFields(s, nil)) + require.NoError(t, err) + assert.Equal(t, []byte(`null`), bytes) + }) + + t.Run("pointer to struct", func(t *testing.T) { + s := &testExplicitFieldsStruct{} + s.SetName(nil) + + bytes, err := json.Marshal(HandleExplicitFields(s, s.explicitFields)) + require.NoError(t, err) + assert.JSONEq(t, `{"name":null}`, string(bytes)) + }) +} + +func TestHandleExplicitFieldsEmbeddedStruct(t *testing.T) { + t.Run("embedded struct with explicit fields", func(t *testing.T) { + // Create a struct similar to what MarshalJSON creates + s := &testExplicitFieldsStruct{} + s.SetName(nil) + s.SetCode(stringPtr("test-code")) + + type embed testExplicitFieldsStruct + var marshaler = struct { + embed + }{ + embed: embed(*s), + } + + bytes, err := json.Marshal(HandleExplicitFields(marshaler, s.explicitFields)) + require.NoError(t, err) + // Should include both explicit fields (name as null, code as "test-code") + assert.JSONEq(t, `{"name":null,"code":"test-code"}`, string(bytes)) + }) + + t.Run("embedded struct with no explicit fields", func(t *testing.T) { + s := &testExplicitFieldsStruct{ + Name: stringPtr("implicit"), + Code: stringPtr("also-implicit"), + } + + type embed testExplicitFieldsStruct + var marshaler = struct { + embed + }{ + embed: embed(*s), + } + + bytes, err := json.Marshal(HandleExplicitFields(marshaler, s.explicitFields)) + require.NoError(t, err) + // Should only include non-nil fields (omitempty behavior) + assert.JSONEq(t, `{"name":"implicit","code":"also-implicit"}`, string(bytes)) + }) + + t.Run("embedded struct with mixed fields", func(t *testing.T) { + s := &testExplicitFieldsStruct{ + Count: intPtr(42), // implicit field + } + s.SetName(nil) // explicit nil + s.SetCode(stringPtr("explicit")) // explicit value + + type embed testExplicitFieldsStruct + var marshaler = struct { + embed + }{ + embed: embed(*s), + } + + bytes, err := json.Marshal(HandleExplicitFields(marshaler, s.explicitFields)) + require.NoError(t, err) + // Should include explicit null, explicit value, and implicit value + assert.JSONEq(t, `{"name":null,"code":"explicit","count":42}`, string(bytes)) + }) +} + +func TestHandleExplicitFieldsTagHandling(t *testing.T) { + type testStructWithComplexTags struct { + Field1 *string `json:"field1,omitempty" url:"field1,omitempty"` + Field2 *string `json:"field2,omitempty,string" url:"field2"` + Field3 *string `json:"-"` + Field4 *string `json:"field4"` + explicitFields *big.Int `json:"-"` + } + + s := &testStructWithComplexTags{ + Field1: stringPtr("test1"), + Field4: stringPtr("test4"), + explicitFields: big.NewInt(1), // Only first field is explicit + } + + bytes, err := json.Marshal(HandleExplicitFields(s, s.explicitFields)) + require.NoError(t, err) + + // Field1 should have omitempty removed, Field2 should keep omitempty, Field4 should be included + assert.JSONEq(t, `{"field1":"test1","field4":"test4"}`, string(bytes)) +} + +// Test types for nested struct explicit fields testing +type testNestedStruct struct { + NestedName *string `json:"nested_name,omitempty"` + NestedCode *string `json:"nested_code,omitempty"` + explicitFields *big.Int `json:"-"` +} + +type testParentStruct struct { + ParentName *string `json:"parent_name,omitempty"` + Nested *testNestedStruct `json:"nested,omitempty"` + explicitFields *big.Int `json:"-"` +} + +var ( + nestedFieldName = big.NewInt(1 << 0) + nestedFieldCode = big.NewInt(1 << 1) +) + +var ( + parentFieldName = big.NewInt(1 << 0) + parentFieldNested = big.NewInt(1 << 1) +) + +func (n *testNestedStruct) require(field *big.Int) { + if n.explicitFields == nil { + n.explicitFields = big.NewInt(0) + } + n.explicitFields.Or(n.explicitFields, field) +} + +func (n *testNestedStruct) SetNestedName(name *string) { + n.NestedName = name + n.require(nestedFieldName) +} + +func (n *testNestedStruct) SetNestedCode(code *string) { + n.NestedCode = code + n.require(nestedFieldCode) +} + +func (n *testNestedStruct) MarshalJSON() ([]byte, error) { + type embed testNestedStruct + var marshaler = struct { + embed + }{ + embed: embed(*n), + } + return json.Marshal(HandleExplicitFields(marshaler, n.explicitFields)) +} + +func (p *testParentStruct) require(field *big.Int) { + if p.explicitFields == nil { + p.explicitFields = big.NewInt(0) + } + p.explicitFields.Or(p.explicitFields, field) +} + +func (p *testParentStruct) SetParentName(name *string) { + p.ParentName = name + p.require(parentFieldName) +} + +func (p *testParentStruct) SetNested(nested *testNestedStruct) { + p.Nested = nested + p.require(parentFieldNested) +} + +func (p *testParentStruct) MarshalJSON() ([]byte, error) { + type embed testParentStruct + var marshaler = struct { + embed + }{ + embed: embed(*p), + } + return json.Marshal(HandleExplicitFields(marshaler, p.explicitFields)) +} + +func TestHandleExplicitFieldsNestedStruct(t *testing.T) { + tests := []struct { + desc string + setupFunc func() *testParentStruct + wantBytes []byte + }{ + { + desc: "nested struct with explicit nil in nested object", + setupFunc: func() *testParentStruct { + nested := &testNestedStruct{ + NestedName: stringPtr("implicit-nested"), + } + nested.SetNestedCode(nil) // explicit nil + + return &testParentStruct{ + ParentName: stringPtr("implicit-parent"), + Nested: nested, + } + }, + wantBytes: []byte(`{"parent_name":"implicit-parent","nested":{"nested_name":"implicit-nested","nested_code":null}}`), + }, + { + desc: "parent with explicit nil nested struct", + setupFunc: func() *testParentStruct { + parent := &testParentStruct{ + ParentName: stringPtr("implicit-parent"), + } + parent.SetNested(nil) // explicit nil nested struct + return parent + }, + wantBytes: []byte(`{"parent_name":"implicit-parent","nested":null}`), + }, + { + desc: "all explicit fields in nested structure", + setupFunc: func() *testParentStruct { + nested := &testNestedStruct{} + nested.SetNestedName(stringPtr("explicit-nested")) + nested.SetNestedCode(nil) // explicit nil + + parent := &testParentStruct{} + parent.SetParentName(nil) // explicit nil + parent.SetNested(nested) // explicit nested struct + + return parent + }, + wantBytes: []byte(`{"parent_name":null,"nested":{"nested_name":"explicit-nested","nested_code":null}}`), + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + parent := tt.setupFunc() + bytes, err := parent.MarshalJSON() + require.NoError(t, err) + assert.JSONEq(t, string(tt.wantBytes), string(bytes)) + + // Verify it's valid JSON + var value interface{} + require.NoError(t, json.Unmarshal(bytes, &value)) + }) + } +} + +// Helper functions +func stringPtr(s string) *string { + return &s +} + +func intPtr(i int) *int { + return &i +} + +func boolPtr(b bool) *bool { + return &b +} diff --git a/seed/go-fiber/oauth-client-credentials-mandatory-auth/internal/extra_properties.go b/seed/go-fiber/oauth-client-credentials-mandatory-auth/internal/extra_properties.go new file mode 100644 index 000000000000..540c3fd89eeb --- /dev/null +++ b/seed/go-fiber/oauth-client-credentials-mandatory-auth/internal/extra_properties.go @@ -0,0 +1,141 @@ +package internal + +import ( + "bytes" + "encoding/json" + "fmt" + "reflect" + "strings" +) + +// MarshalJSONWithExtraProperty marshals the given value to JSON, including the extra property. +func MarshalJSONWithExtraProperty(marshaler interface{}, key string, value interface{}) ([]byte, error) { + return MarshalJSONWithExtraProperties(marshaler, map[string]interface{}{key: value}) +} + +// MarshalJSONWithExtraProperties marshals the given value to JSON, including any extra properties. +func MarshalJSONWithExtraProperties(marshaler interface{}, extraProperties map[string]interface{}) ([]byte, error) { + bytes, err := json.Marshal(marshaler) + if err != nil { + return nil, err + } + if len(extraProperties) == 0 { + return bytes, nil + } + keys, err := getKeys(marshaler) + if err != nil { + return nil, err + } + for _, key := range keys { + if _, ok := extraProperties[key]; ok { + return nil, fmt.Errorf("cannot add extra property %q because it is already defined on the type", key) + } + } + extraBytes, err := json.Marshal(extraProperties) + if err != nil { + return nil, err + } + if isEmptyJSON(bytes) { + if isEmptyJSON(extraBytes) { + return bytes, nil + } + return extraBytes, nil + } + result := bytes[:len(bytes)-1] + result = append(result, ',') + result = append(result, extraBytes[1:len(extraBytes)-1]...) + result = append(result, '}') + return result, nil +} + +// ExtractExtraProperties extracts any extra properties from the given value. +func ExtractExtraProperties(bytes []byte, value interface{}, exclude ...string) (map[string]interface{}, error) { + val := reflect.ValueOf(value) + for val.Kind() == reflect.Ptr { + if val.IsNil() { + return nil, fmt.Errorf("value must be non-nil to extract extra properties") + } + val = val.Elem() + } + if err := json.Unmarshal(bytes, &value); err != nil { + return nil, err + } + var extraProperties map[string]interface{} + if err := json.Unmarshal(bytes, &extraProperties); err != nil { + return nil, err + } + for i := 0; i < val.Type().NumField(); i++ { + key := jsonKey(val.Type().Field(i)) + if key == "" || key == "-" { + continue + } + delete(extraProperties, key) + } + for _, key := range exclude { + delete(extraProperties, key) + } + if len(extraProperties) == 0 { + return nil, nil + } + return extraProperties, nil +} + +// getKeys returns the keys associated with the given value. The value must be a +// a struct or a map with string keys. +func getKeys(value interface{}) ([]string, error) { + val := reflect.ValueOf(value) + if val.Kind() == reflect.Ptr { + val = val.Elem() + } + if !val.IsValid() { + return nil, nil + } + switch val.Kind() { + case reflect.Struct: + return getKeysForStructType(val.Type()), nil + case reflect.Map: + var keys []string + if val.Type().Key().Kind() != reflect.String { + return nil, fmt.Errorf("cannot extract keys from %T; only structs and maps with string keys are supported", value) + } + for _, key := range val.MapKeys() { + keys = append(keys, key.String()) + } + return keys, nil + default: + return nil, fmt.Errorf("cannot extract keys from %T; only structs and maps with string keys are supported", value) + } +} + +// getKeysForStructType returns all the keys associated with the given struct type, +// visiting embedded fields recursively. +func getKeysForStructType(structType reflect.Type) []string { + if structType.Kind() == reflect.Pointer { + structType = structType.Elem() + } + if structType.Kind() != reflect.Struct { + return nil + } + var keys []string + for i := 0; i < structType.NumField(); i++ { + field := structType.Field(i) + if field.Anonymous { + keys = append(keys, getKeysForStructType(field.Type)...) + continue + } + keys = append(keys, jsonKey(field)) + } + return keys +} + +// jsonKey returns the JSON key from the struct tag of the given field, +// excluding the omitempty flag (if any). +func jsonKey(field reflect.StructField) string { + return strings.TrimSuffix(field.Tag.Get("json"), ",omitempty") +} + +// isEmptyJSON returns true if the given data is empty, the empty JSON object, or +// an explicit null. +func isEmptyJSON(data []byte) bool { + return len(data) <= 2 || bytes.Equal(data, []byte("null")) +} diff --git a/seed/go-fiber/oauth-client-credentials-mandatory-auth/internal/extra_properties_test.go b/seed/go-fiber/oauth-client-credentials-mandatory-auth/internal/extra_properties_test.go new file mode 100644 index 000000000000..aa2510ee5121 --- /dev/null +++ b/seed/go-fiber/oauth-client-credentials-mandatory-auth/internal/extra_properties_test.go @@ -0,0 +1,228 @@ +package internal + +import ( + "encoding/json" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type testMarshaler struct { + Name string `json:"name"` + BirthDate time.Time `json:"birthDate"` + CreatedAt time.Time `json:"created_at"` +} + +func (t *testMarshaler) MarshalJSON() ([]byte, error) { + type embed testMarshaler + var marshaler = struct { + embed + BirthDate string `json:"birthDate"` + CreatedAt string `json:"created_at"` + }{ + embed: embed(*t), + BirthDate: t.BirthDate.Format("2006-01-02"), + CreatedAt: t.CreatedAt.Format(time.RFC3339), + } + return MarshalJSONWithExtraProperty(marshaler, "type", "test") +} + +func TestMarshalJSONWithExtraProperties(t *testing.T) { + tests := []struct { + desc string + giveMarshaler interface{} + giveExtraProperties map[string]interface{} + wantBytes []byte + wantError string + }{ + { + desc: "invalid type", + giveMarshaler: []string{"invalid"}, + giveExtraProperties: map[string]interface{}{"key": "overwrite"}, + wantError: `cannot extract keys from []string; only structs and maps with string keys are supported`, + }, + { + desc: "invalid key type", + giveMarshaler: map[int]interface{}{42: "value"}, + giveExtraProperties: map[string]interface{}{"key": "overwrite"}, + wantError: `cannot extract keys from map[int]interface {}; only structs and maps with string keys are supported`, + }, + { + desc: "invalid map overwrite", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{"key": "overwrite"}, + wantError: `cannot add extra property "key" because it is already defined on the type`, + }, + { + desc: "invalid struct overwrite", + giveMarshaler: new(testMarshaler), + giveExtraProperties: map[string]interface{}{"birthDate": "2000-01-01"}, + wantError: `cannot add extra property "birthDate" because it is already defined on the type`, + }, + { + desc: "invalid struct overwrite embedded type", + giveMarshaler: new(testMarshaler), + giveExtraProperties: map[string]interface{}{"name": "bob"}, + wantError: `cannot add extra property "name" because it is already defined on the type`, + }, + { + desc: "nil", + giveMarshaler: nil, + giveExtraProperties: nil, + wantBytes: []byte(`null`), + }, + { + desc: "empty", + giveMarshaler: map[string]interface{}{}, + giveExtraProperties: map[string]interface{}{}, + wantBytes: []byte(`{}`), + }, + { + desc: "no extra properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "only extra properties", + giveMarshaler: map[string]interface{}{}, + giveExtraProperties: map[string]interface{}{"key": "value"}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "single extra property", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{"extra": "property"}, + wantBytes: []byte(`{"key":"value","extra":"property"}`), + }, + { + desc: "multiple extra properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{"one": 1, "two": 2}, + wantBytes: []byte(`{"key":"value","one":1,"two":2}`), + }, + { + desc: "nested properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{ + "user": map[string]interface{}{ + "age": 42, + "name": "alice", + }, + }, + wantBytes: []byte(`{"key":"value","user":{"age":42,"name":"alice"}}`), + }, + { + desc: "multiple nested properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{ + "metadata": map[string]interface{}{ + "ip": "127.0.0.1", + }, + "user": map[string]interface{}{ + "age": 42, + "name": "alice", + }, + }, + wantBytes: []byte(`{"key":"value","metadata":{"ip":"127.0.0.1"},"user":{"age":42,"name":"alice"}}`), + }, + { + desc: "custom marshaler", + giveMarshaler: &testMarshaler{ + Name: "alice", + BirthDate: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), + CreatedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC), + }, + giveExtraProperties: map[string]interface{}{ + "extra": "property", + }, + wantBytes: []byte(`{"name":"alice","birthDate":"2000-01-01","created_at":"2024-01-01T00:00:00Z","type":"test","extra":"property"}`), + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + bytes, err := MarshalJSONWithExtraProperties(tt.giveMarshaler, tt.giveExtraProperties) + if tt.wantError != "" { + require.EqualError(t, err, tt.wantError) + assert.Nil(t, tt.wantBytes) + return + } + require.NoError(t, err) + assert.Equal(t, tt.wantBytes, bytes) + + value := make(map[string]interface{}) + require.NoError(t, json.Unmarshal(bytes, &value)) + }) + } +} + +func TestExtractExtraProperties(t *testing.T) { + t.Run("none", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice"}`), value) + require.NoError(t, err) + assert.Nil(t, extraProperties) + }) + + t.Run("non-nil pointer", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]interface{}{"age": float64(42)}, extraProperties) + }) + + t.Run("nil pointer", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + var value *user + _, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + assert.EqualError(t, err, "value must be non-nil to extract extra properties") + }) + + t.Run("non-zero value", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]interface{}{"age": float64(42)}, extraProperties) + }) + + t.Run("zero value", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + var value user + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]interface{}{"age": float64(42)}, extraProperties) + }) + + t.Run("exclude", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value, "age") + require.NoError(t, err) + assert.Nil(t, extraProperties) + }) +} diff --git a/seed/go-fiber/oauth-client-credentials-mandatory-auth/internal/stringer.go b/seed/go-fiber/oauth-client-credentials-mandatory-auth/internal/stringer.go new file mode 100644 index 000000000000..312801851e0e --- /dev/null +++ b/seed/go-fiber/oauth-client-credentials-mandatory-auth/internal/stringer.go @@ -0,0 +1,13 @@ +package internal + +import "encoding/json" + +// StringifyJSON returns a pretty JSON string representation of +// the given value. +func StringifyJSON(value interface{}) (string, error) { + bytes, err := json.MarshalIndent(value, "", " ") + if err != nil { + return "", err + } + return string(bytes), nil +} diff --git a/seed/go-fiber/oauth-client-credentials-mandatory-auth/internal/time.go b/seed/go-fiber/oauth-client-credentials-mandatory-auth/internal/time.go new file mode 100644 index 000000000000..ab0e269fade3 --- /dev/null +++ b/seed/go-fiber/oauth-client-credentials-mandatory-auth/internal/time.go @@ -0,0 +1,137 @@ +package internal + +import ( + "encoding/json" + "time" +) + +const dateFormat = "2006-01-02" + +// DateTime wraps time.Time and adapts its JSON representation +// to conform to a RFC3339 date (e.g. 2006-01-02). +// +// Ref: https://ijmacd.github.io/rfc3339-iso8601 +type Date struct { + t *time.Time +} + +// NewDate returns a new *Date. If the given time.Time +// is nil, nil will be returned. +func NewDate(t time.Time) *Date { + return &Date{t: &t} +} + +// NewOptionalDate returns a new *Date. If the given time.Time +// is nil, nil will be returned. +func NewOptionalDate(t *time.Time) *Date { + if t == nil { + return nil + } + return &Date{t: t} +} + +// Time returns the Date's underlying time, if any. If the +// date is nil, the zero value is returned. +func (d *Date) Time() time.Time { + if d == nil || d.t == nil { + return time.Time{} + } + return *d.t +} + +// TimePtr returns a pointer to the Date's underlying time.Time, if any. +func (d *Date) TimePtr() *time.Time { + if d == nil || d.t == nil { + return nil + } + if d.t.IsZero() { + return nil + } + return d.t +} + +func (d *Date) MarshalJSON() ([]byte, error) { + if d == nil || d.t == nil { + return nil, nil + } + return json.Marshal(d.t.Format(dateFormat)) +} + +func (d *Date) UnmarshalJSON(data []byte) error { + var raw string + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + + parsedTime, err := time.Parse(dateFormat, raw) + if err != nil { + return err + } + + *d = Date{t: &parsedTime} + return nil +} + +// DateTime wraps time.Time and adapts its JSON representation +// to conform to a RFC3339 date-time (e.g. 2017-07-21T17:32:28Z). +// +// Ref: https://ijmacd.github.io/rfc3339-iso8601 +type DateTime struct { + t *time.Time +} + +// NewDateTime returns a new *DateTime. +func NewDateTime(t time.Time) *DateTime { + return &DateTime{t: &t} +} + +// NewOptionalDateTime returns a new *DateTime. If the given time.Time +// is nil, nil will be returned. +func NewOptionalDateTime(t *time.Time) *DateTime { + if t == nil { + return nil + } + return &DateTime{t: t} +} + +// Time returns the DateTime's underlying time, if any. If the +// date-time is nil, the zero value is returned. +func (d *DateTime) Time() time.Time { + if d == nil || d.t == nil { + return time.Time{} + } + return *d.t +} + +// TimePtr returns a pointer to the DateTime's underlying time.Time, if any. +func (d *DateTime) TimePtr() *time.Time { + if d == nil || d.t == nil { + return nil + } + if d.t.IsZero() { + return nil + } + return d.t +} + +func (d *DateTime) MarshalJSON() ([]byte, error) { + if d == nil || d.t == nil { + return nil, nil + } + return json.Marshal(d.t.Format(time.RFC3339)) +} + +func (d *DateTime) UnmarshalJSON(data []byte) error { + var raw string + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + + parsedTime, err := time.Parse(time.RFC3339, raw) + if err != nil { + return err + } + + *d = DateTime{t: &parsedTime} + return nil +} diff --git a/seed/go-fiber/oauth-client-credentials-mandatory-auth/snippet.json b/seed/go-fiber/oauth-client-credentials-mandatory-auth/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/go-fiber/pagination/users.go b/seed/go-fiber/pagination/users.go index 9f1ffbc44014..d5dae29a4982 100644 --- a/seed/go-fiber/pagination/users.go +++ b/seed/go-fiber/pagination/users.go @@ -103,6 +103,11 @@ type ListUsersOffsetStepPaginationRequest struct { Order *Order `query:"order"` } +type ListUsersOptionalDataRequest struct { + // Defaults to first page + Page *int `query:"page"` +} + var ( listUsersExtendedOptionalListResponseFieldData = big.NewInt(1 << 0) listUsersExtendedOptionalListResponseFieldNext = big.NewInt(1 << 1) @@ -398,6 +403,126 @@ func (l *ListUsersMixedTypePaginationResponse) String() string { return fmt.Sprintf("%#v", l) } +var ( + listUsersOptionalDataPaginationResponseFieldHasNextPage = big.NewInt(1 << 0) + listUsersOptionalDataPaginationResponseFieldPage = big.NewInt(1 << 1) + listUsersOptionalDataPaginationResponseFieldTotalCount = big.NewInt(1 << 2) + listUsersOptionalDataPaginationResponseFieldData = big.NewInt(1 << 3) +) + +type ListUsersOptionalDataPaginationResponse struct { + HasNextPage *bool `json:"hasNextPage,omitempty" url:"hasNextPage,omitempty"` + Page *Page `json:"page,omitempty" url:"page,omitempty"` + // The totall number of /users + TotalCount int `json:"total_count" url:"total_count"` + Data []*User `json:"data,omitempty" url:"data,omitempty"` + + // Private bitmask of fields set to an explicit value and therefore not to be omitted + explicitFields *big.Int `json:"-" url:"-"` + + extraProperties map[string]interface{} +} + +func (l *ListUsersOptionalDataPaginationResponse) GetHasNextPage() *bool { + if l == nil { + return nil + } + return l.HasNextPage +} + +func (l *ListUsersOptionalDataPaginationResponse) GetPage() *Page { + if l == nil { + return nil + } + return l.Page +} + +func (l *ListUsersOptionalDataPaginationResponse) GetTotalCount() int { + if l == nil { + return 0 + } + return l.TotalCount +} + +func (l *ListUsersOptionalDataPaginationResponse) GetData() []*User { + if l == nil { + return nil + } + return l.Data +} + +func (l *ListUsersOptionalDataPaginationResponse) GetExtraProperties() map[string]interface{} { + return l.extraProperties +} + +func (l *ListUsersOptionalDataPaginationResponse) require(field *big.Int) { + if l.explicitFields == nil { + l.explicitFields = big.NewInt(0) + } + l.explicitFields.Or(l.explicitFields, field) +} + +// SetHasNextPage sets the HasNextPage field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (l *ListUsersOptionalDataPaginationResponse) SetHasNextPage(hasNextPage *bool) { + l.HasNextPage = hasNextPage + l.require(listUsersOptionalDataPaginationResponseFieldHasNextPage) +} + +// SetPage sets the Page field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (l *ListUsersOptionalDataPaginationResponse) SetPage(page *Page) { + l.Page = page + l.require(listUsersOptionalDataPaginationResponseFieldPage) +} + +// SetTotalCount sets the TotalCount field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (l *ListUsersOptionalDataPaginationResponse) SetTotalCount(totalCount int) { + l.TotalCount = totalCount + l.require(listUsersOptionalDataPaginationResponseFieldTotalCount) +} + +// SetData sets the Data field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (l *ListUsersOptionalDataPaginationResponse) SetData(data []*User) { + l.Data = data + l.require(listUsersOptionalDataPaginationResponseFieldData) +} + +func (l *ListUsersOptionalDataPaginationResponse) UnmarshalJSON(data []byte) error { + type unmarshaler ListUsersOptionalDataPaginationResponse + var value unmarshaler + if err := json.Unmarshal(data, &value); err != nil { + return err + } + *l = ListUsersOptionalDataPaginationResponse(value) + extraProperties, err := internal.ExtractExtraProperties(data, *l) + if err != nil { + return err + } + l.extraProperties = extraProperties + return nil +} + +func (l *ListUsersOptionalDataPaginationResponse) MarshalJSON() ([]byte, error) { + type embed ListUsersOptionalDataPaginationResponse + var marshaler = struct { + embed + }{ + embed: embed(*l), + } + explicitMarshaler := internal.HandleExplicitFields(marshaler, l.explicitFields) + return json.Marshal(explicitMarshaler) +} + +func (l *ListUsersOptionalDataPaginationResponse) String() string { + if value, err := internal.StringifyJSON(l); err == nil { + return value + } + return fmt.Sprintf("%#v", l) +} + var ( listUsersPaginationResponseFieldHasNextPage = big.NewInt(1 << 0) listUsersPaginationResponseFieldPage = big.NewInt(1 << 1) diff --git a/seed/go-fiber/streaming/.github/workflows/ci.yml b/seed/go-fiber/streaming/.github/workflows/ci.yml new file mode 100644 index 000000000000..56310d69624b --- /dev/null +++ b/seed/go-fiber/streaming/.github/workflows/ci.yml @@ -0,0 +1,35 @@ +name: ci + +on: [push] + +jobs: + compile: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up go + uses: actions/setup-go@v4 + + - name: Compile + run: go build ./... + test: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up go + uses: actions/setup-go@v4 + + - name: Setup wiremock server + run: | + if [ -f wiremock/docker-compose.test.yml ]; then docker compose -f wiremock/docker-compose.test.yml down && docker compose -f wiremock/docker-compose.test.yml up -d; fi + + - name: Test + run: go test ./... + + - name: Teardown wiremock server + run: | + if [ -f wiremock/docker-compose.test.yml ]; then docker compose -f wiremock/docker-compose.test.yml down; fi diff --git a/seed/go-fiber/streaming/internal/explicit_fields.go b/seed/go-fiber/streaming/internal/explicit_fields.go new file mode 100644 index 000000000000..4bdf34fc2b7c --- /dev/null +++ b/seed/go-fiber/streaming/internal/explicit_fields.go @@ -0,0 +1,116 @@ +package internal + +import ( + "math/big" + "reflect" + "strings" +) + +// HandleExplicitFields processes a struct to remove `omitempty` from +// fields that have been explicitly set (as indicated by their corresponding bit in explicitFields). +// Note that `marshaler` should be an embedded struct to avoid infinite recursion. +// Returns an interface{} that can be passed to json.Marshal. +func HandleExplicitFields(marshaler interface{}, explicitFields *big.Int) interface{} { + val := reflect.ValueOf(marshaler) + typ := reflect.TypeOf(marshaler) + + // Handle pointer types + if val.Kind() == reflect.Ptr { + if val.IsNil() { + return nil + } + val = val.Elem() + typ = typ.Elem() + } + + // Only handle struct types + if val.Kind() != reflect.Struct { + return marshaler + } + + // Handle embedded struct pattern + var sourceVal reflect.Value + var sourceType reflect.Type + + // Check if this is an embedded struct pattern + if typ.NumField() == 1 && typ.Field(0).Anonymous { + // This is likely an embedded struct, get the embedded value + embeddedField := val.Field(0) + sourceVal = embeddedField + sourceType = embeddedField.Type() + } else { + // Regular struct + sourceVal = val + sourceType = typ + } + + // If no explicit fields set, use standard marshaling + if explicitFields == nil || explicitFields.Sign() == 0 { + return marshaler + } + + // Create a new struct type with modified tags + fields := make([]reflect.StructField, 0, sourceType.NumField()) + + for i := 0; i < sourceType.NumField(); i++ { + field := sourceType.Field(i) + + // Skip unexported fields and the explicitFields field itself + if !field.IsExported() || field.Name == "explicitFields" { + continue + } + + // Check if this field has been explicitly set + fieldBit := big.NewInt(1) + fieldBit.Lsh(fieldBit, uint(i)) + if big.NewInt(0).And(explicitFields, fieldBit).Sign() != 0 { + // Remove omitempty from the json tag + tag := field.Tag.Get("json") + if tag != "" && tag != "-" { + // Parse the json tag, remove omitempty from options + parts := strings.Split(tag, ",") + if len(parts) > 1 { + var newParts []string + newParts = append(newParts, parts[0]) // Keep the field name + for _, part := range parts[1:] { + if strings.TrimSpace(part) != "omitempty" { + newParts = append(newParts, part) + } + } + tag = strings.Join(newParts, ",") + } + + // Reconstruct the struct tag + newTag := `json:"` + tag + `"` + if urlTag := field.Tag.Get("url"); urlTag != "" { + newTag += ` url:"` + urlTag + `"` + } + + field.Tag = reflect.StructTag(newTag) + } + } + + fields = append(fields, field) + } + + // Create new struct type with modified tags + newType := reflect.StructOf(fields) + newVal := reflect.New(newType).Elem() + + // Copy field values from original struct to new struct + fieldIndex := 0 + for i := 0; i < sourceType.NumField(); i++ { + originalField := sourceType.Field(i) + + // Skip unexported fields and the explicitFields field itself + if !originalField.IsExported() || originalField.Name == "explicitFields" { + continue + } + + originalValue := sourceVal.Field(i) + newVal.Field(fieldIndex).Set(originalValue) + fieldIndex++ + } + + return newVal.Interface() +} diff --git a/seed/go-fiber/streaming/internal/explicit_fields_test.go b/seed/go-fiber/streaming/internal/explicit_fields_test.go new file mode 100644 index 000000000000..d3ec507de14b --- /dev/null +++ b/seed/go-fiber/streaming/internal/explicit_fields_test.go @@ -0,0 +1,497 @@ +package internal + +import ( + "encoding/json" + "math/big" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type testExplicitFieldsStruct struct { + Name *string `json:"name,omitempty"` + Code *string `json:"code,omitempty"` + Count *int `json:"count,omitempty"` + Enabled *bool `json:"enabled,omitempty"` + Tags []string `json:"tags,omitempty"` + //lint:ignore unused this field is intentionally unused for testing + unexported string `json:"-"` + explicitFields *big.Int `json:"-"` +} + +var ( + testFieldName = big.NewInt(1 << 0) + testFieldCode = big.NewInt(1 << 1) + testFieldCount = big.NewInt(1 << 2) + testFieldEnabled = big.NewInt(1 << 3) + testFieldTags = big.NewInt(1 << 4) +) + +func (t *testExplicitFieldsStruct) require(field *big.Int) { + if t.explicitFields == nil { + t.explicitFields = big.NewInt(0) + } + t.explicitFields.Or(t.explicitFields, field) +} + +func (t *testExplicitFieldsStruct) SetName(name *string) { + t.Name = name + t.require(testFieldName) +} + +func (t *testExplicitFieldsStruct) SetCode(code *string) { + t.Code = code + t.require(testFieldCode) +} + +func (t *testExplicitFieldsStruct) SetCount(count *int) { + t.Count = count + t.require(testFieldCount) +} + +func (t *testExplicitFieldsStruct) SetEnabled(enabled *bool) { + t.Enabled = enabled + t.require(testFieldEnabled) +} + +func (t *testExplicitFieldsStruct) SetTags(tags []string) { + t.Tags = tags + t.require(testFieldTags) +} + +func (t *testExplicitFieldsStruct) MarshalJSON() ([]byte, error) { + type embed testExplicitFieldsStruct + var marshaler = struct { + embed + }{ + embed: embed(*t), + } + return json.Marshal(HandleExplicitFields(marshaler, t.explicitFields)) +} + +type testStructWithoutExplicitFields struct { + Name *string `json:"name,omitempty"` + Code *string `json:"code,omitempty"` +} + +func TestHandleExplicitFields(t *testing.T) { + tests := []struct { + desc string + giveInput interface{} + wantBytes []byte + wantError string + }{ + { + desc: "nil input", + giveInput: nil, + wantBytes: []byte(`null`), + }, + { + desc: "non-struct input", + giveInput: "string", + wantBytes: []byte(`"string"`), + }, + { + desc: "slice input", + giveInput: []string{"a", "b"}, + wantBytes: []byte(`["a","b"]`), + }, + { + desc: "map input", + giveInput: map[string]interface{}{"key": "value"}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "struct without explicitFields field", + giveInput: &testStructWithoutExplicitFields{ + Name: stringPtr("test"), + Code: nil, + }, + wantBytes: []byte(`{"name":"test"}`), + }, + { + desc: "struct with no explicit fields set", + giveInput: &testExplicitFieldsStruct{ + Name: stringPtr("test"), + Code: nil, + }, + wantBytes: []byte(`{"name":"test"}`), + }, + { + desc: "struct with explicit nil field", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{ + Name: stringPtr("test"), + } + s.SetCode(nil) + return s + }(), + wantBytes: []byte(`{"name":"test","code":null}`), + }, + { + desc: "struct with explicit non-nil field", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{} + s.SetName(stringPtr("explicit")) + s.SetCode(stringPtr("also-explicit")) + return s + }(), + wantBytes: []byte(`{"name":"explicit","code":"also-explicit"}`), + }, + { + desc: "struct with mixed explicit and implicit fields", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{ + Name: stringPtr("implicit"), + Count: intPtr(42), + } + s.SetCode(nil) // explicit nil + return s + }(), + wantBytes: []byte(`{"name":"implicit","code":null,"count":42}`), + }, + { + desc: "struct with multiple explicit nil fields", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{ + Name: stringPtr("test"), + } + s.SetCode(nil) + s.SetCount(nil) + return s + }(), + wantBytes: []byte(`{"name":"test","code":null,"count":null}`), + }, + { + desc: "struct with slice field", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{ + Tags: []string{"tag1", "tag2"}, + } + s.SetTags(nil) // explicit nil slice + return s + }(), + wantBytes: []byte(`{"tags":null}`), + }, + { + desc: "struct with boolean field", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{} + s.SetEnabled(boolPtr(false)) // explicit false + return s + }(), + wantBytes: []byte(`{"enabled":false}`), + }, + { + desc: "struct with all fields explicit", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{} + s.SetName(stringPtr("test")) + s.SetCode(nil) + s.SetCount(intPtr(0)) + s.SetEnabled(boolPtr(false)) + s.SetTags([]string{}) + return s + }(), + wantBytes: []byte(`{"name":"test","code":null,"count":0,"enabled":false,"tags":[]}`), + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + var explicitFields *big.Int + if s, ok := tt.giveInput.(*testExplicitFieldsStruct); ok { + explicitFields = s.explicitFields + } + bytes, err := json.Marshal(HandleExplicitFields(tt.giveInput, explicitFields)) + if tt.wantError != "" { + require.EqualError(t, err, tt.wantError) + assert.Nil(t, tt.wantBytes) + return + } + require.NoError(t, err) + assert.JSONEq(t, string(tt.wantBytes), string(bytes)) + + // Verify it's valid JSON + var value interface{} + require.NoError(t, json.Unmarshal(bytes, &value)) + }) + } +} + +func TestHandleExplicitFieldsCustomMarshaler(t *testing.T) { + t.Run("custom marshaler with explicit fields", func(t *testing.T) { + s := &testExplicitFieldsStruct{} + s.SetName(nil) + s.SetCode(stringPtr("test-code")) + + bytes, err := s.MarshalJSON() + require.NoError(t, err) + assert.JSONEq(t, `{"name":null,"code":"test-code"}`, string(bytes)) + }) + + t.Run("custom marshaler with no explicit fields", func(t *testing.T) { + s := &testExplicitFieldsStruct{ + Name: stringPtr("implicit"), + Code: stringPtr("also-implicit"), + } + + bytes, err := s.MarshalJSON() + require.NoError(t, err) + assert.JSONEq(t, `{"name":"implicit","code":"also-implicit"}`, string(bytes)) + }) +} + +func TestHandleExplicitFieldsPointerHandling(t *testing.T) { + t.Run("nil pointer", func(t *testing.T) { + var s *testExplicitFieldsStruct + bytes, err := json.Marshal(HandleExplicitFields(s, nil)) + require.NoError(t, err) + assert.Equal(t, []byte(`null`), bytes) + }) + + t.Run("pointer to struct", func(t *testing.T) { + s := &testExplicitFieldsStruct{} + s.SetName(nil) + + bytes, err := json.Marshal(HandleExplicitFields(s, s.explicitFields)) + require.NoError(t, err) + assert.JSONEq(t, `{"name":null}`, string(bytes)) + }) +} + +func TestHandleExplicitFieldsEmbeddedStruct(t *testing.T) { + t.Run("embedded struct with explicit fields", func(t *testing.T) { + // Create a struct similar to what MarshalJSON creates + s := &testExplicitFieldsStruct{} + s.SetName(nil) + s.SetCode(stringPtr("test-code")) + + type embed testExplicitFieldsStruct + var marshaler = struct { + embed + }{ + embed: embed(*s), + } + + bytes, err := json.Marshal(HandleExplicitFields(marshaler, s.explicitFields)) + require.NoError(t, err) + // Should include both explicit fields (name as null, code as "test-code") + assert.JSONEq(t, `{"name":null,"code":"test-code"}`, string(bytes)) + }) + + t.Run("embedded struct with no explicit fields", func(t *testing.T) { + s := &testExplicitFieldsStruct{ + Name: stringPtr("implicit"), + Code: stringPtr("also-implicit"), + } + + type embed testExplicitFieldsStruct + var marshaler = struct { + embed + }{ + embed: embed(*s), + } + + bytes, err := json.Marshal(HandleExplicitFields(marshaler, s.explicitFields)) + require.NoError(t, err) + // Should only include non-nil fields (omitempty behavior) + assert.JSONEq(t, `{"name":"implicit","code":"also-implicit"}`, string(bytes)) + }) + + t.Run("embedded struct with mixed fields", func(t *testing.T) { + s := &testExplicitFieldsStruct{ + Count: intPtr(42), // implicit field + } + s.SetName(nil) // explicit nil + s.SetCode(stringPtr("explicit")) // explicit value + + type embed testExplicitFieldsStruct + var marshaler = struct { + embed + }{ + embed: embed(*s), + } + + bytes, err := json.Marshal(HandleExplicitFields(marshaler, s.explicitFields)) + require.NoError(t, err) + // Should include explicit null, explicit value, and implicit value + assert.JSONEq(t, `{"name":null,"code":"explicit","count":42}`, string(bytes)) + }) +} + +func TestHandleExplicitFieldsTagHandling(t *testing.T) { + type testStructWithComplexTags struct { + Field1 *string `json:"field1,omitempty" url:"field1,omitempty"` + Field2 *string `json:"field2,omitempty,string" url:"field2"` + Field3 *string `json:"-"` + Field4 *string `json:"field4"` + explicitFields *big.Int `json:"-"` + } + + s := &testStructWithComplexTags{ + Field1: stringPtr("test1"), + Field4: stringPtr("test4"), + explicitFields: big.NewInt(1), // Only first field is explicit + } + + bytes, err := json.Marshal(HandleExplicitFields(s, s.explicitFields)) + require.NoError(t, err) + + // Field1 should have omitempty removed, Field2 should keep omitempty, Field4 should be included + assert.JSONEq(t, `{"field1":"test1","field4":"test4"}`, string(bytes)) +} + +// Test types for nested struct explicit fields testing +type testNestedStruct struct { + NestedName *string `json:"nested_name,omitempty"` + NestedCode *string `json:"nested_code,omitempty"` + explicitFields *big.Int `json:"-"` +} + +type testParentStruct struct { + ParentName *string `json:"parent_name,omitempty"` + Nested *testNestedStruct `json:"nested,omitempty"` + explicitFields *big.Int `json:"-"` +} + +var ( + nestedFieldName = big.NewInt(1 << 0) + nestedFieldCode = big.NewInt(1 << 1) +) + +var ( + parentFieldName = big.NewInt(1 << 0) + parentFieldNested = big.NewInt(1 << 1) +) + +func (n *testNestedStruct) require(field *big.Int) { + if n.explicitFields == nil { + n.explicitFields = big.NewInt(0) + } + n.explicitFields.Or(n.explicitFields, field) +} + +func (n *testNestedStruct) SetNestedName(name *string) { + n.NestedName = name + n.require(nestedFieldName) +} + +func (n *testNestedStruct) SetNestedCode(code *string) { + n.NestedCode = code + n.require(nestedFieldCode) +} + +func (n *testNestedStruct) MarshalJSON() ([]byte, error) { + type embed testNestedStruct + var marshaler = struct { + embed + }{ + embed: embed(*n), + } + return json.Marshal(HandleExplicitFields(marshaler, n.explicitFields)) +} + +func (p *testParentStruct) require(field *big.Int) { + if p.explicitFields == nil { + p.explicitFields = big.NewInt(0) + } + p.explicitFields.Or(p.explicitFields, field) +} + +func (p *testParentStruct) SetParentName(name *string) { + p.ParentName = name + p.require(parentFieldName) +} + +func (p *testParentStruct) SetNested(nested *testNestedStruct) { + p.Nested = nested + p.require(parentFieldNested) +} + +func (p *testParentStruct) MarshalJSON() ([]byte, error) { + type embed testParentStruct + var marshaler = struct { + embed + }{ + embed: embed(*p), + } + return json.Marshal(HandleExplicitFields(marshaler, p.explicitFields)) +} + +func TestHandleExplicitFieldsNestedStruct(t *testing.T) { + tests := []struct { + desc string + setupFunc func() *testParentStruct + wantBytes []byte + }{ + { + desc: "nested struct with explicit nil in nested object", + setupFunc: func() *testParentStruct { + nested := &testNestedStruct{ + NestedName: stringPtr("implicit-nested"), + } + nested.SetNestedCode(nil) // explicit nil + + return &testParentStruct{ + ParentName: stringPtr("implicit-parent"), + Nested: nested, + } + }, + wantBytes: []byte(`{"parent_name":"implicit-parent","nested":{"nested_name":"implicit-nested","nested_code":null}}`), + }, + { + desc: "parent with explicit nil nested struct", + setupFunc: func() *testParentStruct { + parent := &testParentStruct{ + ParentName: stringPtr("implicit-parent"), + } + parent.SetNested(nil) // explicit nil nested struct + return parent + }, + wantBytes: []byte(`{"parent_name":"implicit-parent","nested":null}`), + }, + { + desc: "all explicit fields in nested structure", + setupFunc: func() *testParentStruct { + nested := &testNestedStruct{} + nested.SetNestedName(stringPtr("explicit-nested")) + nested.SetNestedCode(nil) // explicit nil + + parent := &testParentStruct{} + parent.SetParentName(nil) // explicit nil + parent.SetNested(nested) // explicit nested struct + + return parent + }, + wantBytes: []byte(`{"parent_name":null,"nested":{"nested_name":"explicit-nested","nested_code":null}}`), + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + parent := tt.setupFunc() + bytes, err := parent.MarshalJSON() + require.NoError(t, err) + assert.JSONEq(t, string(tt.wantBytes), string(bytes)) + + // Verify it's valid JSON + var value interface{} + require.NoError(t, json.Unmarshal(bytes, &value)) + }) + } +} + +// Helper functions +func stringPtr(s string) *string { + return &s +} + +func intPtr(i int) *int { + return &i +} + +func boolPtr(b bool) *bool { + return &b +} diff --git a/seed/go-fiber/streaming/internal/extra_properties.go b/seed/go-fiber/streaming/internal/extra_properties.go new file mode 100644 index 000000000000..540c3fd89eeb --- /dev/null +++ b/seed/go-fiber/streaming/internal/extra_properties.go @@ -0,0 +1,141 @@ +package internal + +import ( + "bytes" + "encoding/json" + "fmt" + "reflect" + "strings" +) + +// MarshalJSONWithExtraProperty marshals the given value to JSON, including the extra property. +func MarshalJSONWithExtraProperty(marshaler interface{}, key string, value interface{}) ([]byte, error) { + return MarshalJSONWithExtraProperties(marshaler, map[string]interface{}{key: value}) +} + +// MarshalJSONWithExtraProperties marshals the given value to JSON, including any extra properties. +func MarshalJSONWithExtraProperties(marshaler interface{}, extraProperties map[string]interface{}) ([]byte, error) { + bytes, err := json.Marshal(marshaler) + if err != nil { + return nil, err + } + if len(extraProperties) == 0 { + return bytes, nil + } + keys, err := getKeys(marshaler) + if err != nil { + return nil, err + } + for _, key := range keys { + if _, ok := extraProperties[key]; ok { + return nil, fmt.Errorf("cannot add extra property %q because it is already defined on the type", key) + } + } + extraBytes, err := json.Marshal(extraProperties) + if err != nil { + return nil, err + } + if isEmptyJSON(bytes) { + if isEmptyJSON(extraBytes) { + return bytes, nil + } + return extraBytes, nil + } + result := bytes[:len(bytes)-1] + result = append(result, ',') + result = append(result, extraBytes[1:len(extraBytes)-1]...) + result = append(result, '}') + return result, nil +} + +// ExtractExtraProperties extracts any extra properties from the given value. +func ExtractExtraProperties(bytes []byte, value interface{}, exclude ...string) (map[string]interface{}, error) { + val := reflect.ValueOf(value) + for val.Kind() == reflect.Ptr { + if val.IsNil() { + return nil, fmt.Errorf("value must be non-nil to extract extra properties") + } + val = val.Elem() + } + if err := json.Unmarshal(bytes, &value); err != nil { + return nil, err + } + var extraProperties map[string]interface{} + if err := json.Unmarshal(bytes, &extraProperties); err != nil { + return nil, err + } + for i := 0; i < val.Type().NumField(); i++ { + key := jsonKey(val.Type().Field(i)) + if key == "" || key == "-" { + continue + } + delete(extraProperties, key) + } + for _, key := range exclude { + delete(extraProperties, key) + } + if len(extraProperties) == 0 { + return nil, nil + } + return extraProperties, nil +} + +// getKeys returns the keys associated with the given value. The value must be a +// a struct or a map with string keys. +func getKeys(value interface{}) ([]string, error) { + val := reflect.ValueOf(value) + if val.Kind() == reflect.Ptr { + val = val.Elem() + } + if !val.IsValid() { + return nil, nil + } + switch val.Kind() { + case reflect.Struct: + return getKeysForStructType(val.Type()), nil + case reflect.Map: + var keys []string + if val.Type().Key().Kind() != reflect.String { + return nil, fmt.Errorf("cannot extract keys from %T; only structs and maps with string keys are supported", value) + } + for _, key := range val.MapKeys() { + keys = append(keys, key.String()) + } + return keys, nil + default: + return nil, fmt.Errorf("cannot extract keys from %T; only structs and maps with string keys are supported", value) + } +} + +// getKeysForStructType returns all the keys associated with the given struct type, +// visiting embedded fields recursively. +func getKeysForStructType(structType reflect.Type) []string { + if structType.Kind() == reflect.Pointer { + structType = structType.Elem() + } + if structType.Kind() != reflect.Struct { + return nil + } + var keys []string + for i := 0; i < structType.NumField(); i++ { + field := structType.Field(i) + if field.Anonymous { + keys = append(keys, getKeysForStructType(field.Type)...) + continue + } + keys = append(keys, jsonKey(field)) + } + return keys +} + +// jsonKey returns the JSON key from the struct tag of the given field, +// excluding the omitempty flag (if any). +func jsonKey(field reflect.StructField) string { + return strings.TrimSuffix(field.Tag.Get("json"), ",omitempty") +} + +// isEmptyJSON returns true if the given data is empty, the empty JSON object, or +// an explicit null. +func isEmptyJSON(data []byte) bool { + return len(data) <= 2 || bytes.Equal(data, []byte("null")) +} diff --git a/seed/go-fiber/streaming/internal/extra_properties_test.go b/seed/go-fiber/streaming/internal/extra_properties_test.go new file mode 100644 index 000000000000..aa2510ee5121 --- /dev/null +++ b/seed/go-fiber/streaming/internal/extra_properties_test.go @@ -0,0 +1,228 @@ +package internal + +import ( + "encoding/json" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type testMarshaler struct { + Name string `json:"name"` + BirthDate time.Time `json:"birthDate"` + CreatedAt time.Time `json:"created_at"` +} + +func (t *testMarshaler) MarshalJSON() ([]byte, error) { + type embed testMarshaler + var marshaler = struct { + embed + BirthDate string `json:"birthDate"` + CreatedAt string `json:"created_at"` + }{ + embed: embed(*t), + BirthDate: t.BirthDate.Format("2006-01-02"), + CreatedAt: t.CreatedAt.Format(time.RFC3339), + } + return MarshalJSONWithExtraProperty(marshaler, "type", "test") +} + +func TestMarshalJSONWithExtraProperties(t *testing.T) { + tests := []struct { + desc string + giveMarshaler interface{} + giveExtraProperties map[string]interface{} + wantBytes []byte + wantError string + }{ + { + desc: "invalid type", + giveMarshaler: []string{"invalid"}, + giveExtraProperties: map[string]interface{}{"key": "overwrite"}, + wantError: `cannot extract keys from []string; only structs and maps with string keys are supported`, + }, + { + desc: "invalid key type", + giveMarshaler: map[int]interface{}{42: "value"}, + giveExtraProperties: map[string]interface{}{"key": "overwrite"}, + wantError: `cannot extract keys from map[int]interface {}; only structs and maps with string keys are supported`, + }, + { + desc: "invalid map overwrite", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{"key": "overwrite"}, + wantError: `cannot add extra property "key" because it is already defined on the type`, + }, + { + desc: "invalid struct overwrite", + giveMarshaler: new(testMarshaler), + giveExtraProperties: map[string]interface{}{"birthDate": "2000-01-01"}, + wantError: `cannot add extra property "birthDate" because it is already defined on the type`, + }, + { + desc: "invalid struct overwrite embedded type", + giveMarshaler: new(testMarshaler), + giveExtraProperties: map[string]interface{}{"name": "bob"}, + wantError: `cannot add extra property "name" because it is already defined on the type`, + }, + { + desc: "nil", + giveMarshaler: nil, + giveExtraProperties: nil, + wantBytes: []byte(`null`), + }, + { + desc: "empty", + giveMarshaler: map[string]interface{}{}, + giveExtraProperties: map[string]interface{}{}, + wantBytes: []byte(`{}`), + }, + { + desc: "no extra properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "only extra properties", + giveMarshaler: map[string]interface{}{}, + giveExtraProperties: map[string]interface{}{"key": "value"}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "single extra property", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{"extra": "property"}, + wantBytes: []byte(`{"key":"value","extra":"property"}`), + }, + { + desc: "multiple extra properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{"one": 1, "two": 2}, + wantBytes: []byte(`{"key":"value","one":1,"two":2}`), + }, + { + desc: "nested properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{ + "user": map[string]interface{}{ + "age": 42, + "name": "alice", + }, + }, + wantBytes: []byte(`{"key":"value","user":{"age":42,"name":"alice"}}`), + }, + { + desc: "multiple nested properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{ + "metadata": map[string]interface{}{ + "ip": "127.0.0.1", + }, + "user": map[string]interface{}{ + "age": 42, + "name": "alice", + }, + }, + wantBytes: []byte(`{"key":"value","metadata":{"ip":"127.0.0.1"},"user":{"age":42,"name":"alice"}}`), + }, + { + desc: "custom marshaler", + giveMarshaler: &testMarshaler{ + Name: "alice", + BirthDate: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), + CreatedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC), + }, + giveExtraProperties: map[string]interface{}{ + "extra": "property", + }, + wantBytes: []byte(`{"name":"alice","birthDate":"2000-01-01","created_at":"2024-01-01T00:00:00Z","type":"test","extra":"property"}`), + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + bytes, err := MarshalJSONWithExtraProperties(tt.giveMarshaler, tt.giveExtraProperties) + if tt.wantError != "" { + require.EqualError(t, err, tt.wantError) + assert.Nil(t, tt.wantBytes) + return + } + require.NoError(t, err) + assert.Equal(t, tt.wantBytes, bytes) + + value := make(map[string]interface{}) + require.NoError(t, json.Unmarshal(bytes, &value)) + }) + } +} + +func TestExtractExtraProperties(t *testing.T) { + t.Run("none", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice"}`), value) + require.NoError(t, err) + assert.Nil(t, extraProperties) + }) + + t.Run("non-nil pointer", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]interface{}{"age": float64(42)}, extraProperties) + }) + + t.Run("nil pointer", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + var value *user + _, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + assert.EqualError(t, err, "value must be non-nil to extract extra properties") + }) + + t.Run("non-zero value", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]interface{}{"age": float64(42)}, extraProperties) + }) + + t.Run("zero value", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + var value user + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]interface{}{"age": float64(42)}, extraProperties) + }) + + t.Run("exclude", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value, "age") + require.NoError(t, err) + assert.Nil(t, extraProperties) + }) +} diff --git a/seed/go-fiber/streaming/internal/stringer.go b/seed/go-fiber/streaming/internal/stringer.go new file mode 100644 index 000000000000..312801851e0e --- /dev/null +++ b/seed/go-fiber/streaming/internal/stringer.go @@ -0,0 +1,13 @@ +package internal + +import "encoding/json" + +// StringifyJSON returns a pretty JSON string representation of +// the given value. +func StringifyJSON(value interface{}) (string, error) { + bytes, err := json.MarshalIndent(value, "", " ") + if err != nil { + return "", err + } + return string(bytes), nil +} diff --git a/seed/go-fiber/streaming/internal/time.go b/seed/go-fiber/streaming/internal/time.go new file mode 100644 index 000000000000..ab0e269fade3 --- /dev/null +++ b/seed/go-fiber/streaming/internal/time.go @@ -0,0 +1,137 @@ +package internal + +import ( + "encoding/json" + "time" +) + +const dateFormat = "2006-01-02" + +// DateTime wraps time.Time and adapts its JSON representation +// to conform to a RFC3339 date (e.g. 2006-01-02). +// +// Ref: https://ijmacd.github.io/rfc3339-iso8601 +type Date struct { + t *time.Time +} + +// NewDate returns a new *Date. If the given time.Time +// is nil, nil will be returned. +func NewDate(t time.Time) *Date { + return &Date{t: &t} +} + +// NewOptionalDate returns a new *Date. If the given time.Time +// is nil, nil will be returned. +func NewOptionalDate(t *time.Time) *Date { + if t == nil { + return nil + } + return &Date{t: t} +} + +// Time returns the Date's underlying time, if any. If the +// date is nil, the zero value is returned. +func (d *Date) Time() time.Time { + if d == nil || d.t == nil { + return time.Time{} + } + return *d.t +} + +// TimePtr returns a pointer to the Date's underlying time.Time, if any. +func (d *Date) TimePtr() *time.Time { + if d == nil || d.t == nil { + return nil + } + if d.t.IsZero() { + return nil + } + return d.t +} + +func (d *Date) MarshalJSON() ([]byte, error) { + if d == nil || d.t == nil { + return nil, nil + } + return json.Marshal(d.t.Format(dateFormat)) +} + +func (d *Date) UnmarshalJSON(data []byte) error { + var raw string + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + + parsedTime, err := time.Parse(dateFormat, raw) + if err != nil { + return err + } + + *d = Date{t: &parsedTime} + return nil +} + +// DateTime wraps time.Time and adapts its JSON representation +// to conform to a RFC3339 date-time (e.g. 2017-07-21T17:32:28Z). +// +// Ref: https://ijmacd.github.io/rfc3339-iso8601 +type DateTime struct { + t *time.Time +} + +// NewDateTime returns a new *DateTime. +func NewDateTime(t time.Time) *DateTime { + return &DateTime{t: &t} +} + +// NewOptionalDateTime returns a new *DateTime. If the given time.Time +// is nil, nil will be returned. +func NewOptionalDateTime(t *time.Time) *DateTime { + if t == nil { + return nil + } + return &DateTime{t: t} +} + +// Time returns the DateTime's underlying time, if any. If the +// date-time is nil, the zero value is returned. +func (d *DateTime) Time() time.Time { + if d == nil || d.t == nil { + return time.Time{} + } + return *d.t +} + +// TimePtr returns a pointer to the DateTime's underlying time.Time, if any. +func (d *DateTime) TimePtr() *time.Time { + if d == nil || d.t == nil { + return nil + } + if d.t.IsZero() { + return nil + } + return d.t +} + +func (d *DateTime) MarshalJSON() ([]byte, error) { + if d == nil || d.t == nil { + return nil, nil + } + return json.Marshal(d.t.Format(time.RFC3339)) +} + +func (d *DateTime) UnmarshalJSON(data []byte) error { + var raw string + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + + parsedTime, err := time.Parse(time.RFC3339, raw) + if err != nil { + return err + } + + *d = DateTime{t: &parsedTime} + return nil +} diff --git a/seed/go-model/idempotency-headers/.fern/metadata.json b/seed/go-model/idempotency-headers/.fern/metadata.json new file mode 100644 index 000000000000..52154345880d --- /dev/null +++ b/seed/go-model/idempotency-headers/.fern/metadata.json @@ -0,0 +1,12 @@ +{ + "cliVersion": "DUMMY", + "generatorName": "fernapi/fern-go-model", + "generatorVersion": "latest", + "generatorConfig": { + "packageName": "fern", + "module": { + "path": "github.com/idempotency-headers/fern" + }, + "includeLegacyClientOptions": true + } +} \ No newline at end of file diff --git a/seed/go-model/idempotency-headers/internal/extra_properties.go b/seed/go-model/idempotency-headers/internal/extra_properties.go new file mode 100644 index 000000000000..57517691f132 --- /dev/null +++ b/seed/go-model/idempotency-headers/internal/extra_properties.go @@ -0,0 +1,141 @@ +package internal + +import ( + "bytes" + "encoding/json" + "fmt" + "reflect" + "strings" +) + +// MarshalJSONWithExtraProperty marshals the given value to JSON, including the extra property. +func MarshalJSONWithExtraProperty(marshaler any, key string, value any) ([]byte, error) { + return MarshalJSONWithExtraProperties(marshaler, map[string]any{key: value}) +} + +// MarshalJSONWithExtraProperties marshals the given value to JSON, including any extra properties. +func MarshalJSONWithExtraProperties(marshaler any, extraProperties map[string]any) ([]byte, error) { + bytes, err := json.Marshal(marshaler) + if err != nil { + return nil, err + } + if len(extraProperties) == 0 { + return bytes, nil + } + keys, err := getKeys(marshaler) + if err != nil { + return nil, err + } + for _, key := range keys { + if _, ok := extraProperties[key]; ok { + return nil, fmt.Errorf("cannot add extra property %q because it is already defined on the type", key) + } + } + extraBytes, err := json.Marshal(extraProperties) + if err != nil { + return nil, err + } + if isEmptyJSON(bytes) { + if isEmptyJSON(extraBytes) { + return bytes, nil + } + return extraBytes, nil + } + result := bytes[:len(bytes)-1] + result = append(result, ',') + result = append(result, extraBytes[1:len(extraBytes)-1]...) + result = append(result, '}') + return result, nil +} + +// ExtractExtraProperties extracts any extra properties from the given value. +func ExtractExtraProperties(bytes []byte, value any, exclude ...string) (map[string]any, error) { + val := reflect.ValueOf(value) + for val.Kind() == reflect.Ptr { + if val.IsNil() { + return nil, fmt.Errorf("value must be non-nil to extract extra properties") + } + val = val.Elem() + } + if err := json.Unmarshal(bytes, &value); err != nil { + return nil, err + } + var extraProperties map[string]any + if err := json.Unmarshal(bytes, &extraProperties); err != nil { + return nil, err + } + for i := 0; i < val.Type().NumField(); i++ { + key := jsonKey(val.Type().Field(i)) + if key == "" || key == "-" { + continue + } + delete(extraProperties, key) + } + for _, key := range exclude { + delete(extraProperties, key) + } + if len(extraProperties) == 0 { + return nil, nil + } + return extraProperties, nil +} + +// getKeys returns the keys associated with the given value. The value must be a +// a struct or a map with string keys. +func getKeys(value any) ([]string, error) { + val := reflect.ValueOf(value) + if val.Kind() == reflect.Ptr { + val = val.Elem() + } + if !val.IsValid() { + return nil, nil + } + switch val.Kind() { + case reflect.Struct: + return getKeysForStructType(val.Type()), nil + case reflect.Map: + var keys []string + if val.Type().Key().Kind() != reflect.String { + return nil, fmt.Errorf("cannot extract keys from %T; only structs and maps with string keys are supported", value) + } + for _, key := range val.MapKeys() { + keys = append(keys, key.String()) + } + return keys, nil + default: + return nil, fmt.Errorf("cannot extract keys from %T; only structs and maps with string keys are supported", value) + } +} + +// getKeysForStructType returns all the keys associated with the given struct type, +// visiting embedded fields recursively. +func getKeysForStructType(structType reflect.Type) []string { + if structType.Kind() == reflect.Pointer { + structType = structType.Elem() + } + if structType.Kind() != reflect.Struct { + return nil + } + var keys []string + for i := 0; i < structType.NumField(); i++ { + field := structType.Field(i) + if field.Anonymous { + keys = append(keys, getKeysForStructType(field.Type)...) + continue + } + keys = append(keys, jsonKey(field)) + } + return keys +} + +// jsonKey returns the JSON key from the struct tag of the given field, +// excluding the omitempty flag (if any). +func jsonKey(field reflect.StructField) string { + return strings.TrimSuffix(field.Tag.Get("json"), ",omitempty") +} + +// isEmptyJSON returns true if the given data is empty, the empty JSON object, or +// an explicit null. +func isEmptyJSON(data []byte) bool { + return len(data) <= 2 || bytes.Equal(data, []byte("null")) +} diff --git a/seed/go-model/idempotency-headers/internal/extra_properties_test.go b/seed/go-model/idempotency-headers/internal/extra_properties_test.go new file mode 100644 index 000000000000..0d46257763fb --- /dev/null +++ b/seed/go-model/idempotency-headers/internal/extra_properties_test.go @@ -0,0 +1,228 @@ +package internal + +import ( + "encoding/json" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type testMarshaler struct { + Name string `json:"name"` + BirthDate time.Time `json:"birthDate"` + CreatedAt time.Time `json:"created_at"` +} + +func (t *testMarshaler) MarshalJSON() ([]byte, error) { + type embed testMarshaler + var marshaler = struct { + embed + BirthDate string `json:"birthDate"` + CreatedAt string `json:"created_at"` + }{ + embed: embed(*t), + BirthDate: t.BirthDate.Format("2006-01-02"), + CreatedAt: t.CreatedAt.Format(time.RFC3339), + } + return MarshalJSONWithExtraProperty(marshaler, "type", "test") +} + +func TestMarshalJSONWithExtraProperties(t *testing.T) { + tests := []struct { + desc string + giveMarshaler any + giveExtraProperties map[string]any + wantBytes []byte + wantError string + }{ + { + desc: "invalid type", + giveMarshaler: []string{"invalid"}, + giveExtraProperties: map[string]any{"key": "overwrite"}, + wantError: `cannot extract keys from []string; only structs and maps with string keys are supported`, + }, + { + desc: "invalid key type", + giveMarshaler: map[int]any{42: "value"}, + giveExtraProperties: map[string]any{"key": "overwrite"}, + wantError: `cannot extract keys from map[int]interface {}; only structs and maps with string keys are supported`, + }, + { + desc: "invalid map overwrite", + giveMarshaler: map[string]any{"key": "value"}, + giveExtraProperties: map[string]any{"key": "overwrite"}, + wantError: `cannot add extra property "key" because it is already defined on the type`, + }, + { + desc: "invalid struct overwrite", + giveMarshaler: new(testMarshaler), + giveExtraProperties: map[string]any{"birthDate": "2000-01-01"}, + wantError: `cannot add extra property "birthDate" because it is already defined on the type`, + }, + { + desc: "invalid struct overwrite embedded type", + giveMarshaler: new(testMarshaler), + giveExtraProperties: map[string]any{"name": "bob"}, + wantError: `cannot add extra property "name" because it is already defined on the type`, + }, + { + desc: "nil", + giveMarshaler: nil, + giveExtraProperties: nil, + wantBytes: []byte(`null`), + }, + { + desc: "empty", + giveMarshaler: map[string]any{}, + giveExtraProperties: map[string]any{}, + wantBytes: []byte(`{}`), + }, + { + desc: "no extra properties", + giveMarshaler: map[string]any{"key": "value"}, + giveExtraProperties: map[string]any{}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "only extra properties", + giveMarshaler: map[string]any{}, + giveExtraProperties: map[string]any{"key": "value"}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "single extra property", + giveMarshaler: map[string]any{"key": "value"}, + giveExtraProperties: map[string]any{"extra": "property"}, + wantBytes: []byte(`{"key":"value","extra":"property"}`), + }, + { + desc: "multiple extra properties", + giveMarshaler: map[string]any{"key": "value"}, + giveExtraProperties: map[string]any{"one": 1, "two": 2}, + wantBytes: []byte(`{"key":"value","one":1,"two":2}`), + }, + { + desc: "nested properties", + giveMarshaler: map[string]any{"key": "value"}, + giveExtraProperties: map[string]any{ + "user": map[string]any{ + "age": 42, + "name": "alice", + }, + }, + wantBytes: []byte(`{"key":"value","user":{"age":42,"name":"alice"}}`), + }, + { + desc: "multiple nested properties", + giveMarshaler: map[string]any{"key": "value"}, + giveExtraProperties: map[string]any{ + "metadata": map[string]any{ + "ip": "127.0.0.1", + }, + "user": map[string]any{ + "age": 42, + "name": "alice", + }, + }, + wantBytes: []byte(`{"key":"value","metadata":{"ip":"127.0.0.1"},"user":{"age":42,"name":"alice"}}`), + }, + { + desc: "custom marshaler", + giveMarshaler: &testMarshaler{ + Name: "alice", + BirthDate: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), + CreatedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC), + }, + giveExtraProperties: map[string]any{ + "extra": "property", + }, + wantBytes: []byte(`{"name":"alice","birthDate":"2000-01-01","created_at":"2024-01-01T00:00:00Z","type":"test","extra":"property"}`), + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + bytes, err := MarshalJSONWithExtraProperties(tt.giveMarshaler, tt.giveExtraProperties) + if tt.wantError != "" { + require.EqualError(t, err, tt.wantError) + assert.Nil(t, tt.wantBytes) + return + } + require.NoError(t, err) + assert.Equal(t, tt.wantBytes, bytes) + + value := make(map[string]any) + require.NoError(t, json.Unmarshal(bytes, &value)) + }) + } +} + +func TestExtractExtraProperties(t *testing.T) { + t.Run("none", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice"}`), value) + require.NoError(t, err) + assert.Nil(t, extraProperties) + }) + + t.Run("non-nil pointer", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]any{"age": float64(42)}, extraProperties) + }) + + t.Run("nil pointer", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + var value *user + _, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + assert.EqualError(t, err, "value must be non-nil to extract extra properties") + }) + + t.Run("non-zero value", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]any{"age": float64(42)}, extraProperties) + }) + + t.Run("zero value", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + var value user + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]any{"age": float64(42)}, extraProperties) + }) + + t.Run("exclude", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value, "age") + require.NoError(t, err) + assert.Nil(t, extraProperties) + }) +} diff --git a/seed/go-model/idempotency-headers/internal/stringer.go b/seed/go-model/idempotency-headers/internal/stringer.go new file mode 100644 index 000000000000..0be54d1b5359 --- /dev/null +++ b/seed/go-model/idempotency-headers/internal/stringer.go @@ -0,0 +1,13 @@ +package internal + +import "encoding/json" + +// StringifyJSON returns a pretty JSON string representation of +// the given value. +func StringifyJSON(value any) (string, error) { + bytes, err := json.MarshalIndent(value, "", " ") + if err != nil { + return "", err + } + return string(bytes), nil +} diff --git a/seed/go-model/idempotency-headers/internal/time.go b/seed/go-model/idempotency-headers/internal/time.go new file mode 100644 index 000000000000..ab0e269fade3 --- /dev/null +++ b/seed/go-model/idempotency-headers/internal/time.go @@ -0,0 +1,137 @@ +package internal + +import ( + "encoding/json" + "time" +) + +const dateFormat = "2006-01-02" + +// DateTime wraps time.Time and adapts its JSON representation +// to conform to a RFC3339 date (e.g. 2006-01-02). +// +// Ref: https://ijmacd.github.io/rfc3339-iso8601 +type Date struct { + t *time.Time +} + +// NewDate returns a new *Date. If the given time.Time +// is nil, nil will be returned. +func NewDate(t time.Time) *Date { + return &Date{t: &t} +} + +// NewOptionalDate returns a new *Date. If the given time.Time +// is nil, nil will be returned. +func NewOptionalDate(t *time.Time) *Date { + if t == nil { + return nil + } + return &Date{t: t} +} + +// Time returns the Date's underlying time, if any. If the +// date is nil, the zero value is returned. +func (d *Date) Time() time.Time { + if d == nil || d.t == nil { + return time.Time{} + } + return *d.t +} + +// TimePtr returns a pointer to the Date's underlying time.Time, if any. +func (d *Date) TimePtr() *time.Time { + if d == nil || d.t == nil { + return nil + } + if d.t.IsZero() { + return nil + } + return d.t +} + +func (d *Date) MarshalJSON() ([]byte, error) { + if d == nil || d.t == nil { + return nil, nil + } + return json.Marshal(d.t.Format(dateFormat)) +} + +func (d *Date) UnmarshalJSON(data []byte) error { + var raw string + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + + parsedTime, err := time.Parse(dateFormat, raw) + if err != nil { + return err + } + + *d = Date{t: &parsedTime} + return nil +} + +// DateTime wraps time.Time and adapts its JSON representation +// to conform to a RFC3339 date-time (e.g. 2017-07-21T17:32:28Z). +// +// Ref: https://ijmacd.github.io/rfc3339-iso8601 +type DateTime struct { + t *time.Time +} + +// NewDateTime returns a new *DateTime. +func NewDateTime(t time.Time) *DateTime { + return &DateTime{t: &t} +} + +// NewOptionalDateTime returns a new *DateTime. If the given time.Time +// is nil, nil will be returned. +func NewOptionalDateTime(t *time.Time) *DateTime { + if t == nil { + return nil + } + return &DateTime{t: t} +} + +// Time returns the DateTime's underlying time, if any. If the +// date-time is nil, the zero value is returned. +func (d *DateTime) Time() time.Time { + if d == nil || d.t == nil { + return time.Time{} + } + return *d.t +} + +// TimePtr returns a pointer to the DateTime's underlying time.Time, if any. +func (d *DateTime) TimePtr() *time.Time { + if d == nil || d.t == nil { + return nil + } + if d.t.IsZero() { + return nil + } + return d.t +} + +func (d *DateTime) MarshalJSON() ([]byte, error) { + if d == nil || d.t == nil { + return nil, nil + } + return json.Marshal(d.t.Format(time.RFC3339)) +} + +func (d *DateTime) UnmarshalJSON(data []byte) error { + var raw string + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + + parsedTime, err := time.Parse(time.RFC3339, raw) + if err != nil { + return err + } + + *d = DateTime{t: &parsedTime} + return nil +} diff --git a/seed/go-model/inferred-auth-implicit-api-key/.fern/metadata.json b/seed/go-model/inferred-auth-implicit-api-key/.fern/metadata.json new file mode 100644 index 000000000000..508350d068b1 --- /dev/null +++ b/seed/go-model/inferred-auth-implicit-api-key/.fern/metadata.json @@ -0,0 +1,5 @@ +{ + "cliVersion": "DUMMY", + "generatorName": "fernapi/fern-go-model", + "generatorVersion": "latest" +} \ No newline at end of file diff --git a/seed/go-model/inferred-auth-implicit-api-key/auth.go b/seed/go-model/inferred-auth-implicit-api-key/auth.go new file mode 100644 index 000000000000..614883cf9eca --- /dev/null +++ b/seed/go-model/inferred-auth-implicit-api-key/auth.go @@ -0,0 +1,85 @@ +// Code generated by Fern. DO NOT EDIT. + +package inferredauthimplicitapikey + +import ( + json "encoding/json" + fmt "fmt" + internal "github.com/inferred-auth-implicit-api-key/fern/internal" +) + +// An auth token response. +type TokenResponse struct { + AccessToken string `json:"access_token" url:"access_token"` + TokenType string `json:"token_type" url:"token_type"` + ExpiresIn int `json:"expires_in" url:"expires_in"` + Scope *string `json:"scope,omitempty" url:"scope,omitempty"` + + extraProperties map[string]any + rawJSON json.RawMessage +} + +func (t *TokenResponse) GetAccessToken() string { + if t == nil { + return "" + } + return t.AccessToken +} + +func (t *TokenResponse) GetTokenType() string { + if t == nil { + return "" + } + return t.TokenType +} + +func (t *TokenResponse) GetExpiresIn() int { + if t == nil { + return 0 + } + return t.ExpiresIn +} + +func (t *TokenResponse) GetScope() *string { + if t == nil { + return nil + } + return t.Scope +} + +func (t *TokenResponse) GetExtraProperties() map[string]any { + if t == nil { + return nil + } + return t.extraProperties +} + +func (t *TokenResponse) UnmarshalJSON( + data []byte, +) error { + type unmarshaler TokenResponse + var value unmarshaler + if err := json.Unmarshal(data, &value); err != nil { + return err + } + *t = TokenResponse(value) + extraProperties, err := internal.ExtractExtraProperties(data, *t) + if err != nil { + return err + } + t.extraProperties = extraProperties + t.rawJSON = json.RawMessage(data) + return nil +} + +func (t *TokenResponse) String() string { + if len(t.rawJSON) > 0 { + if value, err := internal.StringifyJSON(t.rawJSON); err == nil { + return value + } + } + if value, err := internal.StringifyJSON(t); err == nil { + return value + } + return fmt.Sprintf("%#v", t) +} diff --git a/seed/go-model/inferred-auth-implicit-api-key/doc.go b/seed/go-model/inferred-auth-implicit-api-key/doc.go new file mode 100644 index 000000000000..92f5001a72d2 --- /dev/null +++ b/seed/go-model/inferred-auth-implicit-api-key/doc.go @@ -0,0 +1 @@ +package inferredauthimplicitapikey \ No newline at end of file diff --git a/seed/go-model/inferred-auth-implicit-api-key/go.mod b/seed/go-model/inferred-auth-implicit-api-key/go.mod new file mode 100644 index 000000000000..8aea0bdbd7d2 --- /dev/null +++ b/seed/go-model/inferred-auth-implicit-api-key/go.mod @@ -0,0 +1,14 @@ +module github.com/inferred-auth-implicit-api-key/fern + +go 1.21 + +toolchain go1.23.8 + +require github.com/stretchr/testify v1.8.4 + +require gopkg.in/yaml.v3 v3.0.1 // indirect + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect +) diff --git a/seed/go-model/inferred-auth-implicit-api-key/go.sum b/seed/go-model/inferred-auth-implicit-api-key/go.sum new file mode 100644 index 000000000000..fa4b6e6825c4 --- /dev/null +++ b/seed/go-model/inferred-auth-implicit-api-key/go.sum @@ -0,0 +1,10 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/seed/go-model/inferred-auth-implicit-api-key/internal/extra_properties.go b/seed/go-model/inferred-auth-implicit-api-key/internal/extra_properties.go new file mode 100644 index 000000000000..57517691f132 --- /dev/null +++ b/seed/go-model/inferred-auth-implicit-api-key/internal/extra_properties.go @@ -0,0 +1,141 @@ +package internal + +import ( + "bytes" + "encoding/json" + "fmt" + "reflect" + "strings" +) + +// MarshalJSONWithExtraProperty marshals the given value to JSON, including the extra property. +func MarshalJSONWithExtraProperty(marshaler any, key string, value any) ([]byte, error) { + return MarshalJSONWithExtraProperties(marshaler, map[string]any{key: value}) +} + +// MarshalJSONWithExtraProperties marshals the given value to JSON, including any extra properties. +func MarshalJSONWithExtraProperties(marshaler any, extraProperties map[string]any) ([]byte, error) { + bytes, err := json.Marshal(marshaler) + if err != nil { + return nil, err + } + if len(extraProperties) == 0 { + return bytes, nil + } + keys, err := getKeys(marshaler) + if err != nil { + return nil, err + } + for _, key := range keys { + if _, ok := extraProperties[key]; ok { + return nil, fmt.Errorf("cannot add extra property %q because it is already defined on the type", key) + } + } + extraBytes, err := json.Marshal(extraProperties) + if err != nil { + return nil, err + } + if isEmptyJSON(bytes) { + if isEmptyJSON(extraBytes) { + return bytes, nil + } + return extraBytes, nil + } + result := bytes[:len(bytes)-1] + result = append(result, ',') + result = append(result, extraBytes[1:len(extraBytes)-1]...) + result = append(result, '}') + return result, nil +} + +// ExtractExtraProperties extracts any extra properties from the given value. +func ExtractExtraProperties(bytes []byte, value any, exclude ...string) (map[string]any, error) { + val := reflect.ValueOf(value) + for val.Kind() == reflect.Ptr { + if val.IsNil() { + return nil, fmt.Errorf("value must be non-nil to extract extra properties") + } + val = val.Elem() + } + if err := json.Unmarshal(bytes, &value); err != nil { + return nil, err + } + var extraProperties map[string]any + if err := json.Unmarshal(bytes, &extraProperties); err != nil { + return nil, err + } + for i := 0; i < val.Type().NumField(); i++ { + key := jsonKey(val.Type().Field(i)) + if key == "" || key == "-" { + continue + } + delete(extraProperties, key) + } + for _, key := range exclude { + delete(extraProperties, key) + } + if len(extraProperties) == 0 { + return nil, nil + } + return extraProperties, nil +} + +// getKeys returns the keys associated with the given value. The value must be a +// a struct or a map with string keys. +func getKeys(value any) ([]string, error) { + val := reflect.ValueOf(value) + if val.Kind() == reflect.Ptr { + val = val.Elem() + } + if !val.IsValid() { + return nil, nil + } + switch val.Kind() { + case reflect.Struct: + return getKeysForStructType(val.Type()), nil + case reflect.Map: + var keys []string + if val.Type().Key().Kind() != reflect.String { + return nil, fmt.Errorf("cannot extract keys from %T; only structs and maps with string keys are supported", value) + } + for _, key := range val.MapKeys() { + keys = append(keys, key.String()) + } + return keys, nil + default: + return nil, fmt.Errorf("cannot extract keys from %T; only structs and maps with string keys are supported", value) + } +} + +// getKeysForStructType returns all the keys associated with the given struct type, +// visiting embedded fields recursively. +func getKeysForStructType(structType reflect.Type) []string { + if structType.Kind() == reflect.Pointer { + structType = structType.Elem() + } + if structType.Kind() != reflect.Struct { + return nil + } + var keys []string + for i := 0; i < structType.NumField(); i++ { + field := structType.Field(i) + if field.Anonymous { + keys = append(keys, getKeysForStructType(field.Type)...) + continue + } + keys = append(keys, jsonKey(field)) + } + return keys +} + +// jsonKey returns the JSON key from the struct tag of the given field, +// excluding the omitempty flag (if any). +func jsonKey(field reflect.StructField) string { + return strings.TrimSuffix(field.Tag.Get("json"), ",omitempty") +} + +// isEmptyJSON returns true if the given data is empty, the empty JSON object, or +// an explicit null. +func isEmptyJSON(data []byte) bool { + return len(data) <= 2 || bytes.Equal(data, []byte("null")) +} diff --git a/seed/go-model/inferred-auth-implicit-api-key/internal/extra_properties_test.go b/seed/go-model/inferred-auth-implicit-api-key/internal/extra_properties_test.go new file mode 100644 index 000000000000..0d46257763fb --- /dev/null +++ b/seed/go-model/inferred-auth-implicit-api-key/internal/extra_properties_test.go @@ -0,0 +1,228 @@ +package internal + +import ( + "encoding/json" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type testMarshaler struct { + Name string `json:"name"` + BirthDate time.Time `json:"birthDate"` + CreatedAt time.Time `json:"created_at"` +} + +func (t *testMarshaler) MarshalJSON() ([]byte, error) { + type embed testMarshaler + var marshaler = struct { + embed + BirthDate string `json:"birthDate"` + CreatedAt string `json:"created_at"` + }{ + embed: embed(*t), + BirthDate: t.BirthDate.Format("2006-01-02"), + CreatedAt: t.CreatedAt.Format(time.RFC3339), + } + return MarshalJSONWithExtraProperty(marshaler, "type", "test") +} + +func TestMarshalJSONWithExtraProperties(t *testing.T) { + tests := []struct { + desc string + giveMarshaler any + giveExtraProperties map[string]any + wantBytes []byte + wantError string + }{ + { + desc: "invalid type", + giveMarshaler: []string{"invalid"}, + giveExtraProperties: map[string]any{"key": "overwrite"}, + wantError: `cannot extract keys from []string; only structs and maps with string keys are supported`, + }, + { + desc: "invalid key type", + giveMarshaler: map[int]any{42: "value"}, + giveExtraProperties: map[string]any{"key": "overwrite"}, + wantError: `cannot extract keys from map[int]interface {}; only structs and maps with string keys are supported`, + }, + { + desc: "invalid map overwrite", + giveMarshaler: map[string]any{"key": "value"}, + giveExtraProperties: map[string]any{"key": "overwrite"}, + wantError: `cannot add extra property "key" because it is already defined on the type`, + }, + { + desc: "invalid struct overwrite", + giveMarshaler: new(testMarshaler), + giveExtraProperties: map[string]any{"birthDate": "2000-01-01"}, + wantError: `cannot add extra property "birthDate" because it is already defined on the type`, + }, + { + desc: "invalid struct overwrite embedded type", + giveMarshaler: new(testMarshaler), + giveExtraProperties: map[string]any{"name": "bob"}, + wantError: `cannot add extra property "name" because it is already defined on the type`, + }, + { + desc: "nil", + giveMarshaler: nil, + giveExtraProperties: nil, + wantBytes: []byte(`null`), + }, + { + desc: "empty", + giveMarshaler: map[string]any{}, + giveExtraProperties: map[string]any{}, + wantBytes: []byte(`{}`), + }, + { + desc: "no extra properties", + giveMarshaler: map[string]any{"key": "value"}, + giveExtraProperties: map[string]any{}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "only extra properties", + giveMarshaler: map[string]any{}, + giveExtraProperties: map[string]any{"key": "value"}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "single extra property", + giveMarshaler: map[string]any{"key": "value"}, + giveExtraProperties: map[string]any{"extra": "property"}, + wantBytes: []byte(`{"key":"value","extra":"property"}`), + }, + { + desc: "multiple extra properties", + giveMarshaler: map[string]any{"key": "value"}, + giveExtraProperties: map[string]any{"one": 1, "two": 2}, + wantBytes: []byte(`{"key":"value","one":1,"two":2}`), + }, + { + desc: "nested properties", + giveMarshaler: map[string]any{"key": "value"}, + giveExtraProperties: map[string]any{ + "user": map[string]any{ + "age": 42, + "name": "alice", + }, + }, + wantBytes: []byte(`{"key":"value","user":{"age":42,"name":"alice"}}`), + }, + { + desc: "multiple nested properties", + giveMarshaler: map[string]any{"key": "value"}, + giveExtraProperties: map[string]any{ + "metadata": map[string]any{ + "ip": "127.0.0.1", + }, + "user": map[string]any{ + "age": 42, + "name": "alice", + }, + }, + wantBytes: []byte(`{"key":"value","metadata":{"ip":"127.0.0.1"},"user":{"age":42,"name":"alice"}}`), + }, + { + desc: "custom marshaler", + giveMarshaler: &testMarshaler{ + Name: "alice", + BirthDate: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), + CreatedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC), + }, + giveExtraProperties: map[string]any{ + "extra": "property", + }, + wantBytes: []byte(`{"name":"alice","birthDate":"2000-01-01","created_at":"2024-01-01T00:00:00Z","type":"test","extra":"property"}`), + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + bytes, err := MarshalJSONWithExtraProperties(tt.giveMarshaler, tt.giveExtraProperties) + if tt.wantError != "" { + require.EqualError(t, err, tt.wantError) + assert.Nil(t, tt.wantBytes) + return + } + require.NoError(t, err) + assert.Equal(t, tt.wantBytes, bytes) + + value := make(map[string]any) + require.NoError(t, json.Unmarshal(bytes, &value)) + }) + } +} + +func TestExtractExtraProperties(t *testing.T) { + t.Run("none", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice"}`), value) + require.NoError(t, err) + assert.Nil(t, extraProperties) + }) + + t.Run("non-nil pointer", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]any{"age": float64(42)}, extraProperties) + }) + + t.Run("nil pointer", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + var value *user + _, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + assert.EqualError(t, err, "value must be non-nil to extract extra properties") + }) + + t.Run("non-zero value", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]any{"age": float64(42)}, extraProperties) + }) + + t.Run("zero value", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + var value user + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]any{"age": float64(42)}, extraProperties) + }) + + t.Run("exclude", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value, "age") + require.NoError(t, err) + assert.Nil(t, extraProperties) + }) +} diff --git a/seed/go-model/inferred-auth-implicit-api-key/internal/stringer.go b/seed/go-model/inferred-auth-implicit-api-key/internal/stringer.go new file mode 100644 index 000000000000..0be54d1b5359 --- /dev/null +++ b/seed/go-model/inferred-auth-implicit-api-key/internal/stringer.go @@ -0,0 +1,13 @@ +package internal + +import "encoding/json" + +// StringifyJSON returns a pretty JSON string representation of +// the given value. +func StringifyJSON(value any) (string, error) { + bytes, err := json.MarshalIndent(value, "", " ") + if err != nil { + return "", err + } + return string(bytes), nil +} diff --git a/seed/go-model/inferred-auth-implicit-api-key/internal/time.go b/seed/go-model/inferred-auth-implicit-api-key/internal/time.go new file mode 100644 index 000000000000..ab0e269fade3 --- /dev/null +++ b/seed/go-model/inferred-auth-implicit-api-key/internal/time.go @@ -0,0 +1,137 @@ +package internal + +import ( + "encoding/json" + "time" +) + +const dateFormat = "2006-01-02" + +// DateTime wraps time.Time and adapts its JSON representation +// to conform to a RFC3339 date (e.g. 2006-01-02). +// +// Ref: https://ijmacd.github.io/rfc3339-iso8601 +type Date struct { + t *time.Time +} + +// NewDate returns a new *Date. If the given time.Time +// is nil, nil will be returned. +func NewDate(t time.Time) *Date { + return &Date{t: &t} +} + +// NewOptionalDate returns a new *Date. If the given time.Time +// is nil, nil will be returned. +func NewOptionalDate(t *time.Time) *Date { + if t == nil { + return nil + } + return &Date{t: t} +} + +// Time returns the Date's underlying time, if any. If the +// date is nil, the zero value is returned. +func (d *Date) Time() time.Time { + if d == nil || d.t == nil { + return time.Time{} + } + return *d.t +} + +// TimePtr returns a pointer to the Date's underlying time.Time, if any. +func (d *Date) TimePtr() *time.Time { + if d == nil || d.t == nil { + return nil + } + if d.t.IsZero() { + return nil + } + return d.t +} + +func (d *Date) MarshalJSON() ([]byte, error) { + if d == nil || d.t == nil { + return nil, nil + } + return json.Marshal(d.t.Format(dateFormat)) +} + +func (d *Date) UnmarshalJSON(data []byte) error { + var raw string + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + + parsedTime, err := time.Parse(dateFormat, raw) + if err != nil { + return err + } + + *d = Date{t: &parsedTime} + return nil +} + +// DateTime wraps time.Time and adapts its JSON representation +// to conform to a RFC3339 date-time (e.g. 2017-07-21T17:32:28Z). +// +// Ref: https://ijmacd.github.io/rfc3339-iso8601 +type DateTime struct { + t *time.Time +} + +// NewDateTime returns a new *DateTime. +func NewDateTime(t time.Time) *DateTime { + return &DateTime{t: &t} +} + +// NewOptionalDateTime returns a new *DateTime. If the given time.Time +// is nil, nil will be returned. +func NewOptionalDateTime(t *time.Time) *DateTime { + if t == nil { + return nil + } + return &DateTime{t: t} +} + +// Time returns the DateTime's underlying time, if any. If the +// date-time is nil, the zero value is returned. +func (d *DateTime) Time() time.Time { + if d == nil || d.t == nil { + return time.Time{} + } + return *d.t +} + +// TimePtr returns a pointer to the DateTime's underlying time.Time, if any. +func (d *DateTime) TimePtr() *time.Time { + if d == nil || d.t == nil { + return nil + } + if d.t.IsZero() { + return nil + } + return d.t +} + +func (d *DateTime) MarshalJSON() ([]byte, error) { + if d == nil || d.t == nil { + return nil, nil + } + return json.Marshal(d.t.Format(time.RFC3339)) +} + +func (d *DateTime) UnmarshalJSON(data []byte) error { + var raw string + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + + parsedTime, err := time.Parse(time.RFC3339, raw) + if err != nil { + return err + } + + *d = DateTime{t: &parsedTime} + return nil +} diff --git a/seed/go-model/inferred-auth-implicit-api-key/snippet.json b/seed/go-model/inferred-auth-implicit-api-key/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/go-model/inferred-auth-implicit-reference/.fern/metadata.json b/seed/go-model/inferred-auth-implicit-reference/.fern/metadata.json new file mode 100644 index 000000000000..508350d068b1 --- /dev/null +++ b/seed/go-model/inferred-auth-implicit-reference/.fern/metadata.json @@ -0,0 +1,5 @@ +{ + "cliVersion": "DUMMY", + "generatorName": "fernapi/fern-go-model", + "generatorVersion": "latest" +} \ No newline at end of file diff --git a/seed/go-model/inferred-auth-implicit-reference/auth.go b/seed/go-model/inferred-auth-implicit-reference/auth.go new file mode 100644 index 000000000000..640c153e4071 --- /dev/null +++ b/seed/go-model/inferred-auth-implicit-reference/auth.go @@ -0,0 +1,309 @@ +// Code generated by Fern. DO NOT EDIT. + +package inferredauthimplicit + +import ( + json "encoding/json" + fmt "fmt" + internal "github.com/inferred-auth-implicit-reference/fern/internal" +) + +// A request to obtain an OAuth token. +type GetTokenRequest struct { + ClientId string `json:"client_id" url:"client_id"` + ClientSecret string `json:"client_secret" url:"client_secret"` + Scope *string `json:"scope,omitempty" url:"scope,omitempty"` + + audience string + grantType string + extraProperties map[string]any + rawJSON json.RawMessage +} + +func (g *GetTokenRequest) GetClientId() string { + if g == nil { + return "" + } + return g.ClientId +} + +func (g *GetTokenRequest) GetClientSecret() string { + if g == nil { + return "" + } + return g.ClientSecret +} + +func (g *GetTokenRequest) GetAudience() string { + if g == nil { + return "" + } + return g.audience +} + +func (g *GetTokenRequest) GetGrantType() string { + if g == nil { + return "" + } + return g.grantType +} + +func (g *GetTokenRequest) GetScope() *string { + if g == nil { + return nil + } + return g.Scope +} + +func (g *GetTokenRequest) GetExtraProperties() map[string]any { + if g == nil { + return nil + } + return g.extraProperties +} + +func (g *GetTokenRequest) UnmarshalJSON( + data []byte, +) error { + type embed GetTokenRequest + var unmarshaler = struct { + embed + Audience string `json:"audience"` + GrantType string `json:"grant_type"` + }{ + embed: embed(*g), + } + if err := json.Unmarshal(data, &unmarshaler); err != nil { + return err + } + *g = GetTokenRequest(unmarshaler.embed) + if unmarshaler.Audience != "https://api.example.com" { + return fmt.Errorf("unexpected value for literal on type %T; expected %v got %v", g, "https://api.example.com", unmarshaler.Audience) + } + g.audience = unmarshaler.Audience + if unmarshaler.GrantType != "client_credentials" { + return fmt.Errorf("unexpected value for literal on type %T; expected %v got %v", g, "client_credentials", unmarshaler.GrantType) + } + g.grantType = unmarshaler.GrantType + extraProperties, err := internal.ExtractExtraProperties(data, *g, "audience", "grantType") + if err != nil { + return err + } + g.extraProperties = extraProperties + g.rawJSON = json.RawMessage(data) + return nil +} + +func (g *GetTokenRequest) MarshalJSON() ([]byte, error) { + type embed GetTokenRequest + var marshaler = struct { + embed + Audience string `json:"audience"` + GrantType string `json:"grant_type"` + }{ + embed: embed(*g), + Audience: "https://api.example.com", + GrantType: "client_credentials", + } + return json.Marshal(marshaler) +} + +func (g *GetTokenRequest) String() string { + if len(g.rawJSON) > 0 { + if value, err := internal.StringifyJSON(g.rawJSON); err == nil { + return value + } + } + if value, err := internal.StringifyJSON(g); err == nil { + return value + } + return fmt.Sprintf("%#v", g) +} + +// A request to refresh an OAuth token. +type RefreshTokenRequest struct { + ClientId string `json:"client_id" url:"client_id"` + ClientSecret string `json:"client_secret" url:"client_secret"` + RefreshToken string `json:"refresh_token" url:"refresh_token"` + Scope *string `json:"scope,omitempty" url:"scope,omitempty"` + + audience string + grantType string + extraProperties map[string]any + rawJSON json.RawMessage +} + +func (r *RefreshTokenRequest) GetClientId() string { + if r == nil { + return "" + } + return r.ClientId +} + +func (r *RefreshTokenRequest) GetClientSecret() string { + if r == nil { + return "" + } + return r.ClientSecret +} + +func (r *RefreshTokenRequest) GetRefreshToken() string { + if r == nil { + return "" + } + return r.RefreshToken +} + +func (r *RefreshTokenRequest) GetAudience() string { + if r == nil { + return "" + } + return r.audience +} + +func (r *RefreshTokenRequest) GetGrantType() string { + if r == nil { + return "" + } + return r.grantType +} + +func (r *RefreshTokenRequest) GetScope() *string { + if r == nil { + return nil + } + return r.Scope +} + +func (r *RefreshTokenRequest) GetExtraProperties() map[string]any { + if r == nil { + return nil + } + return r.extraProperties +} + +func (r *RefreshTokenRequest) UnmarshalJSON( + data []byte, +) error { + type embed RefreshTokenRequest + var unmarshaler = struct { + embed + Audience string `json:"audience"` + GrantType string `json:"grant_type"` + }{ + embed: embed(*r), + } + if err := json.Unmarshal(data, &unmarshaler); err != nil { + return err + } + *r = RefreshTokenRequest(unmarshaler.embed) + if unmarshaler.Audience != "https://api.example.com" { + return fmt.Errorf("unexpected value for literal on type %T; expected %v got %v", r, "https://api.example.com", unmarshaler.Audience) + } + r.audience = unmarshaler.Audience + if unmarshaler.GrantType != "refresh_token" { + return fmt.Errorf("unexpected value for literal on type %T; expected %v got %v", r, "refresh_token", unmarshaler.GrantType) + } + r.grantType = unmarshaler.GrantType + extraProperties, err := internal.ExtractExtraProperties(data, *r, "audience", "grantType") + if err != nil { + return err + } + r.extraProperties = extraProperties + r.rawJSON = json.RawMessage(data) + return nil +} + +func (r *RefreshTokenRequest) MarshalJSON() ([]byte, error) { + type embed RefreshTokenRequest + var marshaler = struct { + embed + Audience string `json:"audience"` + GrantType string `json:"grant_type"` + }{ + embed: embed(*r), + Audience: "https://api.example.com", + GrantType: "refresh_token", + } + return json.Marshal(marshaler) +} + +func (r *RefreshTokenRequest) String() string { + if len(r.rawJSON) > 0 { + if value, err := internal.StringifyJSON(r.rawJSON); err == nil { + return value + } + } + if value, err := internal.StringifyJSON(r); err == nil { + return value + } + return fmt.Sprintf("%#v", r) +} + +// An OAuth token response. +type TokenResponse struct { + AccessToken string `json:"access_token" url:"access_token"` + ExpiresIn int `json:"expires_in" url:"expires_in"` + RefreshToken *string `json:"refresh_token,omitempty" url:"refresh_token,omitempty"` + + extraProperties map[string]any + rawJSON json.RawMessage +} + +func (t *TokenResponse) GetAccessToken() string { + if t == nil { + return "" + } + return t.AccessToken +} + +func (t *TokenResponse) GetExpiresIn() int { + if t == nil { + return 0 + } + return t.ExpiresIn +} + +func (t *TokenResponse) GetRefreshToken() *string { + if t == nil { + return nil + } + return t.RefreshToken +} + +func (t *TokenResponse) GetExtraProperties() map[string]any { + if t == nil { + return nil + } + return t.extraProperties +} + +func (t *TokenResponse) UnmarshalJSON( + data []byte, +) error { + type unmarshaler TokenResponse + var value unmarshaler + if err := json.Unmarshal(data, &value); err != nil { + return err + } + *t = TokenResponse(value) + extraProperties, err := internal.ExtractExtraProperties(data, *t) + if err != nil { + return err + } + t.extraProperties = extraProperties + t.rawJSON = json.RawMessage(data) + return nil +} + +func (t *TokenResponse) String() string { + if len(t.rawJSON) > 0 { + if value, err := internal.StringifyJSON(t.rawJSON); err == nil { + return value + } + } + if value, err := internal.StringifyJSON(t); err == nil { + return value + } + return fmt.Sprintf("%#v", t) +} diff --git a/seed/go-model/inferred-auth-implicit-reference/doc.go b/seed/go-model/inferred-auth-implicit-reference/doc.go new file mode 100644 index 000000000000..8c3d2a5c18d9 --- /dev/null +++ b/seed/go-model/inferred-auth-implicit-reference/doc.go @@ -0,0 +1 @@ +package inferredauthimplicit \ No newline at end of file diff --git a/seed/go-model/inferred-auth-implicit-reference/go.mod b/seed/go-model/inferred-auth-implicit-reference/go.mod new file mode 100644 index 000000000000..0b7a3c400287 --- /dev/null +++ b/seed/go-model/inferred-auth-implicit-reference/go.mod @@ -0,0 +1,14 @@ +module github.com/inferred-auth-implicit-reference/fern + +go 1.21 + +toolchain go1.23.8 + +require github.com/stretchr/testify v1.8.4 + +require gopkg.in/yaml.v3 v3.0.1 // indirect + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect +) diff --git a/seed/go-model/inferred-auth-implicit-reference/go.sum b/seed/go-model/inferred-auth-implicit-reference/go.sum new file mode 100644 index 000000000000..fa4b6e6825c4 --- /dev/null +++ b/seed/go-model/inferred-auth-implicit-reference/go.sum @@ -0,0 +1,10 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/seed/go-model/inferred-auth-implicit-reference/internal/extra_properties.go b/seed/go-model/inferred-auth-implicit-reference/internal/extra_properties.go new file mode 100644 index 000000000000..57517691f132 --- /dev/null +++ b/seed/go-model/inferred-auth-implicit-reference/internal/extra_properties.go @@ -0,0 +1,141 @@ +package internal + +import ( + "bytes" + "encoding/json" + "fmt" + "reflect" + "strings" +) + +// MarshalJSONWithExtraProperty marshals the given value to JSON, including the extra property. +func MarshalJSONWithExtraProperty(marshaler any, key string, value any) ([]byte, error) { + return MarshalJSONWithExtraProperties(marshaler, map[string]any{key: value}) +} + +// MarshalJSONWithExtraProperties marshals the given value to JSON, including any extra properties. +func MarshalJSONWithExtraProperties(marshaler any, extraProperties map[string]any) ([]byte, error) { + bytes, err := json.Marshal(marshaler) + if err != nil { + return nil, err + } + if len(extraProperties) == 0 { + return bytes, nil + } + keys, err := getKeys(marshaler) + if err != nil { + return nil, err + } + for _, key := range keys { + if _, ok := extraProperties[key]; ok { + return nil, fmt.Errorf("cannot add extra property %q because it is already defined on the type", key) + } + } + extraBytes, err := json.Marshal(extraProperties) + if err != nil { + return nil, err + } + if isEmptyJSON(bytes) { + if isEmptyJSON(extraBytes) { + return bytes, nil + } + return extraBytes, nil + } + result := bytes[:len(bytes)-1] + result = append(result, ',') + result = append(result, extraBytes[1:len(extraBytes)-1]...) + result = append(result, '}') + return result, nil +} + +// ExtractExtraProperties extracts any extra properties from the given value. +func ExtractExtraProperties(bytes []byte, value any, exclude ...string) (map[string]any, error) { + val := reflect.ValueOf(value) + for val.Kind() == reflect.Ptr { + if val.IsNil() { + return nil, fmt.Errorf("value must be non-nil to extract extra properties") + } + val = val.Elem() + } + if err := json.Unmarshal(bytes, &value); err != nil { + return nil, err + } + var extraProperties map[string]any + if err := json.Unmarshal(bytes, &extraProperties); err != nil { + return nil, err + } + for i := 0; i < val.Type().NumField(); i++ { + key := jsonKey(val.Type().Field(i)) + if key == "" || key == "-" { + continue + } + delete(extraProperties, key) + } + for _, key := range exclude { + delete(extraProperties, key) + } + if len(extraProperties) == 0 { + return nil, nil + } + return extraProperties, nil +} + +// getKeys returns the keys associated with the given value. The value must be a +// a struct or a map with string keys. +func getKeys(value any) ([]string, error) { + val := reflect.ValueOf(value) + if val.Kind() == reflect.Ptr { + val = val.Elem() + } + if !val.IsValid() { + return nil, nil + } + switch val.Kind() { + case reflect.Struct: + return getKeysForStructType(val.Type()), nil + case reflect.Map: + var keys []string + if val.Type().Key().Kind() != reflect.String { + return nil, fmt.Errorf("cannot extract keys from %T; only structs and maps with string keys are supported", value) + } + for _, key := range val.MapKeys() { + keys = append(keys, key.String()) + } + return keys, nil + default: + return nil, fmt.Errorf("cannot extract keys from %T; only structs and maps with string keys are supported", value) + } +} + +// getKeysForStructType returns all the keys associated with the given struct type, +// visiting embedded fields recursively. +func getKeysForStructType(structType reflect.Type) []string { + if structType.Kind() == reflect.Pointer { + structType = structType.Elem() + } + if structType.Kind() != reflect.Struct { + return nil + } + var keys []string + for i := 0; i < structType.NumField(); i++ { + field := structType.Field(i) + if field.Anonymous { + keys = append(keys, getKeysForStructType(field.Type)...) + continue + } + keys = append(keys, jsonKey(field)) + } + return keys +} + +// jsonKey returns the JSON key from the struct tag of the given field, +// excluding the omitempty flag (if any). +func jsonKey(field reflect.StructField) string { + return strings.TrimSuffix(field.Tag.Get("json"), ",omitempty") +} + +// isEmptyJSON returns true if the given data is empty, the empty JSON object, or +// an explicit null. +func isEmptyJSON(data []byte) bool { + return len(data) <= 2 || bytes.Equal(data, []byte("null")) +} diff --git a/seed/go-model/inferred-auth-implicit-reference/internal/extra_properties_test.go b/seed/go-model/inferred-auth-implicit-reference/internal/extra_properties_test.go new file mode 100644 index 000000000000..0d46257763fb --- /dev/null +++ b/seed/go-model/inferred-auth-implicit-reference/internal/extra_properties_test.go @@ -0,0 +1,228 @@ +package internal + +import ( + "encoding/json" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type testMarshaler struct { + Name string `json:"name"` + BirthDate time.Time `json:"birthDate"` + CreatedAt time.Time `json:"created_at"` +} + +func (t *testMarshaler) MarshalJSON() ([]byte, error) { + type embed testMarshaler + var marshaler = struct { + embed + BirthDate string `json:"birthDate"` + CreatedAt string `json:"created_at"` + }{ + embed: embed(*t), + BirthDate: t.BirthDate.Format("2006-01-02"), + CreatedAt: t.CreatedAt.Format(time.RFC3339), + } + return MarshalJSONWithExtraProperty(marshaler, "type", "test") +} + +func TestMarshalJSONWithExtraProperties(t *testing.T) { + tests := []struct { + desc string + giveMarshaler any + giveExtraProperties map[string]any + wantBytes []byte + wantError string + }{ + { + desc: "invalid type", + giveMarshaler: []string{"invalid"}, + giveExtraProperties: map[string]any{"key": "overwrite"}, + wantError: `cannot extract keys from []string; only structs and maps with string keys are supported`, + }, + { + desc: "invalid key type", + giveMarshaler: map[int]any{42: "value"}, + giveExtraProperties: map[string]any{"key": "overwrite"}, + wantError: `cannot extract keys from map[int]interface {}; only structs and maps with string keys are supported`, + }, + { + desc: "invalid map overwrite", + giveMarshaler: map[string]any{"key": "value"}, + giveExtraProperties: map[string]any{"key": "overwrite"}, + wantError: `cannot add extra property "key" because it is already defined on the type`, + }, + { + desc: "invalid struct overwrite", + giveMarshaler: new(testMarshaler), + giveExtraProperties: map[string]any{"birthDate": "2000-01-01"}, + wantError: `cannot add extra property "birthDate" because it is already defined on the type`, + }, + { + desc: "invalid struct overwrite embedded type", + giveMarshaler: new(testMarshaler), + giveExtraProperties: map[string]any{"name": "bob"}, + wantError: `cannot add extra property "name" because it is already defined on the type`, + }, + { + desc: "nil", + giveMarshaler: nil, + giveExtraProperties: nil, + wantBytes: []byte(`null`), + }, + { + desc: "empty", + giveMarshaler: map[string]any{}, + giveExtraProperties: map[string]any{}, + wantBytes: []byte(`{}`), + }, + { + desc: "no extra properties", + giveMarshaler: map[string]any{"key": "value"}, + giveExtraProperties: map[string]any{}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "only extra properties", + giveMarshaler: map[string]any{}, + giveExtraProperties: map[string]any{"key": "value"}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "single extra property", + giveMarshaler: map[string]any{"key": "value"}, + giveExtraProperties: map[string]any{"extra": "property"}, + wantBytes: []byte(`{"key":"value","extra":"property"}`), + }, + { + desc: "multiple extra properties", + giveMarshaler: map[string]any{"key": "value"}, + giveExtraProperties: map[string]any{"one": 1, "two": 2}, + wantBytes: []byte(`{"key":"value","one":1,"two":2}`), + }, + { + desc: "nested properties", + giveMarshaler: map[string]any{"key": "value"}, + giveExtraProperties: map[string]any{ + "user": map[string]any{ + "age": 42, + "name": "alice", + }, + }, + wantBytes: []byte(`{"key":"value","user":{"age":42,"name":"alice"}}`), + }, + { + desc: "multiple nested properties", + giveMarshaler: map[string]any{"key": "value"}, + giveExtraProperties: map[string]any{ + "metadata": map[string]any{ + "ip": "127.0.0.1", + }, + "user": map[string]any{ + "age": 42, + "name": "alice", + }, + }, + wantBytes: []byte(`{"key":"value","metadata":{"ip":"127.0.0.1"},"user":{"age":42,"name":"alice"}}`), + }, + { + desc: "custom marshaler", + giveMarshaler: &testMarshaler{ + Name: "alice", + BirthDate: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), + CreatedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC), + }, + giveExtraProperties: map[string]any{ + "extra": "property", + }, + wantBytes: []byte(`{"name":"alice","birthDate":"2000-01-01","created_at":"2024-01-01T00:00:00Z","type":"test","extra":"property"}`), + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + bytes, err := MarshalJSONWithExtraProperties(tt.giveMarshaler, tt.giveExtraProperties) + if tt.wantError != "" { + require.EqualError(t, err, tt.wantError) + assert.Nil(t, tt.wantBytes) + return + } + require.NoError(t, err) + assert.Equal(t, tt.wantBytes, bytes) + + value := make(map[string]any) + require.NoError(t, json.Unmarshal(bytes, &value)) + }) + } +} + +func TestExtractExtraProperties(t *testing.T) { + t.Run("none", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice"}`), value) + require.NoError(t, err) + assert.Nil(t, extraProperties) + }) + + t.Run("non-nil pointer", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]any{"age": float64(42)}, extraProperties) + }) + + t.Run("nil pointer", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + var value *user + _, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + assert.EqualError(t, err, "value must be non-nil to extract extra properties") + }) + + t.Run("non-zero value", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]any{"age": float64(42)}, extraProperties) + }) + + t.Run("zero value", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + var value user + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]any{"age": float64(42)}, extraProperties) + }) + + t.Run("exclude", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value, "age") + require.NoError(t, err) + assert.Nil(t, extraProperties) + }) +} diff --git a/seed/go-model/inferred-auth-implicit-reference/internal/stringer.go b/seed/go-model/inferred-auth-implicit-reference/internal/stringer.go new file mode 100644 index 000000000000..0be54d1b5359 --- /dev/null +++ b/seed/go-model/inferred-auth-implicit-reference/internal/stringer.go @@ -0,0 +1,13 @@ +package internal + +import "encoding/json" + +// StringifyJSON returns a pretty JSON string representation of +// the given value. +func StringifyJSON(value any) (string, error) { + bytes, err := json.MarshalIndent(value, "", " ") + if err != nil { + return "", err + } + return string(bytes), nil +} diff --git a/seed/go-model/inferred-auth-implicit-reference/internal/time.go b/seed/go-model/inferred-auth-implicit-reference/internal/time.go new file mode 100644 index 000000000000..ab0e269fade3 --- /dev/null +++ b/seed/go-model/inferred-auth-implicit-reference/internal/time.go @@ -0,0 +1,137 @@ +package internal + +import ( + "encoding/json" + "time" +) + +const dateFormat = "2006-01-02" + +// DateTime wraps time.Time and adapts its JSON representation +// to conform to a RFC3339 date (e.g. 2006-01-02). +// +// Ref: https://ijmacd.github.io/rfc3339-iso8601 +type Date struct { + t *time.Time +} + +// NewDate returns a new *Date. If the given time.Time +// is nil, nil will be returned. +func NewDate(t time.Time) *Date { + return &Date{t: &t} +} + +// NewOptionalDate returns a new *Date. If the given time.Time +// is nil, nil will be returned. +func NewOptionalDate(t *time.Time) *Date { + if t == nil { + return nil + } + return &Date{t: t} +} + +// Time returns the Date's underlying time, if any. If the +// date is nil, the zero value is returned. +func (d *Date) Time() time.Time { + if d == nil || d.t == nil { + return time.Time{} + } + return *d.t +} + +// TimePtr returns a pointer to the Date's underlying time.Time, if any. +func (d *Date) TimePtr() *time.Time { + if d == nil || d.t == nil { + return nil + } + if d.t.IsZero() { + return nil + } + return d.t +} + +func (d *Date) MarshalJSON() ([]byte, error) { + if d == nil || d.t == nil { + return nil, nil + } + return json.Marshal(d.t.Format(dateFormat)) +} + +func (d *Date) UnmarshalJSON(data []byte) error { + var raw string + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + + parsedTime, err := time.Parse(dateFormat, raw) + if err != nil { + return err + } + + *d = Date{t: &parsedTime} + return nil +} + +// DateTime wraps time.Time and adapts its JSON representation +// to conform to a RFC3339 date-time (e.g. 2017-07-21T17:32:28Z). +// +// Ref: https://ijmacd.github.io/rfc3339-iso8601 +type DateTime struct { + t *time.Time +} + +// NewDateTime returns a new *DateTime. +func NewDateTime(t time.Time) *DateTime { + return &DateTime{t: &t} +} + +// NewOptionalDateTime returns a new *DateTime. If the given time.Time +// is nil, nil will be returned. +func NewOptionalDateTime(t *time.Time) *DateTime { + if t == nil { + return nil + } + return &DateTime{t: t} +} + +// Time returns the DateTime's underlying time, if any. If the +// date-time is nil, the zero value is returned. +func (d *DateTime) Time() time.Time { + if d == nil || d.t == nil { + return time.Time{} + } + return *d.t +} + +// TimePtr returns a pointer to the DateTime's underlying time.Time, if any. +func (d *DateTime) TimePtr() *time.Time { + if d == nil || d.t == nil { + return nil + } + if d.t.IsZero() { + return nil + } + return d.t +} + +func (d *DateTime) MarshalJSON() ([]byte, error) { + if d == nil || d.t == nil { + return nil, nil + } + return json.Marshal(d.t.Format(time.RFC3339)) +} + +func (d *DateTime) UnmarshalJSON(data []byte) error { + var raw string + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + + parsedTime, err := time.Parse(time.RFC3339, raw) + if err != nil { + return err + } + + *d = DateTime{t: &parsedTime} + return nil +} diff --git a/seed/go-model/inferred-auth-implicit-reference/snippet.json b/seed/go-model/inferred-auth-implicit-reference/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/go-model/nullable-allof-extends/.fern/metadata.json b/seed/go-model/nullable-allof-extends/.fern/metadata.json new file mode 100644 index 000000000000..508350d068b1 --- /dev/null +++ b/seed/go-model/nullable-allof-extends/.fern/metadata.json @@ -0,0 +1,5 @@ +{ + "cliVersion": "DUMMY", + "generatorName": "fernapi/fern-go-model", + "generatorVersion": "latest" +} \ No newline at end of file diff --git a/seed/go-model/nullable-allof-extends/doc.go b/seed/go-model/nullable-allof-extends/doc.go new file mode 100644 index 000000000000..a57cc3f67402 --- /dev/null +++ b/seed/go-model/nullable-allof-extends/doc.go @@ -0,0 +1 @@ +package api \ No newline at end of file diff --git a/seed/go-model/nullable-allof-extends/go.mod b/seed/go-model/nullable-allof-extends/go.mod new file mode 100644 index 000000000000..6ac081a399ce --- /dev/null +++ b/seed/go-model/nullable-allof-extends/go.mod @@ -0,0 +1,14 @@ +module github.com/nullable-allof-extends/fern + +go 1.21 + +toolchain go1.23.8 + +require github.com/stretchr/testify v1.8.4 + +require gopkg.in/yaml.v3 v3.0.1 // indirect + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect +) diff --git a/seed/go-model/nullable-allof-extends/go.sum b/seed/go-model/nullable-allof-extends/go.sum new file mode 100644 index 000000000000..fa4b6e6825c4 --- /dev/null +++ b/seed/go-model/nullable-allof-extends/go.sum @@ -0,0 +1,10 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/seed/go-model/nullable-allof-extends/internal/extra_properties.go b/seed/go-model/nullable-allof-extends/internal/extra_properties.go new file mode 100644 index 000000000000..57517691f132 --- /dev/null +++ b/seed/go-model/nullable-allof-extends/internal/extra_properties.go @@ -0,0 +1,141 @@ +package internal + +import ( + "bytes" + "encoding/json" + "fmt" + "reflect" + "strings" +) + +// MarshalJSONWithExtraProperty marshals the given value to JSON, including the extra property. +func MarshalJSONWithExtraProperty(marshaler any, key string, value any) ([]byte, error) { + return MarshalJSONWithExtraProperties(marshaler, map[string]any{key: value}) +} + +// MarshalJSONWithExtraProperties marshals the given value to JSON, including any extra properties. +func MarshalJSONWithExtraProperties(marshaler any, extraProperties map[string]any) ([]byte, error) { + bytes, err := json.Marshal(marshaler) + if err != nil { + return nil, err + } + if len(extraProperties) == 0 { + return bytes, nil + } + keys, err := getKeys(marshaler) + if err != nil { + return nil, err + } + for _, key := range keys { + if _, ok := extraProperties[key]; ok { + return nil, fmt.Errorf("cannot add extra property %q because it is already defined on the type", key) + } + } + extraBytes, err := json.Marshal(extraProperties) + if err != nil { + return nil, err + } + if isEmptyJSON(bytes) { + if isEmptyJSON(extraBytes) { + return bytes, nil + } + return extraBytes, nil + } + result := bytes[:len(bytes)-1] + result = append(result, ',') + result = append(result, extraBytes[1:len(extraBytes)-1]...) + result = append(result, '}') + return result, nil +} + +// ExtractExtraProperties extracts any extra properties from the given value. +func ExtractExtraProperties(bytes []byte, value any, exclude ...string) (map[string]any, error) { + val := reflect.ValueOf(value) + for val.Kind() == reflect.Ptr { + if val.IsNil() { + return nil, fmt.Errorf("value must be non-nil to extract extra properties") + } + val = val.Elem() + } + if err := json.Unmarshal(bytes, &value); err != nil { + return nil, err + } + var extraProperties map[string]any + if err := json.Unmarshal(bytes, &extraProperties); err != nil { + return nil, err + } + for i := 0; i < val.Type().NumField(); i++ { + key := jsonKey(val.Type().Field(i)) + if key == "" || key == "-" { + continue + } + delete(extraProperties, key) + } + for _, key := range exclude { + delete(extraProperties, key) + } + if len(extraProperties) == 0 { + return nil, nil + } + return extraProperties, nil +} + +// getKeys returns the keys associated with the given value. The value must be a +// a struct or a map with string keys. +func getKeys(value any) ([]string, error) { + val := reflect.ValueOf(value) + if val.Kind() == reflect.Ptr { + val = val.Elem() + } + if !val.IsValid() { + return nil, nil + } + switch val.Kind() { + case reflect.Struct: + return getKeysForStructType(val.Type()), nil + case reflect.Map: + var keys []string + if val.Type().Key().Kind() != reflect.String { + return nil, fmt.Errorf("cannot extract keys from %T; only structs and maps with string keys are supported", value) + } + for _, key := range val.MapKeys() { + keys = append(keys, key.String()) + } + return keys, nil + default: + return nil, fmt.Errorf("cannot extract keys from %T; only structs and maps with string keys are supported", value) + } +} + +// getKeysForStructType returns all the keys associated with the given struct type, +// visiting embedded fields recursively. +func getKeysForStructType(structType reflect.Type) []string { + if structType.Kind() == reflect.Pointer { + structType = structType.Elem() + } + if structType.Kind() != reflect.Struct { + return nil + } + var keys []string + for i := 0; i < structType.NumField(); i++ { + field := structType.Field(i) + if field.Anonymous { + keys = append(keys, getKeysForStructType(field.Type)...) + continue + } + keys = append(keys, jsonKey(field)) + } + return keys +} + +// jsonKey returns the JSON key from the struct tag of the given field, +// excluding the omitempty flag (if any). +func jsonKey(field reflect.StructField) string { + return strings.TrimSuffix(field.Tag.Get("json"), ",omitempty") +} + +// isEmptyJSON returns true if the given data is empty, the empty JSON object, or +// an explicit null. +func isEmptyJSON(data []byte) bool { + return len(data) <= 2 || bytes.Equal(data, []byte("null")) +} diff --git a/seed/go-model/nullable-allof-extends/internal/extra_properties_test.go b/seed/go-model/nullable-allof-extends/internal/extra_properties_test.go new file mode 100644 index 000000000000..0d46257763fb --- /dev/null +++ b/seed/go-model/nullable-allof-extends/internal/extra_properties_test.go @@ -0,0 +1,228 @@ +package internal + +import ( + "encoding/json" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type testMarshaler struct { + Name string `json:"name"` + BirthDate time.Time `json:"birthDate"` + CreatedAt time.Time `json:"created_at"` +} + +func (t *testMarshaler) MarshalJSON() ([]byte, error) { + type embed testMarshaler + var marshaler = struct { + embed + BirthDate string `json:"birthDate"` + CreatedAt string `json:"created_at"` + }{ + embed: embed(*t), + BirthDate: t.BirthDate.Format("2006-01-02"), + CreatedAt: t.CreatedAt.Format(time.RFC3339), + } + return MarshalJSONWithExtraProperty(marshaler, "type", "test") +} + +func TestMarshalJSONWithExtraProperties(t *testing.T) { + tests := []struct { + desc string + giveMarshaler any + giveExtraProperties map[string]any + wantBytes []byte + wantError string + }{ + { + desc: "invalid type", + giveMarshaler: []string{"invalid"}, + giveExtraProperties: map[string]any{"key": "overwrite"}, + wantError: `cannot extract keys from []string; only structs and maps with string keys are supported`, + }, + { + desc: "invalid key type", + giveMarshaler: map[int]any{42: "value"}, + giveExtraProperties: map[string]any{"key": "overwrite"}, + wantError: `cannot extract keys from map[int]interface {}; only structs and maps with string keys are supported`, + }, + { + desc: "invalid map overwrite", + giveMarshaler: map[string]any{"key": "value"}, + giveExtraProperties: map[string]any{"key": "overwrite"}, + wantError: `cannot add extra property "key" because it is already defined on the type`, + }, + { + desc: "invalid struct overwrite", + giveMarshaler: new(testMarshaler), + giveExtraProperties: map[string]any{"birthDate": "2000-01-01"}, + wantError: `cannot add extra property "birthDate" because it is already defined on the type`, + }, + { + desc: "invalid struct overwrite embedded type", + giveMarshaler: new(testMarshaler), + giveExtraProperties: map[string]any{"name": "bob"}, + wantError: `cannot add extra property "name" because it is already defined on the type`, + }, + { + desc: "nil", + giveMarshaler: nil, + giveExtraProperties: nil, + wantBytes: []byte(`null`), + }, + { + desc: "empty", + giveMarshaler: map[string]any{}, + giveExtraProperties: map[string]any{}, + wantBytes: []byte(`{}`), + }, + { + desc: "no extra properties", + giveMarshaler: map[string]any{"key": "value"}, + giveExtraProperties: map[string]any{}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "only extra properties", + giveMarshaler: map[string]any{}, + giveExtraProperties: map[string]any{"key": "value"}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "single extra property", + giveMarshaler: map[string]any{"key": "value"}, + giveExtraProperties: map[string]any{"extra": "property"}, + wantBytes: []byte(`{"key":"value","extra":"property"}`), + }, + { + desc: "multiple extra properties", + giveMarshaler: map[string]any{"key": "value"}, + giveExtraProperties: map[string]any{"one": 1, "two": 2}, + wantBytes: []byte(`{"key":"value","one":1,"two":2}`), + }, + { + desc: "nested properties", + giveMarshaler: map[string]any{"key": "value"}, + giveExtraProperties: map[string]any{ + "user": map[string]any{ + "age": 42, + "name": "alice", + }, + }, + wantBytes: []byte(`{"key":"value","user":{"age":42,"name":"alice"}}`), + }, + { + desc: "multiple nested properties", + giveMarshaler: map[string]any{"key": "value"}, + giveExtraProperties: map[string]any{ + "metadata": map[string]any{ + "ip": "127.0.0.1", + }, + "user": map[string]any{ + "age": 42, + "name": "alice", + }, + }, + wantBytes: []byte(`{"key":"value","metadata":{"ip":"127.0.0.1"},"user":{"age":42,"name":"alice"}}`), + }, + { + desc: "custom marshaler", + giveMarshaler: &testMarshaler{ + Name: "alice", + BirthDate: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), + CreatedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC), + }, + giveExtraProperties: map[string]any{ + "extra": "property", + }, + wantBytes: []byte(`{"name":"alice","birthDate":"2000-01-01","created_at":"2024-01-01T00:00:00Z","type":"test","extra":"property"}`), + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + bytes, err := MarshalJSONWithExtraProperties(tt.giveMarshaler, tt.giveExtraProperties) + if tt.wantError != "" { + require.EqualError(t, err, tt.wantError) + assert.Nil(t, tt.wantBytes) + return + } + require.NoError(t, err) + assert.Equal(t, tt.wantBytes, bytes) + + value := make(map[string]any) + require.NoError(t, json.Unmarshal(bytes, &value)) + }) + } +} + +func TestExtractExtraProperties(t *testing.T) { + t.Run("none", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice"}`), value) + require.NoError(t, err) + assert.Nil(t, extraProperties) + }) + + t.Run("non-nil pointer", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]any{"age": float64(42)}, extraProperties) + }) + + t.Run("nil pointer", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + var value *user + _, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + assert.EqualError(t, err, "value must be non-nil to extract extra properties") + }) + + t.Run("non-zero value", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]any{"age": float64(42)}, extraProperties) + }) + + t.Run("zero value", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + var value user + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]any{"age": float64(42)}, extraProperties) + }) + + t.Run("exclude", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value, "age") + require.NoError(t, err) + assert.Nil(t, extraProperties) + }) +} diff --git a/seed/go-model/nullable-allof-extends/internal/stringer.go b/seed/go-model/nullable-allof-extends/internal/stringer.go new file mode 100644 index 000000000000..0be54d1b5359 --- /dev/null +++ b/seed/go-model/nullable-allof-extends/internal/stringer.go @@ -0,0 +1,13 @@ +package internal + +import "encoding/json" + +// StringifyJSON returns a pretty JSON string representation of +// the given value. +func StringifyJSON(value any) (string, error) { + bytes, err := json.MarshalIndent(value, "", " ") + if err != nil { + return "", err + } + return string(bytes), nil +} diff --git a/seed/go-model/nullable-allof-extends/internal/time.go b/seed/go-model/nullable-allof-extends/internal/time.go new file mode 100644 index 000000000000..ab0e269fade3 --- /dev/null +++ b/seed/go-model/nullable-allof-extends/internal/time.go @@ -0,0 +1,137 @@ +package internal + +import ( + "encoding/json" + "time" +) + +const dateFormat = "2006-01-02" + +// DateTime wraps time.Time and adapts its JSON representation +// to conform to a RFC3339 date (e.g. 2006-01-02). +// +// Ref: https://ijmacd.github.io/rfc3339-iso8601 +type Date struct { + t *time.Time +} + +// NewDate returns a new *Date. If the given time.Time +// is nil, nil will be returned. +func NewDate(t time.Time) *Date { + return &Date{t: &t} +} + +// NewOptionalDate returns a new *Date. If the given time.Time +// is nil, nil will be returned. +func NewOptionalDate(t *time.Time) *Date { + if t == nil { + return nil + } + return &Date{t: t} +} + +// Time returns the Date's underlying time, if any. If the +// date is nil, the zero value is returned. +func (d *Date) Time() time.Time { + if d == nil || d.t == nil { + return time.Time{} + } + return *d.t +} + +// TimePtr returns a pointer to the Date's underlying time.Time, if any. +func (d *Date) TimePtr() *time.Time { + if d == nil || d.t == nil { + return nil + } + if d.t.IsZero() { + return nil + } + return d.t +} + +func (d *Date) MarshalJSON() ([]byte, error) { + if d == nil || d.t == nil { + return nil, nil + } + return json.Marshal(d.t.Format(dateFormat)) +} + +func (d *Date) UnmarshalJSON(data []byte) error { + var raw string + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + + parsedTime, err := time.Parse(dateFormat, raw) + if err != nil { + return err + } + + *d = Date{t: &parsedTime} + return nil +} + +// DateTime wraps time.Time and adapts its JSON representation +// to conform to a RFC3339 date-time (e.g. 2017-07-21T17:32:28Z). +// +// Ref: https://ijmacd.github.io/rfc3339-iso8601 +type DateTime struct { + t *time.Time +} + +// NewDateTime returns a new *DateTime. +func NewDateTime(t time.Time) *DateTime { + return &DateTime{t: &t} +} + +// NewOptionalDateTime returns a new *DateTime. If the given time.Time +// is nil, nil will be returned. +func NewOptionalDateTime(t *time.Time) *DateTime { + if t == nil { + return nil + } + return &DateTime{t: t} +} + +// Time returns the DateTime's underlying time, if any. If the +// date-time is nil, the zero value is returned. +func (d *DateTime) Time() time.Time { + if d == nil || d.t == nil { + return time.Time{} + } + return *d.t +} + +// TimePtr returns a pointer to the DateTime's underlying time.Time, if any. +func (d *DateTime) TimePtr() *time.Time { + if d == nil || d.t == nil { + return nil + } + if d.t.IsZero() { + return nil + } + return d.t +} + +func (d *DateTime) MarshalJSON() ([]byte, error) { + if d == nil || d.t == nil { + return nil, nil + } + return json.Marshal(d.t.Format(time.RFC3339)) +} + +func (d *DateTime) UnmarshalJSON(data []byte) error { + var raw string + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + + parsedTime, err := time.Parse(time.RFC3339, raw) + if err != nil { + return err + } + + *d = DateTime{t: &parsedTime} + return nil +} diff --git a/seed/go-model/nullable-allof-extends/snippet.json b/seed/go-model/nullable-allof-extends/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/go-model/nullable-allof-extends/types.go b/seed/go-model/nullable-allof-extends/types.go new file mode 100644 index 000000000000..7d1deaffb3f5 --- /dev/null +++ b/seed/go-model/nullable-allof-extends/types.go @@ -0,0 +1,173 @@ +// Code generated by Fern. DO NOT EDIT. + +package api + +import ( + json "encoding/json" + fmt "fmt" + internal "github.com/nullable-allof-extends/fern/internal" +) + +// Object inheriting from a nullable schema via allOf. +type RootObject struct { + NormalField *string `json:"normalField,omitempty" url:"normalField,omitempty"` + NullableField *string `json:"nullableField,omitempty" url:"nullableField,omitempty"` + + extraProperties map[string]any + rawJSON json.RawMessage +} + +func (r *RootObject) GetNormalField() *string { + if r == nil { + return nil + } + return r.NormalField +} + +func (r *RootObject) GetNullableField() *string { + if r == nil { + return nil + } + return r.NullableField +} + +func (r *RootObject) GetExtraProperties() map[string]any { + if r == nil { + return nil + } + return r.extraProperties +} + +func (r *RootObject) UnmarshalJSON( + data []byte, +) error { + type unmarshaler RootObject + var value unmarshaler + if err := json.Unmarshal(data, &value); err != nil { + return err + } + *r = RootObject(value) + extraProperties, err := internal.ExtractExtraProperties(data, *r) + if err != nil { + return err + } + r.extraProperties = extraProperties + r.rawJSON = json.RawMessage(data) + return nil +} + +func (r *RootObject) String() string { + if len(r.rawJSON) > 0 { + if value, err := internal.StringifyJSON(r.rawJSON); err == nil { + return value + } + } + if value, err := internal.StringifyJSON(r); err == nil { + return value + } + return fmt.Sprintf("%#v", r) +} + +// A standard object with no nullable issues. +type NormalObject struct { + NormalField *string `json:"normalField,omitempty" url:"normalField,omitempty"` + + extraProperties map[string]any + rawJSON json.RawMessage +} + +func (n *NormalObject) GetNormalField() *string { + if n == nil { + return nil + } + return n.NormalField +} + +func (n *NormalObject) GetExtraProperties() map[string]any { + if n == nil { + return nil + } + return n.extraProperties +} + +func (n *NormalObject) UnmarshalJSON( + data []byte, +) error { + type unmarshaler NormalObject + var value unmarshaler + if err := json.Unmarshal(data, &value); err != nil { + return err + } + *n = NormalObject(value) + extraProperties, err := internal.ExtractExtraProperties(data, *n) + if err != nil { + return err + } + n.extraProperties = extraProperties + n.rawJSON = json.RawMessage(data) + return nil +} + +func (n *NormalObject) String() string { + if len(n.rawJSON) > 0 { + if value, err := internal.StringifyJSON(n.rawJSON); err == nil { + return value + } + } + if value, err := internal.StringifyJSON(n); err == nil { + return value + } + return fmt.Sprintf("%#v", n) +} + +// This schema has nullable:true at the top level. +type NullableObject struct { + NullableField *string `json:"nullableField,omitempty" url:"nullableField,omitempty"` + + extraProperties map[string]any + rawJSON json.RawMessage +} + +func (n *NullableObject) GetNullableField() *string { + if n == nil { + return nil + } + return n.NullableField +} + +func (n *NullableObject) GetExtraProperties() map[string]any { + if n == nil { + return nil + } + return n.extraProperties +} + +func (n *NullableObject) UnmarshalJSON( + data []byte, +) error { + type unmarshaler NullableObject + var value unmarshaler + if err := json.Unmarshal(data, &value); err != nil { + return err + } + *n = NullableObject(value) + extraProperties, err := internal.ExtractExtraProperties(data, *n) + if err != nil { + return err + } + n.extraProperties = extraProperties + n.rawJSON = json.RawMessage(data) + return nil +} + +func (n *NullableObject) String() string { + if len(n.rawJSON) > 0 { + if value, err := internal.StringifyJSON(n.rawJSON); err == nil { + return value + } + } + if value, err := internal.StringifyJSON(n); err == nil { + return value + } + return fmt.Sprintf("%#v", n) +} diff --git a/seed/go-model/oauth-client-credentials-mandatory-auth/.fern/metadata.json b/seed/go-model/oauth-client-credentials-mandatory-auth/.fern/metadata.json new file mode 100644 index 000000000000..508350d068b1 --- /dev/null +++ b/seed/go-model/oauth-client-credentials-mandatory-auth/.fern/metadata.json @@ -0,0 +1,5 @@ +{ + "cliVersion": "DUMMY", + "generatorName": "fernapi/fern-go-model", + "generatorVersion": "latest" +} \ No newline at end of file diff --git a/seed/go-model/oauth-client-credentials-mandatory-auth/auth.go b/seed/go-model/oauth-client-credentials-mandatory-auth/auth.go new file mode 100644 index 000000000000..414a7801be34 --- /dev/null +++ b/seed/go-model/oauth-client-credentials-mandatory-auth/auth.go @@ -0,0 +1,77 @@ +// Code generated by Fern. DO NOT EDIT. + +package oauthclientcredentialsmandatoryauth + +import ( + json "encoding/json" + fmt "fmt" + internal "github.com/oauth-client-credentials-mandatory-auth/fern/internal" +) + +// An OAuth token response. +type TokenResponse struct { + AccessToken string `json:"access_token" url:"access_token"` + ExpiresIn int `json:"expires_in" url:"expires_in"` + RefreshToken *string `json:"refresh_token,omitempty" url:"refresh_token,omitempty"` + + extraProperties map[string]any + rawJSON json.RawMessage +} + +func (t *TokenResponse) GetAccessToken() string { + if t == nil { + return "" + } + return t.AccessToken +} + +func (t *TokenResponse) GetExpiresIn() int { + if t == nil { + return 0 + } + return t.ExpiresIn +} + +func (t *TokenResponse) GetRefreshToken() *string { + if t == nil { + return nil + } + return t.RefreshToken +} + +func (t *TokenResponse) GetExtraProperties() map[string]any { + if t == nil { + return nil + } + return t.extraProperties +} + +func (t *TokenResponse) UnmarshalJSON( + data []byte, +) error { + type unmarshaler TokenResponse + var value unmarshaler + if err := json.Unmarshal(data, &value); err != nil { + return err + } + *t = TokenResponse(value) + extraProperties, err := internal.ExtractExtraProperties(data, *t) + if err != nil { + return err + } + t.extraProperties = extraProperties + t.rawJSON = json.RawMessage(data) + return nil +} + +func (t *TokenResponse) String() string { + if len(t.rawJSON) > 0 { + if value, err := internal.StringifyJSON(t.rawJSON); err == nil { + return value + } + } + if value, err := internal.StringifyJSON(t); err == nil { + return value + } + return fmt.Sprintf("%#v", t) +} diff --git a/seed/go-model/oauth-client-credentials-mandatory-auth/doc.go b/seed/go-model/oauth-client-credentials-mandatory-auth/doc.go new file mode 100644 index 000000000000..0d3c40ce39da --- /dev/null +++ b/seed/go-model/oauth-client-credentials-mandatory-auth/doc.go @@ -0,0 +1 @@ +package oauthclientcredentialsmandatoryauth \ No newline at end of file diff --git a/seed/go-model/oauth-client-credentials-mandatory-auth/go.mod b/seed/go-model/oauth-client-credentials-mandatory-auth/go.mod new file mode 100644 index 000000000000..9f43f8eb9597 --- /dev/null +++ b/seed/go-model/oauth-client-credentials-mandatory-auth/go.mod @@ -0,0 +1,14 @@ +module github.com/oauth-client-credentials-mandatory-auth/fern + +go 1.21 + +toolchain go1.23.8 + +require github.com/stretchr/testify v1.8.4 + +require gopkg.in/yaml.v3 v3.0.1 // indirect + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect +) diff --git a/seed/go-model/oauth-client-credentials-mandatory-auth/go.sum b/seed/go-model/oauth-client-credentials-mandatory-auth/go.sum new file mode 100644 index 000000000000..fa4b6e6825c4 --- /dev/null +++ b/seed/go-model/oauth-client-credentials-mandatory-auth/go.sum @@ -0,0 +1,10 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/seed/go-model/oauth-client-credentials-mandatory-auth/internal/extra_properties.go b/seed/go-model/oauth-client-credentials-mandatory-auth/internal/extra_properties.go new file mode 100644 index 000000000000..57517691f132 --- /dev/null +++ b/seed/go-model/oauth-client-credentials-mandatory-auth/internal/extra_properties.go @@ -0,0 +1,141 @@ +package internal + +import ( + "bytes" + "encoding/json" + "fmt" + "reflect" + "strings" +) + +// MarshalJSONWithExtraProperty marshals the given value to JSON, including the extra property. +func MarshalJSONWithExtraProperty(marshaler any, key string, value any) ([]byte, error) { + return MarshalJSONWithExtraProperties(marshaler, map[string]any{key: value}) +} + +// MarshalJSONWithExtraProperties marshals the given value to JSON, including any extra properties. +func MarshalJSONWithExtraProperties(marshaler any, extraProperties map[string]any) ([]byte, error) { + bytes, err := json.Marshal(marshaler) + if err != nil { + return nil, err + } + if len(extraProperties) == 0 { + return bytes, nil + } + keys, err := getKeys(marshaler) + if err != nil { + return nil, err + } + for _, key := range keys { + if _, ok := extraProperties[key]; ok { + return nil, fmt.Errorf("cannot add extra property %q because it is already defined on the type", key) + } + } + extraBytes, err := json.Marshal(extraProperties) + if err != nil { + return nil, err + } + if isEmptyJSON(bytes) { + if isEmptyJSON(extraBytes) { + return bytes, nil + } + return extraBytes, nil + } + result := bytes[:len(bytes)-1] + result = append(result, ',') + result = append(result, extraBytes[1:len(extraBytes)-1]...) + result = append(result, '}') + return result, nil +} + +// ExtractExtraProperties extracts any extra properties from the given value. +func ExtractExtraProperties(bytes []byte, value any, exclude ...string) (map[string]any, error) { + val := reflect.ValueOf(value) + for val.Kind() == reflect.Ptr { + if val.IsNil() { + return nil, fmt.Errorf("value must be non-nil to extract extra properties") + } + val = val.Elem() + } + if err := json.Unmarshal(bytes, &value); err != nil { + return nil, err + } + var extraProperties map[string]any + if err := json.Unmarshal(bytes, &extraProperties); err != nil { + return nil, err + } + for i := 0; i < val.Type().NumField(); i++ { + key := jsonKey(val.Type().Field(i)) + if key == "" || key == "-" { + continue + } + delete(extraProperties, key) + } + for _, key := range exclude { + delete(extraProperties, key) + } + if len(extraProperties) == 0 { + return nil, nil + } + return extraProperties, nil +} + +// getKeys returns the keys associated with the given value. The value must be a +// a struct or a map with string keys. +func getKeys(value any) ([]string, error) { + val := reflect.ValueOf(value) + if val.Kind() == reflect.Ptr { + val = val.Elem() + } + if !val.IsValid() { + return nil, nil + } + switch val.Kind() { + case reflect.Struct: + return getKeysForStructType(val.Type()), nil + case reflect.Map: + var keys []string + if val.Type().Key().Kind() != reflect.String { + return nil, fmt.Errorf("cannot extract keys from %T; only structs and maps with string keys are supported", value) + } + for _, key := range val.MapKeys() { + keys = append(keys, key.String()) + } + return keys, nil + default: + return nil, fmt.Errorf("cannot extract keys from %T; only structs and maps with string keys are supported", value) + } +} + +// getKeysForStructType returns all the keys associated with the given struct type, +// visiting embedded fields recursively. +func getKeysForStructType(structType reflect.Type) []string { + if structType.Kind() == reflect.Pointer { + structType = structType.Elem() + } + if structType.Kind() != reflect.Struct { + return nil + } + var keys []string + for i := 0; i < structType.NumField(); i++ { + field := structType.Field(i) + if field.Anonymous { + keys = append(keys, getKeysForStructType(field.Type)...) + continue + } + keys = append(keys, jsonKey(field)) + } + return keys +} + +// jsonKey returns the JSON key from the struct tag of the given field, +// excluding the omitempty flag (if any). +func jsonKey(field reflect.StructField) string { + return strings.TrimSuffix(field.Tag.Get("json"), ",omitempty") +} + +// isEmptyJSON returns true if the given data is empty, the empty JSON object, or +// an explicit null. +func isEmptyJSON(data []byte) bool { + return len(data) <= 2 || bytes.Equal(data, []byte("null")) +} diff --git a/seed/go-model/oauth-client-credentials-mandatory-auth/internal/extra_properties_test.go b/seed/go-model/oauth-client-credentials-mandatory-auth/internal/extra_properties_test.go new file mode 100644 index 000000000000..0d46257763fb --- /dev/null +++ b/seed/go-model/oauth-client-credentials-mandatory-auth/internal/extra_properties_test.go @@ -0,0 +1,228 @@ +package internal + +import ( + "encoding/json" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type testMarshaler struct { + Name string `json:"name"` + BirthDate time.Time `json:"birthDate"` + CreatedAt time.Time `json:"created_at"` +} + +func (t *testMarshaler) MarshalJSON() ([]byte, error) { + type embed testMarshaler + var marshaler = struct { + embed + BirthDate string `json:"birthDate"` + CreatedAt string `json:"created_at"` + }{ + embed: embed(*t), + BirthDate: t.BirthDate.Format("2006-01-02"), + CreatedAt: t.CreatedAt.Format(time.RFC3339), + } + return MarshalJSONWithExtraProperty(marshaler, "type", "test") +} + +func TestMarshalJSONWithExtraProperties(t *testing.T) { + tests := []struct { + desc string + giveMarshaler any + giveExtraProperties map[string]any + wantBytes []byte + wantError string + }{ + { + desc: "invalid type", + giveMarshaler: []string{"invalid"}, + giveExtraProperties: map[string]any{"key": "overwrite"}, + wantError: `cannot extract keys from []string; only structs and maps with string keys are supported`, + }, + { + desc: "invalid key type", + giveMarshaler: map[int]any{42: "value"}, + giveExtraProperties: map[string]any{"key": "overwrite"}, + wantError: `cannot extract keys from map[int]interface {}; only structs and maps with string keys are supported`, + }, + { + desc: "invalid map overwrite", + giveMarshaler: map[string]any{"key": "value"}, + giveExtraProperties: map[string]any{"key": "overwrite"}, + wantError: `cannot add extra property "key" because it is already defined on the type`, + }, + { + desc: "invalid struct overwrite", + giveMarshaler: new(testMarshaler), + giveExtraProperties: map[string]any{"birthDate": "2000-01-01"}, + wantError: `cannot add extra property "birthDate" because it is already defined on the type`, + }, + { + desc: "invalid struct overwrite embedded type", + giveMarshaler: new(testMarshaler), + giveExtraProperties: map[string]any{"name": "bob"}, + wantError: `cannot add extra property "name" because it is already defined on the type`, + }, + { + desc: "nil", + giveMarshaler: nil, + giveExtraProperties: nil, + wantBytes: []byte(`null`), + }, + { + desc: "empty", + giveMarshaler: map[string]any{}, + giveExtraProperties: map[string]any{}, + wantBytes: []byte(`{}`), + }, + { + desc: "no extra properties", + giveMarshaler: map[string]any{"key": "value"}, + giveExtraProperties: map[string]any{}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "only extra properties", + giveMarshaler: map[string]any{}, + giveExtraProperties: map[string]any{"key": "value"}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "single extra property", + giveMarshaler: map[string]any{"key": "value"}, + giveExtraProperties: map[string]any{"extra": "property"}, + wantBytes: []byte(`{"key":"value","extra":"property"}`), + }, + { + desc: "multiple extra properties", + giveMarshaler: map[string]any{"key": "value"}, + giveExtraProperties: map[string]any{"one": 1, "two": 2}, + wantBytes: []byte(`{"key":"value","one":1,"two":2}`), + }, + { + desc: "nested properties", + giveMarshaler: map[string]any{"key": "value"}, + giveExtraProperties: map[string]any{ + "user": map[string]any{ + "age": 42, + "name": "alice", + }, + }, + wantBytes: []byte(`{"key":"value","user":{"age":42,"name":"alice"}}`), + }, + { + desc: "multiple nested properties", + giveMarshaler: map[string]any{"key": "value"}, + giveExtraProperties: map[string]any{ + "metadata": map[string]any{ + "ip": "127.0.0.1", + }, + "user": map[string]any{ + "age": 42, + "name": "alice", + }, + }, + wantBytes: []byte(`{"key":"value","metadata":{"ip":"127.0.0.1"},"user":{"age":42,"name":"alice"}}`), + }, + { + desc: "custom marshaler", + giveMarshaler: &testMarshaler{ + Name: "alice", + BirthDate: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), + CreatedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC), + }, + giveExtraProperties: map[string]any{ + "extra": "property", + }, + wantBytes: []byte(`{"name":"alice","birthDate":"2000-01-01","created_at":"2024-01-01T00:00:00Z","type":"test","extra":"property"}`), + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + bytes, err := MarshalJSONWithExtraProperties(tt.giveMarshaler, tt.giveExtraProperties) + if tt.wantError != "" { + require.EqualError(t, err, tt.wantError) + assert.Nil(t, tt.wantBytes) + return + } + require.NoError(t, err) + assert.Equal(t, tt.wantBytes, bytes) + + value := make(map[string]any) + require.NoError(t, json.Unmarshal(bytes, &value)) + }) + } +} + +func TestExtractExtraProperties(t *testing.T) { + t.Run("none", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice"}`), value) + require.NoError(t, err) + assert.Nil(t, extraProperties) + }) + + t.Run("non-nil pointer", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]any{"age": float64(42)}, extraProperties) + }) + + t.Run("nil pointer", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + var value *user + _, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + assert.EqualError(t, err, "value must be non-nil to extract extra properties") + }) + + t.Run("non-zero value", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]any{"age": float64(42)}, extraProperties) + }) + + t.Run("zero value", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + var value user + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]any{"age": float64(42)}, extraProperties) + }) + + t.Run("exclude", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value, "age") + require.NoError(t, err) + assert.Nil(t, extraProperties) + }) +} diff --git a/seed/go-model/oauth-client-credentials-mandatory-auth/internal/stringer.go b/seed/go-model/oauth-client-credentials-mandatory-auth/internal/stringer.go new file mode 100644 index 000000000000..0be54d1b5359 --- /dev/null +++ b/seed/go-model/oauth-client-credentials-mandatory-auth/internal/stringer.go @@ -0,0 +1,13 @@ +package internal + +import "encoding/json" + +// StringifyJSON returns a pretty JSON string representation of +// the given value. +func StringifyJSON(value any) (string, error) { + bytes, err := json.MarshalIndent(value, "", " ") + if err != nil { + return "", err + } + return string(bytes), nil +} diff --git a/seed/go-model/oauth-client-credentials-mandatory-auth/internal/time.go b/seed/go-model/oauth-client-credentials-mandatory-auth/internal/time.go new file mode 100644 index 000000000000..ab0e269fade3 --- /dev/null +++ b/seed/go-model/oauth-client-credentials-mandatory-auth/internal/time.go @@ -0,0 +1,137 @@ +package internal + +import ( + "encoding/json" + "time" +) + +const dateFormat = "2006-01-02" + +// DateTime wraps time.Time and adapts its JSON representation +// to conform to a RFC3339 date (e.g. 2006-01-02). +// +// Ref: https://ijmacd.github.io/rfc3339-iso8601 +type Date struct { + t *time.Time +} + +// NewDate returns a new *Date. If the given time.Time +// is nil, nil will be returned. +func NewDate(t time.Time) *Date { + return &Date{t: &t} +} + +// NewOptionalDate returns a new *Date. If the given time.Time +// is nil, nil will be returned. +func NewOptionalDate(t *time.Time) *Date { + if t == nil { + return nil + } + return &Date{t: t} +} + +// Time returns the Date's underlying time, if any. If the +// date is nil, the zero value is returned. +func (d *Date) Time() time.Time { + if d == nil || d.t == nil { + return time.Time{} + } + return *d.t +} + +// TimePtr returns a pointer to the Date's underlying time.Time, if any. +func (d *Date) TimePtr() *time.Time { + if d == nil || d.t == nil { + return nil + } + if d.t.IsZero() { + return nil + } + return d.t +} + +func (d *Date) MarshalJSON() ([]byte, error) { + if d == nil || d.t == nil { + return nil, nil + } + return json.Marshal(d.t.Format(dateFormat)) +} + +func (d *Date) UnmarshalJSON(data []byte) error { + var raw string + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + + parsedTime, err := time.Parse(dateFormat, raw) + if err != nil { + return err + } + + *d = Date{t: &parsedTime} + return nil +} + +// DateTime wraps time.Time and adapts its JSON representation +// to conform to a RFC3339 date-time (e.g. 2017-07-21T17:32:28Z). +// +// Ref: https://ijmacd.github.io/rfc3339-iso8601 +type DateTime struct { + t *time.Time +} + +// NewDateTime returns a new *DateTime. +func NewDateTime(t time.Time) *DateTime { + return &DateTime{t: &t} +} + +// NewOptionalDateTime returns a new *DateTime. If the given time.Time +// is nil, nil will be returned. +func NewOptionalDateTime(t *time.Time) *DateTime { + if t == nil { + return nil + } + return &DateTime{t: t} +} + +// Time returns the DateTime's underlying time, if any. If the +// date-time is nil, the zero value is returned. +func (d *DateTime) Time() time.Time { + if d == nil || d.t == nil { + return time.Time{} + } + return *d.t +} + +// TimePtr returns a pointer to the DateTime's underlying time.Time, if any. +func (d *DateTime) TimePtr() *time.Time { + if d == nil || d.t == nil { + return nil + } + if d.t.IsZero() { + return nil + } + return d.t +} + +func (d *DateTime) MarshalJSON() ([]byte, error) { + if d == nil || d.t == nil { + return nil, nil + } + return json.Marshal(d.t.Format(time.RFC3339)) +} + +func (d *DateTime) UnmarshalJSON(data []byte) error { + var raw string + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + + parsedTime, err := time.Parse(time.RFC3339, raw) + if err != nil { + return err + } + + *d = DateTime{t: &parsedTime} + return nil +} diff --git a/seed/go-model/oauth-client-credentials-mandatory-auth/snippet.json b/seed/go-model/oauth-client-credentials-mandatory-auth/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/go-model/pagination/users.go b/seed/go-model/pagination/users.go index c0c3c17f7595..b6fd25d26e58 100644 --- a/seed/go-model/pagination/users.go +++ b/seed/go-model/pagination/users.go @@ -616,6 +616,82 @@ func (l *ListUsersPaginationResponse) String() string { return fmt.Sprintf("%#v", l) } +type ListUsersOptionalDataPaginationResponse struct { + HasNextPage *bool `json:"hasNextPage,omitempty" url:"hasNextPage,omitempty"` + Page *Page `json:"page,omitempty" url:"page,omitempty"` + // The totall number of /users + TotalCount int `json:"total_count" url:"total_count"` + Data []*User `json:"data,omitempty" url:"data,omitempty"` + + extraProperties map[string]any + rawJSON json.RawMessage +} + +func (l *ListUsersOptionalDataPaginationResponse) GetHasNextPage() *bool { + if l == nil { + return nil + } + return l.HasNextPage +} + +func (l *ListUsersOptionalDataPaginationResponse) GetPage() *Page { + if l == nil { + return nil + } + return l.Page +} + +func (l *ListUsersOptionalDataPaginationResponse) GetTotalCount() int { + if l == nil { + return 0 + } + return l.TotalCount +} + +func (l *ListUsersOptionalDataPaginationResponse) GetData() []*User { + if l == nil { + return nil + } + return l.Data +} + +func (l *ListUsersOptionalDataPaginationResponse) GetExtraProperties() map[string]any { + if l == nil { + return nil + } + return l.extraProperties +} + +func (l *ListUsersOptionalDataPaginationResponse) UnmarshalJSON( + data []byte, +) error { + type unmarshaler ListUsersOptionalDataPaginationResponse + var value unmarshaler + if err := json.Unmarshal(data, &value); err != nil { + return err + } + *l = ListUsersOptionalDataPaginationResponse(value) + extraProperties, err := internal.ExtractExtraProperties(data, *l) + if err != nil { + return err + } + l.extraProperties = extraProperties + l.rawJSON = json.RawMessage(data) + return nil +} + +func (l *ListUsersOptionalDataPaginationResponse) String() string { + if len(l.rawJSON) > 0 { + if value, err := internal.StringifyJSON(l.rawJSON); err == nil { + return value + } + } + if value, err := internal.StringifyJSON(l); err == nil { + return value + } + return fmt.Sprintf("%#v", l) +} + type ListUsersMixedTypePaginationResponse struct { Next string `json:"next" url:"next"` Data []*User `json:"data" url:"data"` diff --git a/seed/go-model/streaming/.fern/metadata.json b/seed/go-model/streaming/.fern/metadata.json new file mode 100644 index 000000000000..0feece7292ec --- /dev/null +++ b/seed/go-model/streaming/.fern/metadata.json @@ -0,0 +1,11 @@ +{ + "cliVersion": "DUMMY", + "generatorName": "fernapi/fern-go-model", + "generatorVersion": "latest", + "generatorConfig": { + "packageName": "stream", + "module": { + "path": "github.com/fern-api/stream-go" + } + } +} \ No newline at end of file diff --git a/seed/go-model/streaming/internal/extra_properties.go b/seed/go-model/streaming/internal/extra_properties.go new file mode 100644 index 000000000000..57517691f132 --- /dev/null +++ b/seed/go-model/streaming/internal/extra_properties.go @@ -0,0 +1,141 @@ +package internal + +import ( + "bytes" + "encoding/json" + "fmt" + "reflect" + "strings" +) + +// MarshalJSONWithExtraProperty marshals the given value to JSON, including the extra property. +func MarshalJSONWithExtraProperty(marshaler any, key string, value any) ([]byte, error) { + return MarshalJSONWithExtraProperties(marshaler, map[string]any{key: value}) +} + +// MarshalJSONWithExtraProperties marshals the given value to JSON, including any extra properties. +func MarshalJSONWithExtraProperties(marshaler any, extraProperties map[string]any) ([]byte, error) { + bytes, err := json.Marshal(marshaler) + if err != nil { + return nil, err + } + if len(extraProperties) == 0 { + return bytes, nil + } + keys, err := getKeys(marshaler) + if err != nil { + return nil, err + } + for _, key := range keys { + if _, ok := extraProperties[key]; ok { + return nil, fmt.Errorf("cannot add extra property %q because it is already defined on the type", key) + } + } + extraBytes, err := json.Marshal(extraProperties) + if err != nil { + return nil, err + } + if isEmptyJSON(bytes) { + if isEmptyJSON(extraBytes) { + return bytes, nil + } + return extraBytes, nil + } + result := bytes[:len(bytes)-1] + result = append(result, ',') + result = append(result, extraBytes[1:len(extraBytes)-1]...) + result = append(result, '}') + return result, nil +} + +// ExtractExtraProperties extracts any extra properties from the given value. +func ExtractExtraProperties(bytes []byte, value any, exclude ...string) (map[string]any, error) { + val := reflect.ValueOf(value) + for val.Kind() == reflect.Ptr { + if val.IsNil() { + return nil, fmt.Errorf("value must be non-nil to extract extra properties") + } + val = val.Elem() + } + if err := json.Unmarshal(bytes, &value); err != nil { + return nil, err + } + var extraProperties map[string]any + if err := json.Unmarshal(bytes, &extraProperties); err != nil { + return nil, err + } + for i := 0; i < val.Type().NumField(); i++ { + key := jsonKey(val.Type().Field(i)) + if key == "" || key == "-" { + continue + } + delete(extraProperties, key) + } + for _, key := range exclude { + delete(extraProperties, key) + } + if len(extraProperties) == 0 { + return nil, nil + } + return extraProperties, nil +} + +// getKeys returns the keys associated with the given value. The value must be a +// a struct or a map with string keys. +func getKeys(value any) ([]string, error) { + val := reflect.ValueOf(value) + if val.Kind() == reflect.Ptr { + val = val.Elem() + } + if !val.IsValid() { + return nil, nil + } + switch val.Kind() { + case reflect.Struct: + return getKeysForStructType(val.Type()), nil + case reflect.Map: + var keys []string + if val.Type().Key().Kind() != reflect.String { + return nil, fmt.Errorf("cannot extract keys from %T; only structs and maps with string keys are supported", value) + } + for _, key := range val.MapKeys() { + keys = append(keys, key.String()) + } + return keys, nil + default: + return nil, fmt.Errorf("cannot extract keys from %T; only structs and maps with string keys are supported", value) + } +} + +// getKeysForStructType returns all the keys associated with the given struct type, +// visiting embedded fields recursively. +func getKeysForStructType(structType reflect.Type) []string { + if structType.Kind() == reflect.Pointer { + structType = structType.Elem() + } + if structType.Kind() != reflect.Struct { + return nil + } + var keys []string + for i := 0; i < structType.NumField(); i++ { + field := structType.Field(i) + if field.Anonymous { + keys = append(keys, getKeysForStructType(field.Type)...) + continue + } + keys = append(keys, jsonKey(field)) + } + return keys +} + +// jsonKey returns the JSON key from the struct tag of the given field, +// excluding the omitempty flag (if any). +func jsonKey(field reflect.StructField) string { + return strings.TrimSuffix(field.Tag.Get("json"), ",omitempty") +} + +// isEmptyJSON returns true if the given data is empty, the empty JSON object, or +// an explicit null. +func isEmptyJSON(data []byte) bool { + return len(data) <= 2 || bytes.Equal(data, []byte("null")) +} diff --git a/seed/go-model/streaming/internal/extra_properties_test.go b/seed/go-model/streaming/internal/extra_properties_test.go new file mode 100644 index 000000000000..0d46257763fb --- /dev/null +++ b/seed/go-model/streaming/internal/extra_properties_test.go @@ -0,0 +1,228 @@ +package internal + +import ( + "encoding/json" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type testMarshaler struct { + Name string `json:"name"` + BirthDate time.Time `json:"birthDate"` + CreatedAt time.Time `json:"created_at"` +} + +func (t *testMarshaler) MarshalJSON() ([]byte, error) { + type embed testMarshaler + var marshaler = struct { + embed + BirthDate string `json:"birthDate"` + CreatedAt string `json:"created_at"` + }{ + embed: embed(*t), + BirthDate: t.BirthDate.Format("2006-01-02"), + CreatedAt: t.CreatedAt.Format(time.RFC3339), + } + return MarshalJSONWithExtraProperty(marshaler, "type", "test") +} + +func TestMarshalJSONWithExtraProperties(t *testing.T) { + tests := []struct { + desc string + giveMarshaler any + giveExtraProperties map[string]any + wantBytes []byte + wantError string + }{ + { + desc: "invalid type", + giveMarshaler: []string{"invalid"}, + giveExtraProperties: map[string]any{"key": "overwrite"}, + wantError: `cannot extract keys from []string; only structs and maps with string keys are supported`, + }, + { + desc: "invalid key type", + giveMarshaler: map[int]any{42: "value"}, + giveExtraProperties: map[string]any{"key": "overwrite"}, + wantError: `cannot extract keys from map[int]interface {}; only structs and maps with string keys are supported`, + }, + { + desc: "invalid map overwrite", + giveMarshaler: map[string]any{"key": "value"}, + giveExtraProperties: map[string]any{"key": "overwrite"}, + wantError: `cannot add extra property "key" because it is already defined on the type`, + }, + { + desc: "invalid struct overwrite", + giveMarshaler: new(testMarshaler), + giveExtraProperties: map[string]any{"birthDate": "2000-01-01"}, + wantError: `cannot add extra property "birthDate" because it is already defined on the type`, + }, + { + desc: "invalid struct overwrite embedded type", + giveMarshaler: new(testMarshaler), + giveExtraProperties: map[string]any{"name": "bob"}, + wantError: `cannot add extra property "name" because it is already defined on the type`, + }, + { + desc: "nil", + giveMarshaler: nil, + giveExtraProperties: nil, + wantBytes: []byte(`null`), + }, + { + desc: "empty", + giveMarshaler: map[string]any{}, + giveExtraProperties: map[string]any{}, + wantBytes: []byte(`{}`), + }, + { + desc: "no extra properties", + giveMarshaler: map[string]any{"key": "value"}, + giveExtraProperties: map[string]any{}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "only extra properties", + giveMarshaler: map[string]any{}, + giveExtraProperties: map[string]any{"key": "value"}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "single extra property", + giveMarshaler: map[string]any{"key": "value"}, + giveExtraProperties: map[string]any{"extra": "property"}, + wantBytes: []byte(`{"key":"value","extra":"property"}`), + }, + { + desc: "multiple extra properties", + giveMarshaler: map[string]any{"key": "value"}, + giveExtraProperties: map[string]any{"one": 1, "two": 2}, + wantBytes: []byte(`{"key":"value","one":1,"two":2}`), + }, + { + desc: "nested properties", + giveMarshaler: map[string]any{"key": "value"}, + giveExtraProperties: map[string]any{ + "user": map[string]any{ + "age": 42, + "name": "alice", + }, + }, + wantBytes: []byte(`{"key":"value","user":{"age":42,"name":"alice"}}`), + }, + { + desc: "multiple nested properties", + giveMarshaler: map[string]any{"key": "value"}, + giveExtraProperties: map[string]any{ + "metadata": map[string]any{ + "ip": "127.0.0.1", + }, + "user": map[string]any{ + "age": 42, + "name": "alice", + }, + }, + wantBytes: []byte(`{"key":"value","metadata":{"ip":"127.0.0.1"},"user":{"age":42,"name":"alice"}}`), + }, + { + desc: "custom marshaler", + giveMarshaler: &testMarshaler{ + Name: "alice", + BirthDate: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), + CreatedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC), + }, + giveExtraProperties: map[string]any{ + "extra": "property", + }, + wantBytes: []byte(`{"name":"alice","birthDate":"2000-01-01","created_at":"2024-01-01T00:00:00Z","type":"test","extra":"property"}`), + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + bytes, err := MarshalJSONWithExtraProperties(tt.giveMarshaler, tt.giveExtraProperties) + if tt.wantError != "" { + require.EqualError(t, err, tt.wantError) + assert.Nil(t, tt.wantBytes) + return + } + require.NoError(t, err) + assert.Equal(t, tt.wantBytes, bytes) + + value := make(map[string]any) + require.NoError(t, json.Unmarshal(bytes, &value)) + }) + } +} + +func TestExtractExtraProperties(t *testing.T) { + t.Run("none", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice"}`), value) + require.NoError(t, err) + assert.Nil(t, extraProperties) + }) + + t.Run("non-nil pointer", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]any{"age": float64(42)}, extraProperties) + }) + + t.Run("nil pointer", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + var value *user + _, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + assert.EqualError(t, err, "value must be non-nil to extract extra properties") + }) + + t.Run("non-zero value", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]any{"age": float64(42)}, extraProperties) + }) + + t.Run("zero value", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + var value user + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]any{"age": float64(42)}, extraProperties) + }) + + t.Run("exclude", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value, "age") + require.NoError(t, err) + assert.Nil(t, extraProperties) + }) +} diff --git a/seed/go-model/streaming/internal/stringer.go b/seed/go-model/streaming/internal/stringer.go new file mode 100644 index 000000000000..0be54d1b5359 --- /dev/null +++ b/seed/go-model/streaming/internal/stringer.go @@ -0,0 +1,13 @@ +package internal + +import "encoding/json" + +// StringifyJSON returns a pretty JSON string representation of +// the given value. +func StringifyJSON(value any) (string, error) { + bytes, err := json.MarshalIndent(value, "", " ") + if err != nil { + return "", err + } + return string(bytes), nil +} diff --git a/seed/go-model/streaming/internal/time.go b/seed/go-model/streaming/internal/time.go new file mode 100644 index 000000000000..ab0e269fade3 --- /dev/null +++ b/seed/go-model/streaming/internal/time.go @@ -0,0 +1,137 @@ +package internal + +import ( + "encoding/json" + "time" +) + +const dateFormat = "2006-01-02" + +// DateTime wraps time.Time and adapts its JSON representation +// to conform to a RFC3339 date (e.g. 2006-01-02). +// +// Ref: https://ijmacd.github.io/rfc3339-iso8601 +type Date struct { + t *time.Time +} + +// NewDate returns a new *Date. If the given time.Time +// is nil, nil will be returned. +func NewDate(t time.Time) *Date { + return &Date{t: &t} +} + +// NewOptionalDate returns a new *Date. If the given time.Time +// is nil, nil will be returned. +func NewOptionalDate(t *time.Time) *Date { + if t == nil { + return nil + } + return &Date{t: t} +} + +// Time returns the Date's underlying time, if any. If the +// date is nil, the zero value is returned. +func (d *Date) Time() time.Time { + if d == nil || d.t == nil { + return time.Time{} + } + return *d.t +} + +// TimePtr returns a pointer to the Date's underlying time.Time, if any. +func (d *Date) TimePtr() *time.Time { + if d == nil || d.t == nil { + return nil + } + if d.t.IsZero() { + return nil + } + return d.t +} + +func (d *Date) MarshalJSON() ([]byte, error) { + if d == nil || d.t == nil { + return nil, nil + } + return json.Marshal(d.t.Format(dateFormat)) +} + +func (d *Date) UnmarshalJSON(data []byte) error { + var raw string + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + + parsedTime, err := time.Parse(dateFormat, raw) + if err != nil { + return err + } + + *d = Date{t: &parsedTime} + return nil +} + +// DateTime wraps time.Time and adapts its JSON representation +// to conform to a RFC3339 date-time (e.g. 2017-07-21T17:32:28Z). +// +// Ref: https://ijmacd.github.io/rfc3339-iso8601 +type DateTime struct { + t *time.Time +} + +// NewDateTime returns a new *DateTime. +func NewDateTime(t time.Time) *DateTime { + return &DateTime{t: &t} +} + +// NewOptionalDateTime returns a new *DateTime. If the given time.Time +// is nil, nil will be returned. +func NewOptionalDateTime(t *time.Time) *DateTime { + if t == nil { + return nil + } + return &DateTime{t: t} +} + +// Time returns the DateTime's underlying time, if any. If the +// date-time is nil, the zero value is returned. +func (d *DateTime) Time() time.Time { + if d == nil || d.t == nil { + return time.Time{} + } + return *d.t +} + +// TimePtr returns a pointer to the DateTime's underlying time.Time, if any. +func (d *DateTime) TimePtr() *time.Time { + if d == nil || d.t == nil { + return nil + } + if d.t.IsZero() { + return nil + } + return d.t +} + +func (d *DateTime) MarshalJSON() ([]byte, error) { + if d == nil || d.t == nil { + return nil, nil + } + return json.Marshal(d.t.Format(time.RFC3339)) +} + +func (d *DateTime) UnmarshalJSON(data []byte) error { + var raw string + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + + parsedTime, err := time.Parse(time.RFC3339, raw) + if err != nil { + return err + } + + *d = DateTime{t: &parsedTime} + return nil +} diff --git a/seed/go-sdk/any-auth/client/client.go b/seed/go-sdk/any-auth/client/client.go index 01c1d0c32453..cf322885a82d 100644 --- a/seed/go-sdk/any-auth/client/client.go +++ b/seed/go-sdk/any-auth/client/client.go @@ -3,6 +3,9 @@ package client import ( + context "context" + errors "errors" + fern "github.com/any-auth/fern" auth "github.com/any-auth/fern/auth" core "github.com/any-auth/fern/core" internal "github.com/any-auth/fern/internal" @@ -34,6 +37,35 @@ func NewClient(opts ...option.RequestOption) *Client { if options.ClientSecret == "" { options.ClientSecret = os.Getenv("MY_CLIENT_SECRET") } + oauthTokenProvider := core.NewOAuthTokenProvider( + options.ClientID, + options.ClientSecret, + ) + authOptions := *options + authClient := auth.NewClient( + &authOptions, + ) + options.SetTokenGetter(func() (string, error) { + return oauthTokenProvider.GetOrFetch(func() (string, int, error) { + response, err := authClient.GetToken(context.Background(), &fern.GetTokenRequest{ + ClientId: options.ClientID, + ClientSecret: options.ClientSecret, + }) + if err != nil { + return "", 0, err + } + if response.AccessToken == "" { + return "", 0, errors.New( + "oauth response missing access token", + ) + } + expiresIn := core.DefaultExpirySeconds + if response.ExpiresIn > 0 { + expiresIn = response.ExpiresIn + } + return response.AccessToken, expiresIn, nil + }) + }) return &Client{ Auth: auth.NewClient(options), User: user.NewClient(options), diff --git a/seed/go-sdk/any-auth/core/oauth.go b/seed/go-sdk/any-auth/core/oauth.go index 114b1f412ec7..9b6f34cc8e79 100644 --- a/seed/go-sdk/any-auth/core/oauth.go +++ b/seed/go-sdk/any-auth/core/oauth.go @@ -9,6 +9,9 @@ const ( // expirationBufferMinutes is subtracted from the token expiration time // to ensure we refresh the token before it actually expires. expirationBufferMinutes = 2 + + // DefaultExpirySeconds is used when the OAuth response doesn't include an expires_in value. + DefaultExpirySeconds = 3600 // 1 hour fallback ) // OAuthTokenProvider manages OAuth access tokens, including caching and automatic refresh. @@ -19,6 +22,9 @@ type OAuthTokenProvider struct { mu sync.Mutex accessToken string expiresAt time.Time + + // fetchMu ensures only one goroutine fetches a new token at a time + fetchMu sync.Mutex } // NewOAuthTokenProvider creates a new OAuthTokenProvider with the given credentials. @@ -73,6 +79,34 @@ func (o *OAuthTokenProvider) GetToken() string { return o.accessToken } +// GetOrFetch returns a valid token, fetching a new one if necessary. +// The fetchFunc is called at most once even if multiple goroutines call GetOrFetch +// concurrently when the token is expired. It should return (accessToken, expiresInSeconds, error). +func (o *OAuthTokenProvider) GetOrFetch(fetchFunc func() (string, int, error)) (string, error) { + // Fast path: check if we have a valid token + if token := o.GetToken(); token != "" { + return token, nil + } + + // Slow path: acquire fetch lock to ensure only one goroutine fetches + o.fetchMu.Lock() + defer o.fetchMu.Unlock() + + // Double-check after acquiring lock (another goroutine may have fetched) + if token := o.GetToken(); token != "" { + return token, nil + } + + // Fetch new token + accessToken, expiresIn, err := fetchFunc() + if err != nil { + return "", err + } + + o.SetToken(accessToken, expiresIn) + return accessToken, nil +} + // NeedsRefresh returns true if the token needs to be refreshed. func (o *OAuthTokenProvider) NeedsRefresh() bool { o.mu.Lock() diff --git a/seed/go-sdk/any-auth/core/request_option.go b/seed/go-sdk/any-auth/core/request_option.go index a5ec5b64cff8..8ee0db95099a 100644 --- a/seed/go-sdk/any-auth/core/request_option.go +++ b/seed/go-sdk/any-auth/core/request_option.go @@ -13,6 +13,9 @@ type RequestOption interface { applyRequestOptions(*RequestOptions) } +// TokenGetter is a function that returns an access token. +type TokenGetter func() (string, error) + // RequestOptions defines all of the possible request options. // // This type is primarily used by the generated code and is not meant @@ -24,6 +27,7 @@ type RequestOptions struct { BodyProperties map[string]interface{} QueryParameters url.Values MaxAttempts uint + tokenGetter TokenGetter Token string ApiKey string ClientID string @@ -168,3 +172,9 @@ func (c *ClientCredentialsOption) applyRequestOptions(opts *RequestOptions) { opts.ClientID = c.ClientID opts.ClientSecret = c.ClientSecret } + +// SetTokenGetter sets the token getter function for OAuth. +// This is an internal method and should not be called directly. +func (r *RequestOptions) SetTokenGetter(getter TokenGetter) { + r.tokenGetter = getter +} diff --git a/seed/go-sdk/idempotency-headers/.fern/metadata.json b/seed/go-sdk/idempotency-headers/.fern/metadata.json new file mode 100644 index 000000000000..66bd514ed2c0 --- /dev/null +++ b/seed/go-sdk/idempotency-headers/.fern/metadata.json @@ -0,0 +1,13 @@ +{ + "cliVersion": "DUMMY", + "generatorName": "fernapi/fern-go-sdk", + "generatorVersion": "latest", + "generatorConfig": { + "enableWireTests": false, + "packageName": "fern", + "module": { + "path": "github.com/idempotency-headers/fern" + }, + "includeLegacyClientOptions": true + } +} \ No newline at end of file diff --git a/seed/go-sdk/idempotency-headers/.github/workflows/ci.yml b/seed/go-sdk/idempotency-headers/.github/workflows/ci.yml new file mode 100644 index 000000000000..56310d69624b --- /dev/null +++ b/seed/go-sdk/idempotency-headers/.github/workflows/ci.yml @@ -0,0 +1,35 @@ +name: ci + +on: [push] + +jobs: + compile: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up go + uses: actions/setup-go@v4 + + - name: Compile + run: go build ./... + test: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up go + uses: actions/setup-go@v4 + + - name: Setup wiremock server + run: | + if [ -f wiremock/docker-compose.test.yml ]; then docker compose -f wiremock/docker-compose.test.yml down && docker compose -f wiremock/docker-compose.test.yml up -d; fi + + - name: Test + run: go test ./... + + - name: Teardown wiremock server + run: | + if [ -f wiremock/docker-compose.test.yml ]; then docker compose -f wiremock/docker-compose.test.yml down; fi diff --git a/seed/go-sdk/idempotency-headers/client/client.go b/seed/go-sdk/idempotency-headers/client/client.go new file mode 100644 index 000000000000..df29cd1e96bd --- /dev/null +++ b/seed/go-sdk/idempotency-headers/client/client.go @@ -0,0 +1,33 @@ +// Code generated by Fern. DO NOT EDIT. + +package client + +import ( + core "github.com/idempotency-headers/fern/core" + internal "github.com/idempotency-headers/fern/internal" + option "github.com/idempotency-headers/fern/option" + payment "github.com/idempotency-headers/fern/payment" +) + +type Client struct { + Payment *payment.Client + + options *core.RequestOptions + baseURL string + caller *internal.Caller +} + +func NewClient(opts ...option.RequestOption) *Client { + options := core.NewRequestOptions(opts...) + return &Client{ + Payment: payment.NewClient(options), + options: options, + baseURL: options.BaseURL, + caller: internal.NewCaller( + &internal.CallerParams{ + Client: options.HTTPClient, + MaxAttempts: options.MaxAttempts, + }, + ), + } +} diff --git a/seed/go-sdk/idempotency-headers/client/client_test.go b/seed/go-sdk/idempotency-headers/client/client_test.go new file mode 100644 index 000000000000..984e654452ff --- /dev/null +++ b/seed/go-sdk/idempotency-headers/client/client_test.go @@ -0,0 +1,45 @@ +// Code generated by Fern. DO NOT EDIT. + +package client + +import ( + option "github.com/idempotency-headers/fern/option" + assert "github.com/stretchr/testify/assert" + http "net/http" + testing "testing" + time "time" +) + +func TestNewClient(t *testing.T) { + t.Run("default", func(t *testing.T) { + c := NewClient() + assert.Empty(t, c.baseURL) + }) + + t.Run("base url", func(t *testing.T) { + c := NewClient( + option.WithBaseURL("test.co"), + ) + assert.Equal(t, "test.co", c.baseURL) + }) + + t.Run("http client", func(t *testing.T) { + httpClient := &http.Client{ + Timeout: 5 * time.Second, + } + c := NewClient( + option.WithHTTPClient(httpClient), + ) + assert.Empty(t, c.baseURL) + }) + + t.Run("http header", func(t *testing.T) { + header := make(http.Header) + header.Set("X-API-Tenancy", "test") + c := NewClient( + option.WithHTTPHeader(header), + ) + assert.Empty(t, c.baseURL) + assert.Equal(t, "test", c.options.HTTPHeader.Get("X-API-Tenancy")) + }) +} diff --git a/seed/go-sdk/idempotency-headers/client/options.go b/seed/go-sdk/idempotency-headers/client/options.go new file mode 100644 index 000000000000..1a2d4df5ad59 --- /dev/null +++ b/seed/go-sdk/idempotency-headers/client/options.go @@ -0,0 +1,45 @@ +// Code generated by Fern. DO NOT EDIT. + +package client + +import ( + core "github.com/idempotency-headers/fern/core" + option "github.com/idempotency-headers/fern/option" + http "net/http" +) + +// WithBaseURL sets the base URL, overriding the default +// environment, if any. +func WithBaseURL(baseURL string) *core.BaseURLOption { + return option.WithBaseURL(baseURL) +} + +// WithHTTPClient uses the given HTTPClient to issue the request. +func WithHTTPClient(httpClient core.HTTPClient) *core.HTTPClientOption { + return option.WithHTTPClient(httpClient) +} + +// WithHTTPHeader adds the given http.Header to the request. +func WithHTTPHeader(httpHeader http.Header) *core.HTTPHeaderOption { + return option.WithHTTPHeader(httpHeader) +} + +// WithMaxAttempts configures the maximum number of retry attempts. +func WithMaxAttempts(attempts uint) *core.MaxAttemptsOption { + return option.WithMaxAttempts(attempts) +} + +// WithToken sets the 'Authorization: Bearer ' request header. +func WithToken(token string) *core.TokenOption { + return option.WithToken(token) +} + +// WithIdempotencyKey sets the idempotencyKey request header. +func WithIdempotencyKey(idempotencyKey string) *core.IdempotencyKeyOption { + return option.WithIdempotencyKey(idempotencyKey) +} + +// WithIdempotencyExpiration sets the idempotencyExpiration request header. +func WithIdempotencyExpiration(idempotencyExpiration int) *core.IdempotencyExpirationOption { + return option.WithIdempotencyExpiration(idempotencyExpiration) +} diff --git a/seed/go-sdk/idempotency-headers/core/api_error.go b/seed/go-sdk/idempotency-headers/core/api_error.go new file mode 100644 index 000000000000..6168388541b4 --- /dev/null +++ b/seed/go-sdk/idempotency-headers/core/api_error.go @@ -0,0 +1,47 @@ +package core + +import ( + "fmt" + "net/http" +) + +// APIError is a lightweight wrapper around the standard error +// interface that preserves the status code from the RPC, if any. +type APIError struct { + err error + + StatusCode int `json:"-"` + Header http.Header `json:"-"` +} + +// NewAPIError constructs a new API error. +func NewAPIError(statusCode int, header http.Header, err error) *APIError { + return &APIError{ + err: err, + Header: header, + StatusCode: statusCode, + } +} + +// Unwrap returns the underlying error. This also makes the error compatible +// with errors.As and errors.Is. +func (a *APIError) Unwrap() error { + if a == nil { + return nil + } + return a.err +} + +// Error returns the API error's message. +func (a *APIError) Error() string { + if a == nil || (a.err == nil && a.StatusCode == 0) { + return "" + } + if a.err == nil { + return fmt.Sprintf("%d", a.StatusCode) + } + if a.StatusCode == 0 { + return a.err.Error() + } + return fmt.Sprintf("%d: %s", a.StatusCode, a.err.Error()) +} diff --git a/seed/go-sdk/idempotency-headers/core/http.go b/seed/go-sdk/idempotency-headers/core/http.go new file mode 100644 index 000000000000..92c435692940 --- /dev/null +++ b/seed/go-sdk/idempotency-headers/core/http.go @@ -0,0 +1,15 @@ +package core + +import "net/http" + +// HTTPClient is an interface for a subset of the *http.Client. +type HTTPClient interface { + Do(*http.Request) (*http.Response, error) +} + +// Response is an HTTP response from an HTTP client. +type Response[T any] struct { + StatusCode int + Header http.Header + Body T +} diff --git a/seed/go-sdk/idempotency-headers/core/idempotent_request_option.go b/seed/go-sdk/idempotency-headers/core/idempotent_request_option.go new file mode 100644 index 000000000000..48e4e5628411 --- /dev/null +++ b/seed/go-sdk/idempotency-headers/core/idempotent_request_option.go @@ -0,0 +1,72 @@ +// Code generated by Fern. DO NOT EDIT. + +package core + +import ( + fmt "fmt" + http "net/http" +) + +// IdempotentRequestOption adapts the behavior of an individual request. +type IdempotentRequestOption interface { + applyIdempotentRequestOptions(*IdempotentRequestOptions) +} + +// IdempotentRequestOptions defines all of the possible idempotent request options. +// +// This type is primarily used by the generated code and is not meant +// to be used directly; use the option package instead. +type IdempotentRequestOptions struct { + *RequestOptions + + IdempotencyKey string + IdempotencyExpiration int +} + +// NewIdempotentRequestOptions returns a new *IdempotentRequestOptions value. +// +// This function is primarily used by the generated code and is not meant +// to be used directly; use IdempotentRequestOption instead. +func NewIdempotentRequestOptions(opts ...IdempotentRequestOption) *IdempotentRequestOptions { + options := &IdempotentRequestOptions{ + RequestOptions: NewRequestOptions(), + } + for _, opt := range opts { + if requestOption, ok := opt.(RequestOption); ok { + requestOption.applyRequestOptions(options.RequestOptions) + } + opt.applyIdempotentRequestOptions(options) + } + return options +} + +// IdempotencyKeyOption implements the RequestOption interface. +type IdempotencyKeyOption struct { + IdempotencyKey string +} + +func (i *IdempotencyKeyOption) applyIdempotentRequestOptions(opts *IdempotentRequestOptions) { + opts.IdempotencyKey = i.IdempotencyKey +} + +// IdempotencyExpirationOption implements the RequestOption interface. +type IdempotencyExpirationOption struct { + IdempotencyExpiration int +} + +func (i *IdempotencyExpirationOption) applyIdempotentRequestOptions(opts *IdempotentRequestOptions) { + opts.IdempotencyExpiration = i.IdempotencyExpiration +} + +// ToHeader maps the configured request options into a http.Header used +// for the request. +func (i *IdempotentRequestOptions) ToHeader() http.Header { + header := i.RequestOptions.ToHeader() + if i.IdempotencyKey != "" { + header.Set("Idempotency-Key", fmt.Sprintf("%v", i.IdempotencyKey)) + } + if i.IdempotencyExpiration != 0 { + header.Set("Idempotency-Expiration", fmt.Sprintf("%v", i.IdempotencyExpiration)) + } + return header +} diff --git a/seed/go-sdk/idempotency-headers/core/request_option.go b/seed/go-sdk/idempotency-headers/core/request_option.go new file mode 100644 index 000000000000..99b12cc2f920 --- /dev/null +++ b/seed/go-sdk/idempotency-headers/core/request_option.go @@ -0,0 +1,153 @@ +// Code generated by Fern. DO NOT EDIT. + +package core + +import ( + http "net/http" + url "net/url" +) + +// RequestOption adapts the behavior of the client or an individual request. +type RequestOption interface { + applyRequestOptions(*RequestOptions) +} + +// RequestOptions defines all of the possible request options. +// +// This type is primarily used by the generated code and is not meant +// to be used directly; use the option package instead. +type RequestOptions struct { + BaseURL string + HTTPClient HTTPClient + HTTPHeader http.Header + BodyProperties map[string]interface{} + QueryParameters url.Values + MaxAttempts uint + Token string +} + +// NewRequestOptions returns a new *RequestOptions value. +// +// This function is primarily used by the generated code and is not meant +// to be used directly; use RequestOption instead. +func NewRequestOptions(opts ...RequestOption) *RequestOptions { + options := &RequestOptions{ + HTTPHeader: make(http.Header), + BodyProperties: make(map[string]interface{}), + QueryParameters: make(url.Values), + } + for _, opt := range opts { + opt.applyRequestOptions(options) + } + return options +} + +// ToHeader maps the configured request options into a http.Header used +// for the request(s). +func (r *RequestOptions) ToHeader() http.Header { + header := r.cloneHeader() + if r.Token != "" { + header.Set("Authorization", "Bearer "+r.Token) + } + return header +} + +func (r *RequestOptions) cloneHeader() http.Header { + headers := r.HTTPHeader.Clone() + headers.Set("X-Fern-Language", "Go") + headers.Set("X-Fern-SDK-Name", "github.com/idempotency-headers/fern") + headers.Set("X-Fern-SDK-Version", "v0.0.1") + headers.Set("User-Agent", "github.com/idempotency-headers/fern/0.0.1") + return headers +} + +// BaseURLOption implements the RequestOption interface. +type BaseURLOption struct { + BaseURL string +} + +func (b *BaseURLOption) applyRequestOptions(opts *RequestOptions) { + opts.BaseURL = b.BaseURL +} + +func (b *BaseURLOption) applyIdempotentRequestOptions(opts *IdempotentRequestOptions) { + opts.BaseURL = b.BaseURL +} + +// HTTPClientOption implements the RequestOption interface. +type HTTPClientOption struct { + HTTPClient HTTPClient +} + +func (h *HTTPClientOption) applyRequestOptions(opts *RequestOptions) { + opts.HTTPClient = h.HTTPClient +} + +func (h *HTTPClientOption) applyIdempotentRequestOptions(opts *IdempotentRequestOptions) { + opts.HTTPClient = h.HTTPClient +} + +// HTTPHeaderOption implements the RequestOption interface. +type HTTPHeaderOption struct { + HTTPHeader http.Header +} + +func (h *HTTPHeaderOption) applyRequestOptions(opts *RequestOptions) { + opts.HTTPHeader = h.HTTPHeader +} + +func (h *HTTPHeaderOption) applyIdempotentRequestOptions(opts *IdempotentRequestOptions) { + opts.HTTPHeader = h.HTTPHeader +} + +// BodyPropertiesOption implements the RequestOption interface. +type BodyPropertiesOption struct { + BodyProperties map[string]interface{} +} + +func (b *BodyPropertiesOption) applyRequestOptions(opts *RequestOptions) { + opts.BodyProperties = b.BodyProperties +} + +func (b *BodyPropertiesOption) applyIdempotentRequestOptions(opts *IdempotentRequestOptions) { + opts.BodyProperties = b.BodyProperties +} + +// QueryParametersOption implements the RequestOption interface. +type QueryParametersOption struct { + QueryParameters url.Values +} + +func (q *QueryParametersOption) applyRequestOptions(opts *RequestOptions) { + opts.QueryParameters = q.QueryParameters +} + +func (q *QueryParametersOption) applyIdempotentRequestOptions(opts *IdempotentRequestOptions) { + opts.QueryParameters = q.QueryParameters +} + +// MaxAttemptsOption implements the RequestOption interface. +type MaxAttemptsOption struct { + MaxAttempts uint +} + +func (m *MaxAttemptsOption) applyRequestOptions(opts *RequestOptions) { + opts.MaxAttempts = m.MaxAttempts +} + +func (m *MaxAttemptsOption) applyIdempotentRequestOptions(opts *IdempotentRequestOptions) { + opts.MaxAttempts = m.MaxAttempts +} + +// TokenOption implements the RequestOption interface. +type TokenOption struct { + Token string +} + +func (t *TokenOption) applyRequestOptions(opts *RequestOptions) { + opts.Token = t.Token +} + +func (t *TokenOption) applyIdempotentRequestOptions(opts *IdempotentRequestOptions) { + opts.Token = t.Token +} diff --git a/seed/go-sdk/idempotency-headers/dynamic-snippets/example0/snippet.go b/seed/go-sdk/idempotency-headers/dynamic-snippets/example0/snippet.go new file mode 100644 index 000000000000..a4d13a04d72e --- /dev/null +++ b/seed/go-sdk/idempotency-headers/dynamic-snippets/example0/snippet.go @@ -0,0 +1,27 @@ +package example + +import ( + client "github.com/idempotency-headers/fern/client" + option "github.com/idempotency-headers/fern/option" + fern "github.com/idempotency-headers/fern" + context "context" +) + +func do() { + client := client.NewClient( + option.WithBaseURL( + "https://api.fern.com", + ), + option.WithToken( + "", + ), + ) + request := &fern.CreatePaymentRequest{ + Amount: 1, + Currency: fern.CurrencyUsd, + } + client.Payment.Create( + context.TODO(), + request, + ) +} diff --git a/seed/go-sdk/idempotency-headers/dynamic-snippets/example1/snippet.go b/seed/go-sdk/idempotency-headers/dynamic-snippets/example1/snippet.go new file mode 100644 index 000000000000..45e01220fe25 --- /dev/null +++ b/seed/go-sdk/idempotency-headers/dynamic-snippets/example1/snippet.go @@ -0,0 +1,22 @@ +package example + +import ( + client "github.com/idempotency-headers/fern/client" + option "github.com/idempotency-headers/fern/option" + context "context" +) + +func do() { + client := client.NewClient( + option.WithBaseURL( + "https://api.fern.com", + ), + option.WithToken( + "", + ), + ) + client.Payment.Delete( + context.TODO(), + "paymentId", + ) +} diff --git a/seed/go-sdk/idempotency-headers/internal/caller.go b/seed/go-sdk/idempotency-headers/internal/caller.go new file mode 100644 index 000000000000..6cc9c680f1bb --- /dev/null +++ b/seed/go-sdk/idempotency-headers/internal/caller.go @@ -0,0 +1,250 @@ +package internal + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "reflect" + "strings" + + "github.com/idempotency-headers/fern/core" +) + +const ( + // contentType specifies the JSON Content-Type header value. + contentType = "application/json" + contentTypeHeader = "Content-Type" +) + +// Caller calls APIs and deserializes their response, if any. +type Caller struct { + client core.HTTPClient + retrier *Retrier +} + +// CallerParams represents the parameters used to constrcut a new *Caller. +type CallerParams struct { + Client core.HTTPClient + MaxAttempts uint +} + +// NewCaller returns a new *Caller backed by the given parameters. +func NewCaller(params *CallerParams) *Caller { + var httpClient core.HTTPClient = http.DefaultClient + if params.Client != nil { + httpClient = params.Client + } + var retryOptions []RetryOption + if params.MaxAttempts > 0 { + retryOptions = append(retryOptions, WithMaxAttempts(params.MaxAttempts)) + } + return &Caller{ + client: httpClient, + retrier: NewRetrier(retryOptions...), + } +} + +// CallParams represents the parameters used to issue an API call. +type CallParams struct { + URL string + Method string + MaxAttempts uint + Headers http.Header + BodyProperties map[string]interface{} + QueryParameters url.Values + Client core.HTTPClient + Request interface{} + Response interface{} + ResponseIsOptional bool + ErrorDecoder ErrorDecoder +} + +// CallResponse is a parsed HTTP response from an API call. +type CallResponse struct { + StatusCode int + Header http.Header +} + +// Call issues an API call according to the given call parameters. +func (c *Caller) Call(ctx context.Context, params *CallParams) (*CallResponse, error) { + url := buildURL(params.URL, params.QueryParameters) + req, err := newRequest( + ctx, + url, + params.Method, + params.Headers, + params.Request, + params.BodyProperties, + ) + if err != nil { + return nil, err + } + + // If the call has been cancelled, don't issue the request. + if err := ctx.Err(); err != nil { + return nil, err + } + + client := c.client + if params.Client != nil { + // Use the HTTP client scoped to the request. + client = params.Client + } + + var retryOptions []RetryOption + if params.MaxAttempts > 0 { + retryOptions = append(retryOptions, WithMaxAttempts(params.MaxAttempts)) + } + + resp, err := c.retrier.Run( + client.Do, + req, + params.ErrorDecoder, + retryOptions..., + ) + if err != nil { + return nil, err + } + + // Close the response body after we're done. + defer resp.Body.Close() + + // Check if the call was cancelled before we return the error + // associated with the call and/or unmarshal the response data. + if err := ctx.Err(); err != nil { + return nil, err + } + + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + return nil, decodeError(resp, params.ErrorDecoder) + } + + // Mutate the response parameter in-place. + if params.Response != nil { + if writer, ok := params.Response.(io.Writer); ok { + _, err = io.Copy(writer, resp.Body) + } else { + err = json.NewDecoder(resp.Body).Decode(params.Response) + } + if err != nil { + if err == io.EOF { + if params.ResponseIsOptional { + // The response is optional, so we should ignore the + // io.EOF error + return &CallResponse{ + StatusCode: resp.StatusCode, + Header: resp.Header, + }, nil + } + return nil, fmt.Errorf("expected a %T response, but the server responded with nothing", params.Response) + } + return nil, err + } + } + + return &CallResponse{ + StatusCode: resp.StatusCode, + Header: resp.Header, + }, nil +} + +// buildURL constructs the final URL by appending the given query parameters (if any). +func buildURL( + url string, + queryParameters url.Values, +) string { + if len(queryParameters) == 0 { + return url + } + if strings.ContainsRune(url, '?') { + url += "&" + } else { + url += "?" + } + url += queryParameters.Encode() + return url +} + +// newRequest returns a new *http.Request with all of the fields +// required to issue the call. +func newRequest( + ctx context.Context, + url string, + method string, + endpointHeaders http.Header, + request interface{}, + bodyProperties map[string]interface{}, +) (*http.Request, error) { + requestBody, err := newRequestBody(request, bodyProperties) + if err != nil { + return nil, err + } + req, err := http.NewRequestWithContext(ctx, method, url, requestBody) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + req.Header.Set(contentTypeHeader, contentType) + for name, values := range endpointHeaders { + req.Header[name] = values + } + return req, nil +} + +// newRequestBody returns a new io.Reader that represents the HTTP request body. +func newRequestBody(request interface{}, bodyProperties map[string]interface{}) (io.Reader, error) { + if isNil(request) { + if len(bodyProperties) == 0 { + return nil, nil + } + requestBytes, err := json.Marshal(bodyProperties) + if err != nil { + return nil, err + } + return bytes.NewReader(requestBytes), nil + } + if body, ok := request.(io.Reader); ok { + return body, nil + } + requestBytes, err := MarshalJSONWithExtraProperties(request, bodyProperties) + if err != nil { + return nil, err + } + return bytes.NewReader(requestBytes), nil +} + +// decodeError decodes the error from the given HTTP response. Note that +// it's the caller's responsibility to close the response body. +func decodeError(response *http.Response, errorDecoder ErrorDecoder) error { + if errorDecoder != nil { + // This endpoint has custom errors, so we'll + // attempt to unmarshal the error into a structured + // type based on the status code. + return errorDecoder(response.StatusCode, response.Header, response.Body) + } + // This endpoint doesn't have any custom error + // types, so we just read the body as-is, and + // put it into a normal error. + bytes, err := io.ReadAll(response.Body) + if err != nil && err != io.EOF { + return err + } + if err == io.EOF { + // The error didn't have a response body, + // so all we can do is return an error + // with the status code. + return core.NewAPIError(response.StatusCode, response.Header, nil) + } + return core.NewAPIError(response.StatusCode, response.Header, errors.New(string(bytes))) +} + +// isNil is used to determine if the request value is equal to nil (i.e. an interface +// value that holds a nil concrete value is itself non-nil). +func isNil(value interface{}) bool { + return value == nil || reflect.ValueOf(value).IsNil() +} diff --git a/seed/go-sdk/idempotency-headers/internal/caller_test.go b/seed/go-sdk/idempotency-headers/internal/caller_test.go new file mode 100644 index 000000000000..6a9c0950126d --- /dev/null +++ b/seed/go-sdk/idempotency-headers/internal/caller_test.go @@ -0,0 +1,395 @@ +package internal + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "net/http/httptest" + "net/url" + "strconv" + "testing" + + "github.com/idempotency-headers/fern/core" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// InternalTestCase represents a single test case. +type InternalTestCase struct { + description string + + // Server-side assertions. + givePathSuffix string + giveMethod string + giveResponseIsOptional bool + giveHeader http.Header + giveErrorDecoder ErrorDecoder + giveRequest *InternalTestRequest + giveQueryParams url.Values + giveBodyProperties map[string]interface{} + + // Client-side assertions. + wantResponse *InternalTestResponse + wantHeaders http.Header + wantError error +} + +// InternalTestRequest a simple request body. +type InternalTestRequest struct { + Id string `json:"id"` +} + +// InternalTestResponse a simple response body. +type InternalTestResponse struct { + Id string `json:"id"` + ExtraBodyProperties map[string]interface{} `json:"extraBodyProperties,omitempty"` + QueryParameters url.Values `json:"queryParameters,omitempty"` +} + +// InternalTestNotFoundError represents a 404. +type InternalTestNotFoundError struct { + *core.APIError + + Message string `json:"message"` +} + +func TestCall(t *testing.T) { + tests := []*InternalTestCase{ + { + description: "GET success", + giveMethod: http.MethodGet, + giveHeader: http.Header{ + "X-API-Status": []string{"success"}, + }, + giveRequest: &InternalTestRequest{ + Id: "123", + }, + wantResponse: &InternalTestResponse{ + Id: "123", + }, + }, + { + description: "GET success with query", + givePathSuffix: "?limit=1", + giveMethod: http.MethodGet, + giveHeader: http.Header{ + "X-API-Status": []string{"success"}, + }, + giveRequest: &InternalTestRequest{ + Id: "123", + }, + wantResponse: &InternalTestResponse{ + Id: "123", + QueryParameters: url.Values{ + "limit": []string{"1"}, + }, + }, + }, + { + description: "GET not found", + giveMethod: http.MethodGet, + giveHeader: http.Header{ + "X-API-Status": []string{"fail"}, + }, + giveRequest: &InternalTestRequest{ + Id: strconv.Itoa(http.StatusNotFound), + }, + giveErrorDecoder: newTestErrorDecoder(t), + wantError: &InternalTestNotFoundError{ + APIError: core.NewAPIError( + http.StatusNotFound, + http.Header{}, + errors.New(`{"message":"ID \"404\" not found"}`), + ), + }, + }, + { + description: "POST empty body", + giveMethod: http.MethodPost, + giveHeader: http.Header{ + "X-API-Status": []string{"fail"}, + }, + giveRequest: nil, + wantError: core.NewAPIError( + http.StatusBadRequest, + http.Header{}, + errors.New("invalid request"), + ), + }, + { + description: "POST optional response", + giveMethod: http.MethodPost, + giveHeader: http.Header{ + "X-API-Status": []string{"success"}, + }, + giveRequest: &InternalTestRequest{ + Id: "123", + }, + giveResponseIsOptional: true, + }, + { + description: "POST API error", + giveMethod: http.MethodPost, + giveHeader: http.Header{ + "X-API-Status": []string{"fail"}, + }, + giveRequest: &InternalTestRequest{ + Id: strconv.Itoa(http.StatusInternalServerError), + }, + wantError: core.NewAPIError( + http.StatusInternalServerError, + http.Header{}, + errors.New("failed to process request"), + ), + }, + { + description: "POST extra properties", + giveMethod: http.MethodPost, + giveHeader: http.Header{ + "X-API-Status": []string{"success"}, + }, + giveRequest: new(InternalTestRequest), + giveBodyProperties: map[string]interface{}{ + "key": "value", + }, + wantResponse: &InternalTestResponse{ + ExtraBodyProperties: map[string]interface{}{ + "key": "value", + }, + }, + }, + { + description: "GET extra query parameters", + giveMethod: http.MethodGet, + giveHeader: http.Header{ + "X-API-Status": []string{"success"}, + }, + giveQueryParams: url.Values{ + "extra": []string{"true"}, + }, + giveRequest: &InternalTestRequest{ + Id: "123", + }, + wantResponse: &InternalTestResponse{ + Id: "123", + QueryParameters: url.Values{ + "extra": []string{"true"}, + }, + }, + }, + { + description: "GET merge extra query parameters", + givePathSuffix: "?limit=1", + giveMethod: http.MethodGet, + giveHeader: http.Header{ + "X-API-Status": []string{"success"}, + }, + giveRequest: &InternalTestRequest{ + Id: "123", + }, + giveQueryParams: url.Values{ + "extra": []string{"true"}, + }, + wantResponse: &InternalTestResponse{ + Id: "123", + QueryParameters: url.Values{ + "limit": []string{"1"}, + "extra": []string{"true"}, + }, + }, + }, + } + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + var ( + server = newTestServer(t, test) + client = server.Client() + ) + caller := NewCaller( + &CallerParams{ + Client: client, + }, + ) + var response *InternalTestResponse + _, err := caller.Call( + context.Background(), + &CallParams{ + URL: server.URL + test.givePathSuffix, + Method: test.giveMethod, + Headers: test.giveHeader, + BodyProperties: test.giveBodyProperties, + QueryParameters: test.giveQueryParams, + Request: test.giveRequest, + Response: &response, + ResponseIsOptional: test.giveResponseIsOptional, + ErrorDecoder: test.giveErrorDecoder, + }, + ) + if test.wantError != nil { + assert.EqualError(t, err, test.wantError.Error()) + return + } + require.NoError(t, err) + assert.Equal(t, test.wantResponse, response) + }) + } +} + +func TestMergeHeaders(t *testing.T) { + t.Run("both empty", func(t *testing.T) { + merged := MergeHeaders(make(http.Header), make(http.Header)) + assert.Empty(t, merged) + }) + + t.Run("empty left", func(t *testing.T) { + left := make(http.Header) + + right := make(http.Header) + right.Set("X-API-Version", "0.0.1") + + merged := MergeHeaders(left, right) + assert.Equal(t, "0.0.1", merged.Get("X-API-Version")) + }) + + t.Run("empty right", func(t *testing.T) { + left := make(http.Header) + left.Set("X-API-Version", "0.0.1") + + right := make(http.Header) + + merged := MergeHeaders(left, right) + assert.Equal(t, "0.0.1", merged.Get("X-API-Version")) + }) + + t.Run("single value override", func(t *testing.T) { + left := make(http.Header) + left.Set("X-API-Version", "0.0.0") + + right := make(http.Header) + right.Set("X-API-Version", "0.0.1") + + merged := MergeHeaders(left, right) + assert.Equal(t, []string{"0.0.1"}, merged.Values("X-API-Version")) + }) + + t.Run("multiple value override", func(t *testing.T) { + left := make(http.Header) + left.Set("X-API-Versions", "0.0.0") + + right := make(http.Header) + right.Add("X-API-Versions", "0.0.1") + right.Add("X-API-Versions", "0.0.2") + + merged := MergeHeaders(left, right) + assert.Equal(t, []string{"0.0.1", "0.0.2"}, merged.Values("X-API-Versions")) + }) + + t.Run("disjoint merge", func(t *testing.T) { + left := make(http.Header) + left.Set("X-API-Tenancy", "test") + + right := make(http.Header) + right.Set("X-API-Version", "0.0.1") + + merged := MergeHeaders(left, right) + assert.Equal(t, []string{"test"}, merged.Values("X-API-Tenancy")) + assert.Equal(t, []string{"0.0.1"}, merged.Values("X-API-Version")) + }) +} + +// newTestServer returns a new *httptest.Server configured with the +// given test parameters. +func newTestServer(t *testing.T, tc *InternalTestCase) *httptest.Server { + return httptest.NewServer( + http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, tc.giveMethod, r.Method) + assert.Equal(t, contentType, r.Header.Get(contentTypeHeader)) + for header, value := range tc.giveHeader { + assert.Equal(t, value, r.Header.Values(header)) + } + + request := new(InternalTestRequest) + + bytes, err := io.ReadAll(r.Body) + if tc.giveRequest == nil { + require.Empty(t, bytes) + w.WriteHeader(http.StatusBadRequest) + _, err = w.Write([]byte("invalid request")) + require.NoError(t, err) + return + } + require.NoError(t, err) + require.NoError(t, json.Unmarshal(bytes, request)) + + switch request.Id { + case strconv.Itoa(http.StatusNotFound): + notFoundError := &InternalTestNotFoundError{ + APIError: &core.APIError{ + StatusCode: http.StatusNotFound, + }, + Message: fmt.Sprintf("ID %q not found", request.Id), + } + bytes, err = json.Marshal(notFoundError) + require.NoError(t, err) + + w.WriteHeader(http.StatusNotFound) + _, err = w.Write(bytes) + require.NoError(t, err) + return + + case strconv.Itoa(http.StatusInternalServerError): + w.WriteHeader(http.StatusInternalServerError) + _, err = w.Write([]byte("failed to process request")) + require.NoError(t, err) + return + } + + if tc.giveResponseIsOptional { + w.WriteHeader(http.StatusOK) + return + } + + extraBodyProperties := make(map[string]interface{}) + require.NoError(t, json.Unmarshal(bytes, &extraBodyProperties)) + delete(extraBodyProperties, "id") + + response := &InternalTestResponse{ + Id: request.Id, + ExtraBodyProperties: extraBodyProperties, + QueryParameters: r.URL.Query(), + } + bytes, err = json.Marshal(response) + require.NoError(t, err) + + _, err = w.Write(bytes) + require.NoError(t, err) + }, + ), + ) +} + +// newTestErrorDecoder returns an error decoder suitable for tests. +func newTestErrorDecoder(t *testing.T) func(int, http.Header, io.Reader) error { + return func(statusCode int, header http.Header, body io.Reader) error { + raw, err := io.ReadAll(body) + require.NoError(t, err) + + var ( + apiError = core.NewAPIError(statusCode, header, errors.New(string(raw))) + decoder = json.NewDecoder(bytes.NewReader(raw)) + ) + if statusCode == http.StatusNotFound { + value := new(InternalTestNotFoundError) + value.APIError = apiError + require.NoError(t, decoder.Decode(value)) + + return value + } + return apiError + } +} diff --git a/seed/go-sdk/idempotency-headers/internal/error_decoder.go b/seed/go-sdk/idempotency-headers/internal/error_decoder.go new file mode 100644 index 000000000000..07dd5e0c787c --- /dev/null +++ b/seed/go-sdk/idempotency-headers/internal/error_decoder.go @@ -0,0 +1,64 @@ +package internal + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + + "github.com/idempotency-headers/fern/core" +) + +// ErrorCodes maps HTTP status codes to error constructors. +type ErrorCodes map[int]func(*core.APIError) error + +// ErrorDecoder decodes *http.Response errors and returns a +// typed API error (e.g. *core.APIError). +type ErrorDecoder func(statusCode int, header http.Header, body io.Reader) error + +// NewErrorDecoder returns a new ErrorDecoder backed by the given error codes. +// errorCodesOverrides is optional and will be merged with the default error codes, +// with overrides taking precedence. +func NewErrorDecoder(errorCodes ErrorCodes, errorCodesOverrides ...ErrorCodes) ErrorDecoder { + // Merge default error codes with overrides + mergedErrorCodes := make(ErrorCodes) + + // Start with default error codes + for statusCode, errorFunc := range errorCodes { + mergedErrorCodes[statusCode] = errorFunc + } + + // Apply overrides if provided + if len(errorCodesOverrides) > 0 && errorCodesOverrides[0] != nil { + for statusCode, errorFunc := range errorCodesOverrides[0] { + mergedErrorCodes[statusCode] = errorFunc + } + } + + return func(statusCode int, header http.Header, body io.Reader) error { + raw, err := io.ReadAll(body) + if err != nil { + return fmt.Errorf("failed to read error from response body: %w", err) + } + apiError := core.NewAPIError( + statusCode, + header, + errors.New(string(raw)), + ) + newErrorFunc, ok := mergedErrorCodes[statusCode] + if !ok { + // This status code isn't recognized, so we return + // the API error as-is. + return apiError + } + customError := newErrorFunc(apiError) + if err := json.NewDecoder(bytes.NewReader(raw)).Decode(customError); err != nil { + // If we fail to decode the error, we return the + // API error as-is. + return apiError + } + return customError + } +} diff --git a/seed/go-sdk/idempotency-headers/internal/error_decoder_test.go b/seed/go-sdk/idempotency-headers/internal/error_decoder_test.go new file mode 100644 index 000000000000..1cefd210fcb8 --- /dev/null +++ b/seed/go-sdk/idempotency-headers/internal/error_decoder_test.go @@ -0,0 +1,59 @@ +package internal + +import ( + "bytes" + "errors" + "net/http" + "testing" + + "github.com/idempotency-headers/fern/core" + "github.com/stretchr/testify/assert" +) + +func TestErrorDecoder(t *testing.T) { + decoder := NewErrorDecoder( + ErrorCodes{ + http.StatusNotFound: func(apiError *core.APIError) error { + return &InternalTestNotFoundError{APIError: apiError} + }, + }) + + tests := []struct { + description string + giveStatusCode int + giveHeader http.Header + giveBody string + wantError error + }{ + { + description: "unrecognized status code", + giveStatusCode: http.StatusInternalServerError, + giveHeader: http.Header{}, + giveBody: "Internal Server Error", + wantError: core.NewAPIError(http.StatusInternalServerError, http.Header{}, errors.New("Internal Server Error")), + }, + { + description: "not found with valid JSON", + giveStatusCode: http.StatusNotFound, + giveHeader: http.Header{}, + giveBody: `{"message": "Resource not found"}`, + wantError: &InternalTestNotFoundError{ + APIError: core.NewAPIError(http.StatusNotFound, http.Header{}, errors.New(`{"message": "Resource not found"}`)), + Message: "Resource not found", + }, + }, + { + description: "not found with invalid JSON", + giveStatusCode: http.StatusNotFound, + giveHeader: http.Header{}, + giveBody: `Resource not found`, + wantError: core.NewAPIError(http.StatusNotFound, http.Header{}, errors.New("Resource not found")), + }, + } + + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + assert.Equal(t, tt.wantError, decoder(tt.giveStatusCode, tt.giveHeader, bytes.NewReader([]byte(tt.giveBody)))) + }) + } +} diff --git a/seed/go-sdk/idempotency-headers/internal/explicit_fields.go b/seed/go-sdk/idempotency-headers/internal/explicit_fields.go new file mode 100644 index 000000000000..4bdf34fc2b7c --- /dev/null +++ b/seed/go-sdk/idempotency-headers/internal/explicit_fields.go @@ -0,0 +1,116 @@ +package internal + +import ( + "math/big" + "reflect" + "strings" +) + +// HandleExplicitFields processes a struct to remove `omitempty` from +// fields that have been explicitly set (as indicated by their corresponding bit in explicitFields). +// Note that `marshaler` should be an embedded struct to avoid infinite recursion. +// Returns an interface{} that can be passed to json.Marshal. +func HandleExplicitFields(marshaler interface{}, explicitFields *big.Int) interface{} { + val := reflect.ValueOf(marshaler) + typ := reflect.TypeOf(marshaler) + + // Handle pointer types + if val.Kind() == reflect.Ptr { + if val.IsNil() { + return nil + } + val = val.Elem() + typ = typ.Elem() + } + + // Only handle struct types + if val.Kind() != reflect.Struct { + return marshaler + } + + // Handle embedded struct pattern + var sourceVal reflect.Value + var sourceType reflect.Type + + // Check if this is an embedded struct pattern + if typ.NumField() == 1 && typ.Field(0).Anonymous { + // This is likely an embedded struct, get the embedded value + embeddedField := val.Field(0) + sourceVal = embeddedField + sourceType = embeddedField.Type() + } else { + // Regular struct + sourceVal = val + sourceType = typ + } + + // If no explicit fields set, use standard marshaling + if explicitFields == nil || explicitFields.Sign() == 0 { + return marshaler + } + + // Create a new struct type with modified tags + fields := make([]reflect.StructField, 0, sourceType.NumField()) + + for i := 0; i < sourceType.NumField(); i++ { + field := sourceType.Field(i) + + // Skip unexported fields and the explicitFields field itself + if !field.IsExported() || field.Name == "explicitFields" { + continue + } + + // Check if this field has been explicitly set + fieldBit := big.NewInt(1) + fieldBit.Lsh(fieldBit, uint(i)) + if big.NewInt(0).And(explicitFields, fieldBit).Sign() != 0 { + // Remove omitempty from the json tag + tag := field.Tag.Get("json") + if tag != "" && tag != "-" { + // Parse the json tag, remove omitempty from options + parts := strings.Split(tag, ",") + if len(parts) > 1 { + var newParts []string + newParts = append(newParts, parts[0]) // Keep the field name + for _, part := range parts[1:] { + if strings.TrimSpace(part) != "omitempty" { + newParts = append(newParts, part) + } + } + tag = strings.Join(newParts, ",") + } + + // Reconstruct the struct tag + newTag := `json:"` + tag + `"` + if urlTag := field.Tag.Get("url"); urlTag != "" { + newTag += ` url:"` + urlTag + `"` + } + + field.Tag = reflect.StructTag(newTag) + } + } + + fields = append(fields, field) + } + + // Create new struct type with modified tags + newType := reflect.StructOf(fields) + newVal := reflect.New(newType).Elem() + + // Copy field values from original struct to new struct + fieldIndex := 0 + for i := 0; i < sourceType.NumField(); i++ { + originalField := sourceType.Field(i) + + // Skip unexported fields and the explicitFields field itself + if !originalField.IsExported() || originalField.Name == "explicitFields" { + continue + } + + originalValue := sourceVal.Field(i) + newVal.Field(fieldIndex).Set(originalValue) + fieldIndex++ + } + + return newVal.Interface() +} diff --git a/seed/go-sdk/idempotency-headers/internal/explicit_fields_test.go b/seed/go-sdk/idempotency-headers/internal/explicit_fields_test.go new file mode 100644 index 000000000000..3d05e88a2ce9 --- /dev/null +++ b/seed/go-sdk/idempotency-headers/internal/explicit_fields_test.go @@ -0,0 +1,497 @@ +package internal + +import ( + "encoding/json" + "math/big" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type testExplicitFieldsStruct struct { + Name *string `json:"name,omitempty"` + Code *string `json:"code,omitempty"` + Count *int `json:"count,omitempty"` + Enabled *bool `json:"enabled,omitempty"` + Tags []string `json:"tags,omitempty"` + //lint:ignore unused this field is intentionally unused for testing + unexported string `json:"-"` + explicitFields *big.Int `json:"-"` +} + +var ( + testFieldName = big.NewInt(1 << 0) + testFieldCode = big.NewInt(1 << 1) + testFieldCount = big.NewInt(1 << 2) + testFieldEnabled = big.NewInt(1 << 3) + testFieldTags = big.NewInt(1 << 4) +) + +func (t *testExplicitFieldsStruct) require(field *big.Int) { + if t.explicitFields == nil { + t.explicitFields = big.NewInt(0) + } + t.explicitFields.Or(t.explicitFields, field) +} + +func (t *testExplicitFieldsStruct) SetName(name *string) { + t.Name = name + t.require(testFieldName) +} + +func (t *testExplicitFieldsStruct) SetCode(code *string) { + t.Code = code + t.require(testFieldCode) +} + +func (t *testExplicitFieldsStruct) SetCount(count *int) { + t.Count = count + t.require(testFieldCount) +} + +func (t *testExplicitFieldsStruct) SetEnabled(enabled *bool) { + t.Enabled = enabled + t.require(testFieldEnabled) +} + +func (t *testExplicitFieldsStruct) SetTags(tags []string) { + t.Tags = tags + t.require(testFieldTags) +} + +func (t *testExplicitFieldsStruct) MarshalJSON() ([]byte, error) { + type embed testExplicitFieldsStruct + var marshaler = struct { + embed + }{ + embed: embed(*t), + } + return json.Marshal(HandleExplicitFields(marshaler, t.explicitFields)) +} + +type testStructWithoutExplicitFields struct { + Name *string `json:"name,omitempty"` + Code *string `json:"code,omitempty"` +} + +func TestHandleExplicitFields(t *testing.T) { + tests := []struct { + desc string + giveInput interface{} + wantBytes []byte + wantError string + }{ + { + desc: "nil input", + giveInput: nil, + wantBytes: []byte(`null`), + }, + { + desc: "non-struct input", + giveInput: "string", + wantBytes: []byte(`"string"`), + }, + { + desc: "slice input", + giveInput: []string{"a", "b"}, + wantBytes: []byte(`["a","b"]`), + }, + { + desc: "map input", + giveInput: map[string]interface{}{"key": "value"}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "struct without explicitFields field", + giveInput: &testStructWithoutExplicitFields{ + Name: stringPtr("test"), + Code: nil, + }, + wantBytes: []byte(`{"name":"test"}`), + }, + { + desc: "struct with no explicit fields set", + giveInput: &testExplicitFieldsStruct{ + Name: stringPtr("test"), + Code: nil, + }, + wantBytes: []byte(`{"name":"test"}`), + }, + { + desc: "struct with explicit nil field", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{ + Name: stringPtr("test"), + } + s.SetCode(nil) + return s + }(), + wantBytes: []byte(`{"name":"test","code":null}`), + }, + { + desc: "struct with explicit non-nil field", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{} + s.SetName(stringPtr("explicit")) + s.SetCode(stringPtr("also-explicit")) + return s + }(), + wantBytes: []byte(`{"name":"explicit","code":"also-explicit"}`), + }, + { + desc: "struct with mixed explicit and implicit fields", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{ + Name: stringPtr("implicit"), + Count: intPtr(42), + } + s.SetCode(nil) // explicit nil + return s + }(), + wantBytes: []byte(`{"name":"implicit","code":null,"count":42}`), + }, + { + desc: "struct with multiple explicit nil fields", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{ + Name: stringPtr("test"), + } + s.SetCode(nil) + s.SetCount(nil) + return s + }(), + wantBytes: []byte(`{"name":"test","code":null,"count":null}`), + }, + { + desc: "struct with slice field", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{ + Tags: []string{"tag1", "tag2"}, + } + s.SetTags(nil) // explicit nil slice + return s + }(), + wantBytes: []byte(`{"tags":null}`), + }, + { + desc: "struct with boolean field", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{} + s.SetEnabled(boolPtr(false)) // explicit false + return s + }(), + wantBytes: []byte(`{"enabled":false}`), + }, + { + desc: "struct with all fields explicit", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{} + s.SetName(stringPtr("test")) + s.SetCode(nil) + s.SetCount(intPtr(0)) + s.SetEnabled(boolPtr(false)) + s.SetTags([]string{}) + return s + }(), + wantBytes: []byte(`{"name":"test","code":null,"count":0,"enabled":false,"tags":[]}`), + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + var explicitFields *big.Int + if s, ok := tt.giveInput.(*testExplicitFieldsStruct); ok { + explicitFields = s.explicitFields + } + bytes, err := json.Marshal(HandleExplicitFields(tt.giveInput, explicitFields)) + if tt.wantError != "" { + require.EqualError(t, err, tt.wantError) + assert.Nil(t, tt.wantBytes) + return + } + require.NoError(t, err) + assert.JSONEq(t, string(tt.wantBytes), string(bytes)) + + // Verify it's valid JSON + var value interface{} + require.NoError(t, json.Unmarshal(bytes, &value)) + }) + } +} + +func TestHandleExplicitFieldsCustomMarshaler(t *testing.T) { + t.Run("custom marshaler with explicit fields", func(t *testing.T) { + s := &testExplicitFieldsStruct{} + s.SetName(nil) + s.SetCode(stringPtr("test-code")) + + bytes, err := s.MarshalJSON() + require.NoError(t, err) + assert.JSONEq(t, `{"name":null,"code":"test-code"}`, string(bytes)) + }) + + t.Run("custom marshaler with no explicit fields", func(t *testing.T) { + s := &testExplicitFieldsStruct{ + Name: stringPtr("implicit"), + Code: stringPtr("also-implicit"), + } + + bytes, err := s.MarshalJSON() + require.NoError(t, err) + assert.JSONEq(t, `{"name":"implicit","code":"also-implicit"}`, string(bytes)) + }) +} + +func TestHandleExplicitFieldsPointerHandling(t *testing.T) { + t.Run("nil pointer", func(t *testing.T) { + var s *testExplicitFieldsStruct + bytes, err := json.Marshal(HandleExplicitFields(s, nil)) + require.NoError(t, err) + assert.Equal(t, []byte(`null`), bytes) + }) + + t.Run("pointer to struct", func(t *testing.T) { + s := &testExplicitFieldsStruct{} + s.SetName(nil) + + bytes, err := json.Marshal(HandleExplicitFields(s, s.explicitFields)) + require.NoError(t, err) + assert.JSONEq(t, `{"name":null}`, string(bytes)) + }) +} + +func TestHandleExplicitFieldsEmbeddedStruct(t *testing.T) { + t.Run("embedded struct with explicit fields", func(t *testing.T) { + // Create a struct similar to what MarshalJSON creates + s := &testExplicitFieldsStruct{} + s.SetName(nil) + s.SetCode(stringPtr("test-code")) + + type embed testExplicitFieldsStruct + var marshaler = struct { + embed + }{ + embed: embed(*s), + } + + bytes, err := json.Marshal(HandleExplicitFields(marshaler, s.explicitFields)) + require.NoError(t, err) + // Should include both explicit fields (name as null, code as "test-code") + assert.JSONEq(t, `{"name":null,"code":"test-code"}`, string(bytes)) + }) + + t.Run("embedded struct with no explicit fields", func(t *testing.T) { + s := &testExplicitFieldsStruct{ + Name: stringPtr("implicit"), + Code: stringPtr("also-implicit"), + } + + type embed testExplicitFieldsStruct + var marshaler = struct { + embed + }{ + embed: embed(*s), + } + + bytes, err := json.Marshal(HandleExplicitFields(marshaler, s.explicitFields)) + require.NoError(t, err) + // Should only include non-nil fields (omitempty behavior) + assert.JSONEq(t, `{"name":"implicit","code":"also-implicit"}`, string(bytes)) + }) + + t.Run("embedded struct with mixed fields", func(t *testing.T) { + s := &testExplicitFieldsStruct{ + Count: intPtr(42), // implicit field + } + s.SetName(nil) // explicit nil + s.SetCode(stringPtr("explicit")) // explicit value + + type embed testExplicitFieldsStruct + var marshaler = struct { + embed + }{ + embed: embed(*s), + } + + bytes, err := json.Marshal(HandleExplicitFields(marshaler, s.explicitFields)) + require.NoError(t, err) + // Should include explicit null, explicit value, and implicit value + assert.JSONEq(t, `{"name":null,"code":"explicit","count":42}`, string(bytes)) + }) +} + +func TestHandleExplicitFieldsTagHandling(t *testing.T) { + type testStructWithComplexTags struct { + Field1 *string `json:"field1,omitempty" url:"field1,omitempty"` + Field2 *string `json:"field2,omitempty,string" url:"field2"` + Field3 *string `json:"-"` + Field4 *string `json:"field4"` + explicitFields *big.Int `json:"-"` + } + + s := &testStructWithComplexTags{ + Field1: stringPtr("test1"), + Field4: stringPtr("test4"), + explicitFields: big.NewInt(1), // Only first field is explicit + } + + bytes, err := json.Marshal(HandleExplicitFields(s, s.explicitFields)) + require.NoError(t, err) + + // Field1 should have omitempty removed, Field2 should keep omitempty, Field4 should be included + assert.JSONEq(t, `{"field1":"test1","field4":"test4"}`, string(bytes)) +} + +// Test types for nested struct explicit fields testing +type testNestedStruct struct { + NestedName *string `json:"nested_name,omitempty"` + NestedCode *string `json:"nested_code,omitempty"` + explicitFields *big.Int `json:"-"` +} + +type testParentStruct struct { + ParentName *string `json:"parent_name,omitempty"` + Nested *testNestedStruct `json:"nested,omitempty"` + explicitFields *big.Int `json:"-"` +} + +var ( + nestedFieldName = big.NewInt(1 << 0) + nestedFieldCode = big.NewInt(1 << 1) +) + +var ( + parentFieldName = big.NewInt(1 << 0) + parentFieldNested = big.NewInt(1 << 1) +) + +func (n *testNestedStruct) require(field *big.Int) { + if n.explicitFields == nil { + n.explicitFields = big.NewInt(0) + } + n.explicitFields.Or(n.explicitFields, field) +} + +func (n *testNestedStruct) SetNestedName(name *string) { + n.NestedName = name + n.require(nestedFieldName) +} + +func (n *testNestedStruct) SetNestedCode(code *string) { + n.NestedCode = code + n.require(nestedFieldCode) +} + +func (n *testNestedStruct) MarshalJSON() ([]byte, error) { + type embed testNestedStruct + var marshaler = struct { + embed + }{ + embed: embed(*n), + } + return json.Marshal(HandleExplicitFields(marshaler, n.explicitFields)) +} + +func (p *testParentStruct) require(field *big.Int) { + if p.explicitFields == nil { + p.explicitFields = big.NewInt(0) + } + p.explicitFields.Or(p.explicitFields, field) +} + +func (p *testParentStruct) SetParentName(name *string) { + p.ParentName = name + p.require(parentFieldName) +} + +func (p *testParentStruct) SetNested(nested *testNestedStruct) { + p.Nested = nested + p.require(parentFieldNested) +} + +func (p *testParentStruct) MarshalJSON() ([]byte, error) { + type embed testParentStruct + var marshaler = struct { + embed + }{ + embed: embed(*p), + } + return json.Marshal(HandleExplicitFields(marshaler, p.explicitFields)) +} + +func TestHandleExplicitFieldsNestedStruct(t *testing.T) { + tests := []struct { + desc string + setupFunc func() *testParentStruct + wantBytes []byte + }{ + { + desc: "nested struct with explicit nil in nested object", + setupFunc: func() *testParentStruct { + nested := &testNestedStruct{ + NestedName: stringPtr("implicit-nested"), + } + nested.SetNestedCode(nil) // explicit nil + + return &testParentStruct{ + ParentName: stringPtr("implicit-parent"), + Nested: nested, + } + }, + wantBytes: []byte(`{"parent_name":"implicit-parent","nested":{"nested_name":"implicit-nested","nested_code":null}}`), + }, + { + desc: "parent with explicit nil nested struct", + setupFunc: func() *testParentStruct { + parent := &testParentStruct{ + ParentName: stringPtr("implicit-parent"), + } + parent.SetNested(nil) // explicit nil nested struct + return parent + }, + wantBytes: []byte(`{"parent_name":"implicit-parent","nested":null}`), + }, + { + desc: "all explicit fields in nested structure", + setupFunc: func() *testParentStruct { + nested := &testNestedStruct{} + nested.SetNestedName(stringPtr("explicit-nested")) + nested.SetNestedCode(nil) // explicit nil + + parent := &testParentStruct{} + parent.SetParentName(nil) // explicit nil + parent.SetNested(nested) // explicit nested struct + + return parent + }, + wantBytes: []byte(`{"parent_name":null,"nested":{"nested_name":"explicit-nested","nested_code":null}}`), + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + parent := tt.setupFunc() + bytes, err := parent.MarshalJSON() + require.NoError(t, err) + assert.JSONEq(t, string(tt.wantBytes), string(bytes)) + + // Verify it's valid JSON + var value interface{} + require.NoError(t, json.Unmarshal(bytes, &value)) + }) + } +} + +// Helper functions +func stringPtr(s string) *string { + return &s +} + +func intPtr(i int) *int { + return &i +} + +func boolPtr(b bool) *bool { + return &b +} diff --git a/seed/go-sdk/idempotency-headers/internal/extra_properties.go b/seed/go-sdk/idempotency-headers/internal/extra_properties.go new file mode 100644 index 000000000000..540c3fd89eeb --- /dev/null +++ b/seed/go-sdk/idempotency-headers/internal/extra_properties.go @@ -0,0 +1,141 @@ +package internal + +import ( + "bytes" + "encoding/json" + "fmt" + "reflect" + "strings" +) + +// MarshalJSONWithExtraProperty marshals the given value to JSON, including the extra property. +func MarshalJSONWithExtraProperty(marshaler interface{}, key string, value interface{}) ([]byte, error) { + return MarshalJSONWithExtraProperties(marshaler, map[string]interface{}{key: value}) +} + +// MarshalJSONWithExtraProperties marshals the given value to JSON, including any extra properties. +func MarshalJSONWithExtraProperties(marshaler interface{}, extraProperties map[string]interface{}) ([]byte, error) { + bytes, err := json.Marshal(marshaler) + if err != nil { + return nil, err + } + if len(extraProperties) == 0 { + return bytes, nil + } + keys, err := getKeys(marshaler) + if err != nil { + return nil, err + } + for _, key := range keys { + if _, ok := extraProperties[key]; ok { + return nil, fmt.Errorf("cannot add extra property %q because it is already defined on the type", key) + } + } + extraBytes, err := json.Marshal(extraProperties) + if err != nil { + return nil, err + } + if isEmptyJSON(bytes) { + if isEmptyJSON(extraBytes) { + return bytes, nil + } + return extraBytes, nil + } + result := bytes[:len(bytes)-1] + result = append(result, ',') + result = append(result, extraBytes[1:len(extraBytes)-1]...) + result = append(result, '}') + return result, nil +} + +// ExtractExtraProperties extracts any extra properties from the given value. +func ExtractExtraProperties(bytes []byte, value interface{}, exclude ...string) (map[string]interface{}, error) { + val := reflect.ValueOf(value) + for val.Kind() == reflect.Ptr { + if val.IsNil() { + return nil, fmt.Errorf("value must be non-nil to extract extra properties") + } + val = val.Elem() + } + if err := json.Unmarshal(bytes, &value); err != nil { + return nil, err + } + var extraProperties map[string]interface{} + if err := json.Unmarshal(bytes, &extraProperties); err != nil { + return nil, err + } + for i := 0; i < val.Type().NumField(); i++ { + key := jsonKey(val.Type().Field(i)) + if key == "" || key == "-" { + continue + } + delete(extraProperties, key) + } + for _, key := range exclude { + delete(extraProperties, key) + } + if len(extraProperties) == 0 { + return nil, nil + } + return extraProperties, nil +} + +// getKeys returns the keys associated with the given value. The value must be a +// a struct or a map with string keys. +func getKeys(value interface{}) ([]string, error) { + val := reflect.ValueOf(value) + if val.Kind() == reflect.Ptr { + val = val.Elem() + } + if !val.IsValid() { + return nil, nil + } + switch val.Kind() { + case reflect.Struct: + return getKeysForStructType(val.Type()), nil + case reflect.Map: + var keys []string + if val.Type().Key().Kind() != reflect.String { + return nil, fmt.Errorf("cannot extract keys from %T; only structs and maps with string keys are supported", value) + } + for _, key := range val.MapKeys() { + keys = append(keys, key.String()) + } + return keys, nil + default: + return nil, fmt.Errorf("cannot extract keys from %T; only structs and maps with string keys are supported", value) + } +} + +// getKeysForStructType returns all the keys associated with the given struct type, +// visiting embedded fields recursively. +func getKeysForStructType(structType reflect.Type) []string { + if structType.Kind() == reflect.Pointer { + structType = structType.Elem() + } + if structType.Kind() != reflect.Struct { + return nil + } + var keys []string + for i := 0; i < structType.NumField(); i++ { + field := structType.Field(i) + if field.Anonymous { + keys = append(keys, getKeysForStructType(field.Type)...) + continue + } + keys = append(keys, jsonKey(field)) + } + return keys +} + +// jsonKey returns the JSON key from the struct tag of the given field, +// excluding the omitempty flag (if any). +func jsonKey(field reflect.StructField) string { + return strings.TrimSuffix(field.Tag.Get("json"), ",omitempty") +} + +// isEmptyJSON returns true if the given data is empty, the empty JSON object, or +// an explicit null. +func isEmptyJSON(data []byte) bool { + return len(data) <= 2 || bytes.Equal(data, []byte("null")) +} diff --git a/seed/go-sdk/idempotency-headers/internal/extra_properties_test.go b/seed/go-sdk/idempotency-headers/internal/extra_properties_test.go new file mode 100644 index 000000000000..aa2510ee5121 --- /dev/null +++ b/seed/go-sdk/idempotency-headers/internal/extra_properties_test.go @@ -0,0 +1,228 @@ +package internal + +import ( + "encoding/json" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type testMarshaler struct { + Name string `json:"name"` + BirthDate time.Time `json:"birthDate"` + CreatedAt time.Time `json:"created_at"` +} + +func (t *testMarshaler) MarshalJSON() ([]byte, error) { + type embed testMarshaler + var marshaler = struct { + embed + BirthDate string `json:"birthDate"` + CreatedAt string `json:"created_at"` + }{ + embed: embed(*t), + BirthDate: t.BirthDate.Format("2006-01-02"), + CreatedAt: t.CreatedAt.Format(time.RFC3339), + } + return MarshalJSONWithExtraProperty(marshaler, "type", "test") +} + +func TestMarshalJSONWithExtraProperties(t *testing.T) { + tests := []struct { + desc string + giveMarshaler interface{} + giveExtraProperties map[string]interface{} + wantBytes []byte + wantError string + }{ + { + desc: "invalid type", + giveMarshaler: []string{"invalid"}, + giveExtraProperties: map[string]interface{}{"key": "overwrite"}, + wantError: `cannot extract keys from []string; only structs and maps with string keys are supported`, + }, + { + desc: "invalid key type", + giveMarshaler: map[int]interface{}{42: "value"}, + giveExtraProperties: map[string]interface{}{"key": "overwrite"}, + wantError: `cannot extract keys from map[int]interface {}; only structs and maps with string keys are supported`, + }, + { + desc: "invalid map overwrite", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{"key": "overwrite"}, + wantError: `cannot add extra property "key" because it is already defined on the type`, + }, + { + desc: "invalid struct overwrite", + giveMarshaler: new(testMarshaler), + giveExtraProperties: map[string]interface{}{"birthDate": "2000-01-01"}, + wantError: `cannot add extra property "birthDate" because it is already defined on the type`, + }, + { + desc: "invalid struct overwrite embedded type", + giveMarshaler: new(testMarshaler), + giveExtraProperties: map[string]interface{}{"name": "bob"}, + wantError: `cannot add extra property "name" because it is already defined on the type`, + }, + { + desc: "nil", + giveMarshaler: nil, + giveExtraProperties: nil, + wantBytes: []byte(`null`), + }, + { + desc: "empty", + giveMarshaler: map[string]interface{}{}, + giveExtraProperties: map[string]interface{}{}, + wantBytes: []byte(`{}`), + }, + { + desc: "no extra properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "only extra properties", + giveMarshaler: map[string]interface{}{}, + giveExtraProperties: map[string]interface{}{"key": "value"}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "single extra property", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{"extra": "property"}, + wantBytes: []byte(`{"key":"value","extra":"property"}`), + }, + { + desc: "multiple extra properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{"one": 1, "two": 2}, + wantBytes: []byte(`{"key":"value","one":1,"two":2}`), + }, + { + desc: "nested properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{ + "user": map[string]interface{}{ + "age": 42, + "name": "alice", + }, + }, + wantBytes: []byte(`{"key":"value","user":{"age":42,"name":"alice"}}`), + }, + { + desc: "multiple nested properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{ + "metadata": map[string]interface{}{ + "ip": "127.0.0.1", + }, + "user": map[string]interface{}{ + "age": 42, + "name": "alice", + }, + }, + wantBytes: []byte(`{"key":"value","metadata":{"ip":"127.0.0.1"},"user":{"age":42,"name":"alice"}}`), + }, + { + desc: "custom marshaler", + giveMarshaler: &testMarshaler{ + Name: "alice", + BirthDate: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), + CreatedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC), + }, + giveExtraProperties: map[string]interface{}{ + "extra": "property", + }, + wantBytes: []byte(`{"name":"alice","birthDate":"2000-01-01","created_at":"2024-01-01T00:00:00Z","type":"test","extra":"property"}`), + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + bytes, err := MarshalJSONWithExtraProperties(tt.giveMarshaler, tt.giveExtraProperties) + if tt.wantError != "" { + require.EqualError(t, err, tt.wantError) + assert.Nil(t, tt.wantBytes) + return + } + require.NoError(t, err) + assert.Equal(t, tt.wantBytes, bytes) + + value := make(map[string]interface{}) + require.NoError(t, json.Unmarshal(bytes, &value)) + }) + } +} + +func TestExtractExtraProperties(t *testing.T) { + t.Run("none", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice"}`), value) + require.NoError(t, err) + assert.Nil(t, extraProperties) + }) + + t.Run("non-nil pointer", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]interface{}{"age": float64(42)}, extraProperties) + }) + + t.Run("nil pointer", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + var value *user + _, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + assert.EqualError(t, err, "value must be non-nil to extract extra properties") + }) + + t.Run("non-zero value", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]interface{}{"age": float64(42)}, extraProperties) + }) + + t.Run("zero value", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + var value user + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]interface{}{"age": float64(42)}, extraProperties) + }) + + t.Run("exclude", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value, "age") + require.NoError(t, err) + assert.Nil(t, extraProperties) + }) +} diff --git a/seed/go-sdk/idempotency-headers/internal/http.go b/seed/go-sdk/idempotency-headers/internal/http.go new file mode 100644 index 000000000000..77863752bb58 --- /dev/null +++ b/seed/go-sdk/idempotency-headers/internal/http.go @@ -0,0 +1,71 @@ +package internal + +import ( + "fmt" + "net/http" + "net/url" + "reflect" +) + +// HTTPClient is an interface for a subset of the *http.Client. +type HTTPClient interface { + Do(*http.Request) (*http.Response, error) +} + +// ResolveBaseURL resolves the base URL from the given arguments, +// preferring the first non-empty value. +func ResolveBaseURL(values ...string) string { + for _, value := range values { + if value != "" { + return value + } + } + return "" +} + +// EncodeURL encodes the given arguments into the URL, escaping +// values as needed. Pointer arguments are dereferenced before processing. +func EncodeURL(urlFormat string, args ...interface{}) string { + escapedArgs := make([]interface{}, 0, len(args)) + for _, arg := range args { + // Dereference the argument if it's a pointer + value := dereferenceArg(arg) + escapedArgs = append(escapedArgs, url.PathEscape(fmt.Sprintf("%v", value))) + } + return fmt.Sprintf(urlFormat, escapedArgs...) +} + +// dereferenceArg dereferences a pointer argument if necessary, returning the underlying value. +// If the argument is not a pointer or is nil, it returns the argument as-is. +func dereferenceArg(arg interface{}) interface{} { + if arg == nil { + return arg + } + + v := reflect.ValueOf(arg) + + // Keep dereferencing until we get to a non-pointer value or hit nil + for v.Kind() == reflect.Ptr { + if v.IsNil() { + return nil + } + v = v.Elem() + } + + return v.Interface() +} + +// MergeHeaders merges the given headers together, where the right +// takes precedence over the left. +func MergeHeaders(left, right http.Header) http.Header { + for key, values := range right { + if len(values) > 1 { + left[key] = values + continue + } + if value := right.Get(key); value != "" { + left.Set(key, value) + } + } + return left +} diff --git a/seed/go-sdk/idempotency-headers/internal/query.go b/seed/go-sdk/idempotency-headers/internal/query.go new file mode 100644 index 000000000000..1cbaf7fe1c02 --- /dev/null +++ b/seed/go-sdk/idempotency-headers/internal/query.go @@ -0,0 +1,353 @@ +package internal + +import ( + "encoding/base64" + "fmt" + "net/url" + "reflect" + "strings" + "time" + + "github.com/google/uuid" +) + +var ( + bytesType = reflect.TypeOf([]byte{}) + queryEncoderType = reflect.TypeOf(new(QueryEncoder)).Elem() + timeType = reflect.TypeOf(time.Time{}) + uuidType = reflect.TypeOf(uuid.UUID{}) +) + +// QueryEncoder is an interface implemented by any type that wishes to encode +// itself into URL values in a non-standard way. +type QueryEncoder interface { + EncodeQueryValues(key string, v *url.Values) error +} + +// prepareValue handles common validation and unwrapping logic for both functions +func prepareValue(v interface{}) (reflect.Value, url.Values, error) { + values := make(url.Values) + val := reflect.ValueOf(v) + for val.Kind() == reflect.Ptr { + if val.IsNil() { + return reflect.Value{}, values, nil + } + val = val.Elem() + } + + if v == nil { + return reflect.Value{}, values, nil + } + + if val.Kind() != reflect.Struct { + return reflect.Value{}, nil, fmt.Errorf("query: Values() expects struct input. Got %v", val.Kind()) + } + + err := reflectValue(values, val, "") + if err != nil { + return reflect.Value{}, nil, err + } + + return val, values, nil +} + +// QueryValues encodes url.Values from request objects. +// +// Note: This type is inspired by Google's query encoding library, but +// supports far less customization and is tailored to fit this SDK's use case. +// +// Ref: https://github.com/google/go-querystring +func QueryValues(v interface{}) (url.Values, error) { + _, values, err := prepareValue(v) + return values, err +} + +// QueryValuesWithDefaults encodes url.Values from request objects +// and default values, merging the defaults into the request. +// It's expected that the values of defaults are wire names. +func QueryValuesWithDefaults(v interface{}, defaults map[string]interface{}) (url.Values, error) { + val, values, err := prepareValue(v) + if err != nil { + return values, err + } + if !val.IsValid() { + return values, nil + } + + // apply defaults to zero-value fields directly on the original struct + valType := val.Type() + for i := 0; i < val.NumField(); i++ { + field := val.Field(i) + fieldType := valType.Field(i) + fieldName := fieldType.Name + + if fieldType.PkgPath != "" && !fieldType.Anonymous { + // Skip unexported fields. + continue + } + + // check if field is zero value and we have a default for it + if field.CanSet() && field.IsZero() { + tag := fieldType.Tag.Get("url") + if tag == "" || tag == "-" { + continue + } + wireName, _ := parseTag(tag) + if wireName == "" { + wireName = fieldName + } + if defaultVal, exists := defaults[wireName]; exists { + values.Set(wireName, valueString(reflect.ValueOf(defaultVal), tagOptions{}, reflect.StructField{})) + } + } + } + + return values, err +} + +// reflectValue populates the values parameter from the struct fields in val. +// Embedded structs are followed recursively (using the rules defined in the +// Values function documentation) breadth-first. +func reflectValue(values url.Values, val reflect.Value, scope string) error { + typ := val.Type() + for i := 0; i < typ.NumField(); i++ { + sf := typ.Field(i) + if sf.PkgPath != "" && !sf.Anonymous { + // Skip unexported fields. + continue + } + + sv := val.Field(i) + tag := sf.Tag.Get("url") + if tag == "" || tag == "-" { + continue + } + + name, opts := parseTag(tag) + if name == "" { + name = sf.Name + } + + if scope != "" { + name = scope + "[" + name + "]" + } + + if opts.Contains("omitempty") && isEmptyValue(sv) { + continue + } + + if sv.Type().Implements(queryEncoderType) { + // If sv is a nil pointer and the custom encoder is defined on a non-pointer + // method receiver, set sv to the zero value of the underlying type + if !reflect.Indirect(sv).IsValid() && sv.Type().Elem().Implements(queryEncoderType) { + sv = reflect.New(sv.Type().Elem()) + } + + m := sv.Interface().(QueryEncoder) + if err := m.EncodeQueryValues(name, &values); err != nil { + return err + } + continue + } + + // Recursively dereference pointers, but stop at nil pointers. + for sv.Kind() == reflect.Ptr { + if sv.IsNil() { + break + } + sv = sv.Elem() + } + + if sv.Type() == uuidType || sv.Type() == bytesType || sv.Type() == timeType { + values.Add(name, valueString(sv, opts, sf)) + continue + } + + if sv.Kind() == reflect.Slice || sv.Kind() == reflect.Array { + if sv.Len() == 0 { + // Skip if slice or array is empty. + continue + } + for i := 0; i < sv.Len(); i++ { + value := sv.Index(i) + if isStructPointer(value) && !value.IsNil() { + if err := reflectValue(values, value.Elem(), name); err != nil { + return err + } + } else { + values.Add(name, valueString(value, opts, sf)) + } + } + continue + } + + if sv.Kind() == reflect.Map { + if err := reflectMap(values, sv, name); err != nil { + return err + } + continue + } + + if sv.Kind() == reflect.Struct { + if err := reflectValue(values, sv, name); err != nil { + return err + } + continue + } + + values.Add(name, valueString(sv, opts, sf)) + } + + return nil +} + +// reflectMap handles map types specifically, generating query parameters in the format key[mapkey]=value +func reflectMap(values url.Values, val reflect.Value, scope string) error { + if val.IsNil() { + return nil + } + + iter := val.MapRange() + for iter.Next() { + k := iter.Key() + v := iter.Value() + + key := fmt.Sprint(k.Interface()) + paramName := scope + "[" + key + "]" + + for v.Kind() == reflect.Ptr { + if v.IsNil() { + break + } + v = v.Elem() + } + + for v.Kind() == reflect.Interface { + v = v.Elem() + } + + if v.Kind() == reflect.Map { + if err := reflectMap(values, v, paramName); err != nil { + return err + } + continue + } + + if v.Kind() == reflect.Struct { + if err := reflectValue(values, v, paramName); err != nil { + return err + } + continue + } + + if v.Kind() == reflect.Slice || v.Kind() == reflect.Array { + if v.Len() == 0 { + continue + } + for i := 0; i < v.Len(); i++ { + value := v.Index(i) + if isStructPointer(value) && !value.IsNil() { + if err := reflectValue(values, value.Elem(), paramName); err != nil { + return err + } + } else { + values.Add(paramName, valueString(value, tagOptions{}, reflect.StructField{})) + } + } + continue + } + + values.Add(paramName, valueString(v, tagOptions{}, reflect.StructField{})) + } + + return nil +} + +// valueString returns the string representation of a value. +func valueString(v reflect.Value, opts tagOptions, sf reflect.StructField) string { + for v.Kind() == reflect.Ptr { + if v.IsNil() { + return "" + } + v = v.Elem() + } + + if v.Type() == timeType { + t := v.Interface().(time.Time) + if format := sf.Tag.Get("format"); format == "date" { + return t.Format("2006-01-02") + } + return t.Format(time.RFC3339) + } + + if v.Type() == uuidType { + u := v.Interface().(uuid.UUID) + return u.String() + } + + if v.Type() == bytesType { + b := v.Interface().([]byte) + return base64.StdEncoding.EncodeToString(b) + } + + return fmt.Sprint(v.Interface()) +} + +// isEmptyValue checks if a value should be considered empty for the purposes +// of omitting fields with the "omitempty" option. +func isEmptyValue(v reflect.Value) bool { + type zeroable interface { + IsZero() bool + } + + if !v.IsZero() { + if z, ok := v.Interface().(zeroable); ok { + return z.IsZero() + } + } + + switch v.Kind() { + case reflect.Array, reflect.Map, reflect.Slice, reflect.String: + return v.Len() == 0 + case reflect.Bool: + return !v.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Interface, reflect.Ptr: + return v.IsNil() + case reflect.Invalid, reflect.Complex64, reflect.Complex128, reflect.Chan, reflect.Func, reflect.Struct, reflect.UnsafePointer: + return false + } + + return false +} + +// isStructPointer returns true if the given reflect.Value is a pointer to a struct. +func isStructPointer(v reflect.Value) bool { + return v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct +} + +// tagOptions is the string following a comma in a struct field's "url" tag, or +// the empty string. It does not include the leading comma. +type tagOptions []string + +// parseTag splits a struct field's url tag into its name and comma-separated +// options. +func parseTag(tag string) (string, tagOptions) { + s := strings.Split(tag, ",") + return s[0], s[1:] +} + +// Contains checks whether the tagOptions contains the specified option. +func (o tagOptions) Contains(option string) bool { + for _, s := range o { + if s == option { + return true + } + } + return false +} diff --git a/seed/go-sdk/idempotency-headers/internal/query_test.go b/seed/go-sdk/idempotency-headers/internal/query_test.go new file mode 100644 index 000000000000..2c28cb8acf68 --- /dev/null +++ b/seed/go-sdk/idempotency-headers/internal/query_test.go @@ -0,0 +1,395 @@ +package internal + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestQueryValues(t *testing.T) { + t.Run("empty optional", func(t *testing.T) { + type nested struct { + Value *string `json:"value,omitempty" url:"value,omitempty"` + } + type example struct { + Nested *nested `json:"nested,omitempty" url:"nested,omitempty"` + } + + values, err := QueryValues(&example{}) + require.NoError(t, err) + assert.Empty(t, values) + }) + + t.Run("empty required", func(t *testing.T) { + type nested struct { + Value *string `json:"value,omitempty" url:"value,omitempty"` + } + type example struct { + Required string `json:"required" url:"required"` + Nested *nested `json:"nested,omitempty" url:"nested,omitempty"` + } + + values, err := QueryValues(&example{}) + require.NoError(t, err) + assert.Equal(t, "required=", values.Encode()) + }) + + t.Run("allow multiple", func(t *testing.T) { + type example struct { + Values []string `json:"values" url:"values"` + } + + values, err := QueryValues( + &example{ + Values: []string{"foo", "bar", "baz"}, + }, + ) + require.NoError(t, err) + assert.Equal(t, "values=foo&values=bar&values=baz", values.Encode()) + }) + + t.Run("nested object", func(t *testing.T) { + type nested struct { + Value *string `json:"value,omitempty" url:"value,omitempty"` + } + type example struct { + Required string `json:"required" url:"required"` + Nested *nested `json:"nested,omitempty" url:"nested,omitempty"` + } + + nestedValue := "nestedValue" + values, err := QueryValues( + &example{ + Required: "requiredValue", + Nested: &nested{ + Value: &nestedValue, + }, + }, + ) + require.NoError(t, err) + assert.Equal(t, "nested%5Bvalue%5D=nestedValue&required=requiredValue", values.Encode()) + }) + + t.Run("url unspecified", func(t *testing.T) { + type example struct { + Required string `json:"required" url:"required"` + NotFound string `json:"notFound"` + } + + values, err := QueryValues( + &example{ + Required: "requiredValue", + NotFound: "notFound", + }, + ) + require.NoError(t, err) + assert.Equal(t, "required=requiredValue", values.Encode()) + }) + + t.Run("url ignored", func(t *testing.T) { + type example struct { + Required string `json:"required" url:"required"` + NotFound string `json:"notFound" url:"-"` + } + + values, err := QueryValues( + &example{ + Required: "requiredValue", + NotFound: "notFound", + }, + ) + require.NoError(t, err) + assert.Equal(t, "required=requiredValue", values.Encode()) + }) + + t.Run("datetime", func(t *testing.T) { + type example struct { + DateTime time.Time `json:"dateTime" url:"dateTime"` + } + + values, err := QueryValues( + &example{ + DateTime: time.Date(1994, 3, 16, 12, 34, 56, 0, time.UTC), + }, + ) + require.NoError(t, err) + assert.Equal(t, "dateTime=1994-03-16T12%3A34%3A56Z", values.Encode()) + }) + + t.Run("date", func(t *testing.T) { + type example struct { + Date time.Time `json:"date" url:"date" format:"date"` + } + + values, err := QueryValues( + &example{ + Date: time.Date(1994, 3, 16, 12, 34, 56, 0, time.UTC), + }, + ) + require.NoError(t, err) + assert.Equal(t, "date=1994-03-16", values.Encode()) + }) + + t.Run("optional time", func(t *testing.T) { + type example struct { + Date *time.Time `json:"date,omitempty" url:"date,omitempty" format:"date"` + } + + values, err := QueryValues( + &example{}, + ) + require.NoError(t, err) + assert.Empty(t, values.Encode()) + }) + + t.Run("omitempty with non-pointer zero value", func(t *testing.T) { + type enum string + + type example struct { + Enum enum `json:"enum,omitempty" url:"enum,omitempty"` + } + + values, err := QueryValues( + &example{}, + ) + require.NoError(t, err) + assert.Empty(t, values.Encode()) + }) + + t.Run("object array", func(t *testing.T) { + type object struct { + Key string `json:"key" url:"key"` + Value string `json:"value" url:"value"` + } + type example struct { + Objects []*object `json:"objects,omitempty" url:"objects,omitempty"` + } + + values, err := QueryValues( + &example{ + Objects: []*object{ + { + Key: "hello", + Value: "world", + }, + { + Key: "foo", + Value: "bar", + }, + }, + }, + ) + require.NoError(t, err) + assert.Equal(t, "objects%5Bkey%5D=hello&objects%5Bkey%5D=foo&objects%5Bvalue%5D=world&objects%5Bvalue%5D=bar", values.Encode()) + }) + + t.Run("map", func(t *testing.T) { + type request struct { + Metadata map[string]interface{} `json:"metadata" url:"metadata"` + } + values, err := QueryValues( + &request{ + Metadata: map[string]interface{}{ + "foo": "bar", + "baz": "qux", + }, + }, + ) + require.NoError(t, err) + assert.Equal(t, "metadata%5Bbaz%5D=qux&metadata%5Bfoo%5D=bar", values.Encode()) + }) + + t.Run("nested map", func(t *testing.T) { + type request struct { + Metadata map[string]interface{} `json:"metadata" url:"metadata"` + } + values, err := QueryValues( + &request{ + Metadata: map[string]interface{}{ + "inner": map[string]interface{}{ + "foo": "bar", + }, + }, + }, + ) + require.NoError(t, err) + assert.Equal(t, "metadata%5Binner%5D%5Bfoo%5D=bar", values.Encode()) + }) + + t.Run("nested map array", func(t *testing.T) { + type request struct { + Metadata map[string]interface{} `json:"metadata" url:"metadata"` + } + values, err := QueryValues( + &request{ + Metadata: map[string]interface{}{ + "inner": []string{ + "one", + "two", + "three", + }, + }, + }, + ) + require.NoError(t, err) + assert.Equal(t, "metadata%5Binner%5D=one&metadata%5Binner%5D=two&metadata%5Binner%5D=three", values.Encode()) + }) +} + +func TestQueryValuesWithDefaults(t *testing.T) { + t.Run("apply defaults to zero values", func(t *testing.T) { + type example struct { + Name string `json:"name" url:"name"` + Age int `json:"age" url:"age"` + Enabled bool `json:"enabled" url:"enabled"` + } + + defaults := map[string]interface{}{ + "name": "default-name", + "age": 25, + "enabled": true, + } + + values, err := QueryValuesWithDefaults(&example{}, defaults) + require.NoError(t, err) + assert.Equal(t, "age=25&enabled=true&name=default-name", values.Encode()) + }) + + t.Run("preserve non-zero values over defaults", func(t *testing.T) { + type example struct { + Name string `json:"name" url:"name"` + Age int `json:"age" url:"age"` + Enabled bool `json:"enabled" url:"enabled"` + } + + defaults := map[string]interface{}{ + "name": "default-name", + "age": 25, + "enabled": true, + } + + values, err := QueryValuesWithDefaults(&example{ + Name: "actual-name", + Age: 30, + // Enabled remains false (zero value), should get default + }, defaults) + require.NoError(t, err) + assert.Equal(t, "age=30&enabled=true&name=actual-name", values.Encode()) + }) + + t.Run("ignore defaults for fields not in struct", func(t *testing.T) { + type example struct { + Name string `json:"name" url:"name"` + Age int `json:"age" url:"age"` + } + + defaults := map[string]interface{}{ + "name": "default-name", + "age": 25, + "nonexistent": "should-be-ignored", + } + + values, err := QueryValuesWithDefaults(&example{}, defaults) + require.NoError(t, err) + assert.Equal(t, "age=25&name=default-name", values.Encode()) + }) + + t.Run("type conversion for compatible defaults", func(t *testing.T) { + type example struct { + Count int64 `json:"count" url:"count"` + Rate float64 `json:"rate" url:"rate"` + Message string `json:"message" url:"message"` + } + + defaults := map[string]interface{}{ + "count": int(100), // int -> int64 conversion + "rate": float32(2.5), // float32 -> float64 conversion + "message": "hello", // string -> string (no conversion needed) + } + + values, err := QueryValuesWithDefaults(&example{}, defaults) + require.NoError(t, err) + assert.Equal(t, "count=100&message=hello&rate=2.5", values.Encode()) + }) + + t.Run("mixed with pointer fields and omitempty", func(t *testing.T) { + type example struct { + Required string `json:"required" url:"required"` + Optional *string `json:"optional,omitempty" url:"optional,omitempty"` + Count int `json:"count,omitempty" url:"count,omitempty"` + } + + defaultOptional := "default-optional" + defaults := map[string]interface{}{ + "required": "default-required", + "optional": &defaultOptional, // pointer type + "count": 42, + } + + values, err := QueryValuesWithDefaults(&example{ + Required: "custom-required", // should override default + // Optional is nil, should get default + // Count is 0, should get default + }, defaults) + require.NoError(t, err) + assert.Equal(t, "count=42&optional=default-optional&required=custom-required", values.Encode()) + }) + + t.Run("override non-zero defaults with explicit zero values", func(t *testing.T) { + type example struct { + Name *string `json:"name" url:"name"` + Age *int `json:"age" url:"age"` + Enabled *bool `json:"enabled" url:"enabled"` + } + + defaults := map[string]interface{}{ + "name": "default-name", + "age": 25, + "enabled": true, + } + + // first, test that a properly empty request is overridden: + { + values, err := QueryValuesWithDefaults(&example{}, defaults) + require.NoError(t, err) + assert.Equal(t, "age=25&enabled=true&name=default-name", values.Encode()) + } + + // second, test that a request that contains zeros is not overridden: + var ( + name = "" + age = 0 + enabled = false + ) + values, err := QueryValuesWithDefaults(&example{ + Name: &name, // explicit empty string should override default + Age: &age, // explicit zero should override default + Enabled: &enabled, // explicit false should override default + }, defaults) + require.NoError(t, err) + assert.Equal(t, "age=0&enabled=false&name=", values.Encode()) + }) + + t.Run("nil input returns empty values", func(t *testing.T) { + defaults := map[string]any{ + "name": "default-name", + "age": 25, + } + + // Test with nil + values, err := QueryValuesWithDefaults(nil, defaults) + require.NoError(t, err) + assert.Empty(t, values) + + // Test with nil pointer + type example struct { + Name string `json:"name" url:"name"` + } + var nilPtr *example + values, err = QueryValuesWithDefaults(nilPtr, defaults) + require.NoError(t, err) + assert.Empty(t, values) + }) +} diff --git a/seed/go-sdk/idempotency-headers/internal/retrier.go b/seed/go-sdk/idempotency-headers/internal/retrier.go new file mode 100644 index 000000000000..4efae1b4c286 --- /dev/null +++ b/seed/go-sdk/idempotency-headers/internal/retrier.go @@ -0,0 +1,230 @@ +package internal + +import ( + "crypto/rand" + "math/big" + "net/http" + "strconv" + "time" +) + +const ( + defaultRetryAttempts = 2 + minRetryDelay = 1000 * time.Millisecond + maxRetryDelay = 60000 * time.Millisecond +) + +// RetryOption adapts the behavior the *Retrier. +type RetryOption func(*retryOptions) + +// RetryFunc is a retryable HTTP function call (i.e. *http.Client.Do). +type RetryFunc func(*http.Request) (*http.Response, error) + +// WithMaxAttempts configures the maximum number of attempts +// of the *Retrier. +func WithMaxAttempts(attempts uint) RetryOption { + return func(opts *retryOptions) { + opts.attempts = attempts + } +} + +// Retrier retries failed requests a configurable number of times with an +// exponential back-off between each retry. +type Retrier struct { + attempts uint +} + +// NewRetrier constructs a new *Retrier with the given options, if any. +func NewRetrier(opts ...RetryOption) *Retrier { + options := new(retryOptions) + for _, opt := range opts { + opt(options) + } + attempts := uint(defaultRetryAttempts) + if options.attempts > 0 { + attempts = options.attempts + } + return &Retrier{ + attempts: attempts, + } +} + +// Run issues the request and, upon failure, retries the request if possible. +// +// The request will be retried as long as the request is deemed retryable and the +// number of retry attempts has not grown larger than the configured retry limit. +func (r *Retrier) Run( + fn RetryFunc, + request *http.Request, + errorDecoder ErrorDecoder, + opts ...RetryOption, +) (*http.Response, error) { + options := new(retryOptions) + for _, opt := range opts { + opt(options) + } + maxRetryAttempts := r.attempts + if options.attempts > 0 { + maxRetryAttempts = options.attempts + } + var ( + retryAttempt uint + previousError error + ) + return r.run( + fn, + request, + errorDecoder, + maxRetryAttempts, + retryAttempt, + previousError, + ) +} + +func (r *Retrier) run( + fn RetryFunc, + request *http.Request, + errorDecoder ErrorDecoder, + maxRetryAttempts uint, + retryAttempt uint, + previousError error, +) (*http.Response, error) { + if retryAttempt >= maxRetryAttempts { + return nil, previousError + } + + // If the call has been cancelled, don't issue the request. + if err := request.Context().Err(); err != nil { + return nil, err + } + + response, err := fn(request) + if err != nil { + return nil, err + } + + if r.shouldRetry(response) { + defer response.Body.Close() + + delay, err := r.retryDelay(response, retryAttempt) + if err != nil { + return nil, err + } + + time.Sleep(delay) + + return r.run( + fn, + request, + errorDecoder, + maxRetryAttempts, + retryAttempt+1, + decodeError(response, errorDecoder), + ) + } + + return response, nil +} + +// shouldRetry returns true if the request should be retried based on the given +// response status code. +func (r *Retrier) shouldRetry(response *http.Response) bool { + return response.StatusCode == http.StatusTooManyRequests || + response.StatusCode == http.StatusRequestTimeout || + response.StatusCode >= http.StatusInternalServerError +} + +// retryDelay calculates the delay time based on response headers, +// falling back to exponential backoff if no headers are present. +func (r *Retrier) retryDelay(response *http.Response, retryAttempt uint) (time.Duration, error) { + // Check for Retry-After header first (RFC 7231), applying no jitter + if retryAfter := response.Header.Get("Retry-After"); retryAfter != "" { + // Parse as number of seconds... + if seconds, err := strconv.Atoi(retryAfter); err == nil { + delay := time.Duration(seconds) * time.Second + if delay > 0 { + if delay > maxRetryDelay { + delay = maxRetryDelay + } + return delay, nil + } + } + + // ...or as an HTTP date; both are valid + if retryTime, err := time.Parse(time.RFC1123, retryAfter); err == nil { + delay := time.Until(retryTime) + if delay > 0 { + if delay > maxRetryDelay { + delay = maxRetryDelay + } + return delay, nil + } + } + } + + // Then check for industry-standard X-RateLimit-Reset header, applying positive jitter + if rateLimitReset := response.Header.Get("X-RateLimit-Reset"); rateLimitReset != "" { + if resetTimestamp, err := strconv.ParseInt(rateLimitReset, 10, 64); err == nil { + // Assume Unix timestamp in seconds + resetTime := time.Unix(resetTimestamp, 0) + delay := time.Until(resetTime) + if delay > 0 { + if delay > maxRetryDelay { + delay = maxRetryDelay + } + return r.addPositiveJitter(delay) + } + } + } + + // Fall back to exponential backoff + return r.exponentialBackoff(retryAttempt) +} + +// exponentialBackoff calculates the delay time based on the retry attempt +// and applies symmetric jitter (±10% around the delay). +func (r *Retrier) exponentialBackoff(retryAttempt uint) (time.Duration, error) { + if retryAttempt > 63 { // 2^63+ would overflow uint64 + retryAttempt = 63 + } + + delay := minRetryDelay << retryAttempt + if delay > maxRetryDelay { + delay = maxRetryDelay + } + + return r.addSymmetricJitter(delay) +} + +// addJitterWithRange applies jitter to the given delay. +// minPercent and maxPercent define the jitter range (e.g., 100, 120 for +0% to +20%). +func (r *Retrier) addJitterWithRange(delay time.Duration, minPercent, maxPercent int) (time.Duration, error) { + jitterRange := big.NewInt(int64(delay * time.Duration(maxPercent-minPercent) / 100)) + jitter, err := rand.Int(rand.Reader, jitterRange) + if err != nil { + return 0, err + } + + jitteredDelay := delay + time.Duration(jitter.Int64()) + delay*time.Duration(minPercent-100)/100 + if jitteredDelay < minRetryDelay { + jitteredDelay = minRetryDelay + } + if jitteredDelay > maxRetryDelay { + jitteredDelay = maxRetryDelay + } + return jitteredDelay, nil +} + +// addPositiveJitter applies positive jitter to the given delay (100%-120% range). +func (r *Retrier) addPositiveJitter(delay time.Duration) (time.Duration, error) { + return r.addJitterWithRange(delay, 100, 120) +} + +// addSymmetricJitter applies symmetric jitter to the given delay (90%-110% range). +func (r *Retrier) addSymmetricJitter(delay time.Duration) (time.Duration, error) { + return r.addJitterWithRange(delay, 90, 110) +} + +type retryOptions struct { + attempts uint +} diff --git a/seed/go-sdk/idempotency-headers/internal/retrier_test.go b/seed/go-sdk/idempotency-headers/internal/retrier_test.go new file mode 100644 index 000000000000..31768996da2b --- /dev/null +++ b/seed/go-sdk/idempotency-headers/internal/retrier_test.go @@ -0,0 +1,300 @@ +package internal + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/idempotency-headers/fern/core" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type RetryTestCase struct { + description string + + giveAttempts uint + giveStatusCodes []int + giveResponse *InternalTestResponse + + wantResponse *InternalTestResponse + wantError *core.APIError +} + +func TestRetrier(t *testing.T) { + tests := []*RetryTestCase{ + { + description: "retry request succeeds after multiple failures", + giveAttempts: 3, + giveStatusCodes: []int{ + http.StatusServiceUnavailable, + http.StatusServiceUnavailable, + http.StatusOK, + }, + giveResponse: &InternalTestResponse{ + Id: "1", + }, + wantResponse: &InternalTestResponse{ + Id: "1", + }, + }, + { + description: "retry request fails if MaxAttempts is exceeded", + giveAttempts: 3, + giveStatusCodes: []int{ + http.StatusRequestTimeout, + http.StatusRequestTimeout, + http.StatusRequestTimeout, + http.StatusOK, + }, + wantError: &core.APIError{ + StatusCode: http.StatusRequestTimeout, + }, + }, + { + description: "retry durations increase exponentially and stay within the min and max delay values", + giveAttempts: 4, + giveStatusCodes: []int{ + http.StatusServiceUnavailable, + http.StatusServiceUnavailable, + http.StatusServiceUnavailable, + http.StatusOK, + }, + }, + { + description: "retry does not occur on status code 404", + giveAttempts: 2, + giveStatusCodes: []int{http.StatusNotFound, http.StatusOK}, + wantError: &core.APIError{ + StatusCode: http.StatusNotFound, + }, + }, + { + description: "retries occur on status code 429", + giveAttempts: 2, + giveStatusCodes: []int{http.StatusTooManyRequests, http.StatusOK}, + }, + { + description: "retries occur on status code 408", + giveAttempts: 2, + giveStatusCodes: []int{http.StatusRequestTimeout, http.StatusOK}, + }, + { + description: "retries occur on status code 500", + giveAttempts: 2, + giveStatusCodes: []int{http.StatusInternalServerError, http.StatusOK}, + }, + } + + for _, tc := range tests { + t.Run(tc.description, func(t *testing.T) { + var ( + test = tc + server = newTestRetryServer(t, test) + client = server.Client() + ) + + t.Parallel() + + caller := NewCaller( + &CallerParams{ + Client: client, + }, + ) + + var response *InternalTestResponse + _, err := caller.Call( + context.Background(), + &CallParams{ + URL: server.URL, + Method: http.MethodGet, + Request: &InternalTestRequest{}, + Response: &response, + MaxAttempts: test.giveAttempts, + ResponseIsOptional: true, + }, + ) + + if test.wantError != nil { + require.IsType(t, err, &core.APIError{}) + expectedErrorCode := test.wantError.StatusCode + actualErrorCode := err.(*core.APIError).StatusCode + assert.Equal(t, expectedErrorCode, actualErrorCode) + return + } + + require.NoError(t, err) + assert.Equal(t, test.wantResponse, response) + }) + } +} + +// newTestRetryServer returns a new *httptest.Server configured with the +// given test parameters, suitable for testing retries. +func newTestRetryServer(t *testing.T, tc *RetryTestCase) *httptest.Server { + var index int + timestamps := make([]time.Time, 0, len(tc.giveStatusCodes)) + + return httptest.NewServer( + http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + timestamps = append(timestamps, time.Now()) + if index > 0 && index < len(expectedRetryDurations) { + // Ensure that the duration between retries increases exponentially, + // and that it is within the minimum and maximum retry delay values. + actualDuration := timestamps[index].Sub(timestamps[index-1]) + expectedDurationMin := expectedRetryDurations[index-1] * 50 / 100 + expectedDurationMax := expectedRetryDurations[index-1] * 150 / 100 + assert.True( + t, + actualDuration >= expectedDurationMin && actualDuration <= expectedDurationMax, + "expected duration to be in range [%v, %v], got %v", + expectedDurationMin, + expectedDurationMax, + actualDuration, + ) + assert.LessOrEqual( + t, + actualDuration, + maxRetryDelay, + "expected duration to be less than the maxRetryDelay (%v), got %v", + maxRetryDelay, + actualDuration, + ) + assert.GreaterOrEqual( + t, + actualDuration, + minRetryDelay, + "expected duration to be greater than the minRetryDelay (%v), got %v", + minRetryDelay, + actualDuration, + ) + } + + request := new(InternalTestRequest) + bytes, err := io.ReadAll(r.Body) + require.NoError(t, err) + require.NoError(t, json.Unmarshal(bytes, request)) + require.LessOrEqual(t, index, len(tc.giveStatusCodes)) + + statusCode := tc.giveStatusCodes[index] + + w.WriteHeader(statusCode) + + if tc.giveResponse != nil && statusCode == http.StatusOK { + bytes, err = json.Marshal(tc.giveResponse) + require.NoError(t, err) + _, err = w.Write(bytes) + require.NoError(t, err) + } + + index++ + }, + ), + ) +} + +// expectedRetryDurations holds an array of calculated retry durations, +// where the index of the array should correspond to the retry attempt. +// +// Values are calculated based off of `minRetryDelay * 2^i`. +var expectedRetryDurations = []time.Duration{ + 1000 * time.Millisecond, // 500ms * 2^1 = 1000ms + 2000 * time.Millisecond, // 500ms * 2^2 = 2000ms + 4000 * time.Millisecond, // 500ms * 2^3 = 4000ms + 8000 * time.Millisecond, // 500ms * 2^4 = 8000ms +} + +func TestRetryDelayTiming(t *testing.T) { + tests := []struct { + name string + headerName string + headerValueFunc func() string + expectedMinMs int64 + expectedMaxMs int64 + }{ + { + name: "retry-after with seconds value", + headerName: "retry-after", + headerValueFunc: func() string { + return "1" + }, + expectedMinMs: 500, + expectedMaxMs: 1500, + }, + { + name: "retry-after with HTTP date", + headerName: "retry-after", + headerValueFunc: func() string { + return time.Now().Add(3 * time.Second).Format(time.RFC1123) + }, + expectedMinMs: 1500, + expectedMaxMs: 4500, + }, + { + name: "x-ratelimit-reset with future timestamp", + headerName: "x-ratelimit-reset", + headerValueFunc: func() string { + return fmt.Sprintf("%d", time.Now().Add(3*time.Second).Unix()) + }, + expectedMinMs: 1500, + expectedMaxMs: 4500, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + var timestamps []time.Time + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + timestamps = append(timestamps, time.Now()) + if len(timestamps) == 1 { + // First request - return retryable error with header + w.Header().Set(tt.headerName, tt.headerValueFunc()) + w.WriteHeader(http.StatusTooManyRequests) + } else { + // Second request - return success + w.WriteHeader(http.StatusOK) + response := &InternalTestResponse{Id: "success"} + bytes, _ := json.Marshal(response) + w.Write(bytes) + } + })) + defer server.Close() + + caller := NewCaller(&CallerParams{ + Client: server.Client(), + }) + + var response *InternalTestResponse + _, err := caller.Call( + context.Background(), + &CallParams{ + URL: server.URL, + Method: http.MethodGet, + Request: &InternalTestRequest{}, + Response: &response, + MaxAttempts: 2, + ResponseIsOptional: true, + }, + ) + + require.NoError(t, err) + require.Len(t, timestamps, 2, "Expected exactly 2 requests") + + actualDelayMs := timestamps[1].Sub(timestamps[0]).Milliseconds() + + assert.GreaterOrEqual(t, actualDelayMs, tt.expectedMinMs, + "Actual delay %dms should be >= expected min %dms", actualDelayMs, tt.expectedMinMs) + assert.LessOrEqual(t, actualDelayMs, tt.expectedMaxMs, + "Actual delay %dms should be <= expected max %dms", actualDelayMs, tt.expectedMaxMs) + }) + } +} diff --git a/seed/go-sdk/idempotency-headers/internal/stringer.go b/seed/go-sdk/idempotency-headers/internal/stringer.go new file mode 100644 index 000000000000..312801851e0e --- /dev/null +++ b/seed/go-sdk/idempotency-headers/internal/stringer.go @@ -0,0 +1,13 @@ +package internal + +import "encoding/json" + +// StringifyJSON returns a pretty JSON string representation of +// the given value. +func StringifyJSON(value interface{}) (string, error) { + bytes, err := json.MarshalIndent(value, "", " ") + if err != nil { + return "", err + } + return string(bytes), nil +} diff --git a/seed/go-sdk/idempotency-headers/internal/time.go b/seed/go-sdk/idempotency-headers/internal/time.go new file mode 100644 index 000000000000..ab0e269fade3 --- /dev/null +++ b/seed/go-sdk/idempotency-headers/internal/time.go @@ -0,0 +1,137 @@ +package internal + +import ( + "encoding/json" + "time" +) + +const dateFormat = "2006-01-02" + +// DateTime wraps time.Time and adapts its JSON representation +// to conform to a RFC3339 date (e.g. 2006-01-02). +// +// Ref: https://ijmacd.github.io/rfc3339-iso8601 +type Date struct { + t *time.Time +} + +// NewDate returns a new *Date. If the given time.Time +// is nil, nil will be returned. +func NewDate(t time.Time) *Date { + return &Date{t: &t} +} + +// NewOptionalDate returns a new *Date. If the given time.Time +// is nil, nil will be returned. +func NewOptionalDate(t *time.Time) *Date { + if t == nil { + return nil + } + return &Date{t: t} +} + +// Time returns the Date's underlying time, if any. If the +// date is nil, the zero value is returned. +func (d *Date) Time() time.Time { + if d == nil || d.t == nil { + return time.Time{} + } + return *d.t +} + +// TimePtr returns a pointer to the Date's underlying time.Time, if any. +func (d *Date) TimePtr() *time.Time { + if d == nil || d.t == nil { + return nil + } + if d.t.IsZero() { + return nil + } + return d.t +} + +func (d *Date) MarshalJSON() ([]byte, error) { + if d == nil || d.t == nil { + return nil, nil + } + return json.Marshal(d.t.Format(dateFormat)) +} + +func (d *Date) UnmarshalJSON(data []byte) error { + var raw string + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + + parsedTime, err := time.Parse(dateFormat, raw) + if err != nil { + return err + } + + *d = Date{t: &parsedTime} + return nil +} + +// DateTime wraps time.Time and adapts its JSON representation +// to conform to a RFC3339 date-time (e.g. 2017-07-21T17:32:28Z). +// +// Ref: https://ijmacd.github.io/rfc3339-iso8601 +type DateTime struct { + t *time.Time +} + +// NewDateTime returns a new *DateTime. +func NewDateTime(t time.Time) *DateTime { + return &DateTime{t: &t} +} + +// NewOptionalDateTime returns a new *DateTime. If the given time.Time +// is nil, nil will be returned. +func NewOptionalDateTime(t *time.Time) *DateTime { + if t == nil { + return nil + } + return &DateTime{t: t} +} + +// Time returns the DateTime's underlying time, if any. If the +// date-time is nil, the zero value is returned. +func (d *DateTime) Time() time.Time { + if d == nil || d.t == nil { + return time.Time{} + } + return *d.t +} + +// TimePtr returns a pointer to the DateTime's underlying time.Time, if any. +func (d *DateTime) TimePtr() *time.Time { + if d == nil || d.t == nil { + return nil + } + if d.t.IsZero() { + return nil + } + return d.t +} + +func (d *DateTime) MarshalJSON() ([]byte, error) { + if d == nil || d.t == nil { + return nil, nil + } + return json.Marshal(d.t.Format(time.RFC3339)) +} + +func (d *DateTime) UnmarshalJSON(data []byte) error { + var raw string + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + + parsedTime, err := time.Parse(time.RFC3339, raw) + if err != nil { + return err + } + + *d = DateTime{t: &parsedTime} + return nil +} diff --git a/seed/go-sdk/idempotency-headers/option/idempotent_request_option.go b/seed/go-sdk/idempotency-headers/option/idempotent_request_option.go new file mode 100644 index 000000000000..ca6d4d4b62c8 --- /dev/null +++ b/seed/go-sdk/idempotency-headers/option/idempotent_request_option.go @@ -0,0 +1,24 @@ +// Code generated by Fern. DO NOT EDIT. + +package option + +import ( + core "github.com/idempotency-headers/fern/core" +) + +// IdempotentRequestOption adapts the behavior of an individual request. +type IdempotentRequestOption = core.IdempotentRequestOption + +// WithIdempotencyKey sets the idempotencyKey request header. +func WithIdempotencyKey(idempotencyKey string) *core.IdempotencyKeyOption { + return &core.IdempotencyKeyOption{ + IdempotencyKey: idempotencyKey, + } +} + +// WithIdempotencyExpiration sets the idempotencyExpiration request header. +func WithIdempotencyExpiration(idempotencyExpiration int) *core.IdempotencyExpirationOption { + return &core.IdempotencyExpirationOption{ + IdempotencyExpiration: idempotencyExpiration, + } +} diff --git a/seed/go-sdk/idempotency-headers/option/request_option.go b/seed/go-sdk/idempotency-headers/option/request_option.go new file mode 100644 index 000000000000..395b097fbc69 --- /dev/null +++ b/seed/go-sdk/idempotency-headers/option/request_option.go @@ -0,0 +1,71 @@ +// Code generated by Fern. DO NOT EDIT. + +package option + +import ( + core "github.com/idempotency-headers/fern/core" + http "net/http" + url "net/url" +) + +// RequestOption adapts the behavior of an individual request. +type RequestOption = core.RequestOption + +// WithBaseURL sets the base URL, overriding the default +// environment, if any. +func WithBaseURL(baseURL string) *core.BaseURLOption { + return &core.BaseURLOption{ + BaseURL: baseURL, + } +} + +// WithHTTPClient uses the given HTTPClient to issue the request. +func WithHTTPClient(httpClient core.HTTPClient) *core.HTTPClientOption { + return &core.HTTPClientOption{ + HTTPClient: httpClient, + } +} + +// WithHTTPHeader adds the given http.Header to the request. +func WithHTTPHeader(httpHeader http.Header) *core.HTTPHeaderOption { + return &core.HTTPHeaderOption{ + // Clone the headers so they can't be modified after the option call. + HTTPHeader: httpHeader.Clone(), + } +} + +// WithBodyProperties adds the given body properties to the request. +func WithBodyProperties(bodyProperties map[string]interface{}) *core.BodyPropertiesOption { + copiedBodyProperties := make(map[string]interface{}, len(bodyProperties)) + for key, value := range bodyProperties { + copiedBodyProperties[key] = value + } + return &core.BodyPropertiesOption{ + BodyProperties: copiedBodyProperties, + } +} + +// WithQueryParameters adds the given query parameters to the request. +func WithQueryParameters(queryParameters url.Values) *core.QueryParametersOption { + copiedQueryParameters := make(url.Values, len(queryParameters)) + for key, values := range queryParameters { + copiedQueryParameters[key] = values + } + return &core.QueryParametersOption{ + QueryParameters: copiedQueryParameters, + } +} + +// WithMaxAttempts configures the maximum number of retry attempts. +func WithMaxAttempts(attempts uint) *core.MaxAttemptsOption { + return &core.MaxAttemptsOption{ + MaxAttempts: attempts, + } +} + +// WithToken sets the 'Authorization: Bearer ' request header. +func WithToken(token string) *core.TokenOption { + return &core.TokenOption{ + Token: token, + } +} diff --git a/seed/go-sdk/idempotency-headers/payment/client.go b/seed/go-sdk/idempotency-headers/payment/client.go new file mode 100644 index 000000000000..c254ebcafd77 --- /dev/null +++ b/seed/go-sdk/idempotency-headers/payment/client.go @@ -0,0 +1,66 @@ +// Code generated by Fern. DO NOT EDIT. + +package payment + +import ( + context "context" + uuid "github.com/google/uuid" + fern "github.com/idempotency-headers/fern" + core "github.com/idempotency-headers/fern/core" + internal "github.com/idempotency-headers/fern/internal" + option "github.com/idempotency-headers/fern/option" +) + +type Client struct { + WithRawResponse *RawClient + + options *core.RequestOptions + baseURL string + caller *internal.Caller +} + +func NewClient(options *core.RequestOptions) *Client { + return &Client{ + WithRawResponse: NewRawClient(options), + options: options, + baseURL: options.BaseURL, + caller: internal.NewCaller( + &internal.CallerParams{ + Client: options.HTTPClient, + MaxAttempts: options.MaxAttempts, + }, + ), + } +} + +func (c *Client) Create( + ctx context.Context, + request *fern.CreatePaymentRequest, + opts ...option.IdempotentRequestOption, +) (uuid.UUID, error) { + response, err := c.WithRawResponse.Create( + ctx, + request, + opts..., + ) + if err != nil { + return uuid.UUID{}, err + } + return response.Body, nil +} + +func (c *Client) Delete( + ctx context.Context, + paymentId string, + opts ...option.RequestOption, +) error { + _, err := c.WithRawResponse.Delete( + ctx, + paymentId, + opts..., + ) + if err != nil { + return err + } + return nil +} diff --git a/seed/go-sdk/idempotency-headers/payment/raw_client.go b/seed/go-sdk/idempotency-headers/payment/raw_client.go new file mode 100644 index 000000000000..f86f02e8c882 --- /dev/null +++ b/seed/go-sdk/idempotency-headers/payment/raw_client.go @@ -0,0 +1,114 @@ +// Code generated by Fern. DO NOT EDIT. + +package payment + +import ( + context "context" + uuid "github.com/google/uuid" + fern "github.com/idempotency-headers/fern" + core "github.com/idempotency-headers/fern/core" + internal "github.com/idempotency-headers/fern/internal" + option "github.com/idempotency-headers/fern/option" + http "net/http" +) + +type RawClient struct { + baseURL string + caller *internal.Caller + options *core.RequestOptions +} + +func NewRawClient(options *core.RequestOptions) *RawClient { + return &RawClient{ + options: options, + baseURL: options.BaseURL, + caller: internal.NewCaller( + &internal.CallerParams{ + Client: options.HTTPClient, + MaxAttempts: options.MaxAttempts, + }, + ), + } +} + +func (r *RawClient) Create( + ctx context.Context, + request *fern.CreatePaymentRequest, + opts ...option.IdempotentRequestOption, +) (*core.Response[uuid.UUID], error) { + options := core.NewIdempotentRequestOptions(opts...) + baseURL := internal.ResolveBaseURL( + options.BaseURL, + r.baseURL, + "", + ) + endpointURL := baseURL + "/payment" + headers := internal.MergeHeaders( + r.options.ToHeader(), + options.ToHeader(), + ) + var response uuid.UUID + raw, err := r.caller.Call( + ctx, + &internal.CallParams{ + URL: endpointURL, + Method: http.MethodPost, + Headers: headers, + MaxAttempts: options.MaxAttempts, + BodyProperties: options.BodyProperties, + QueryParameters: options.QueryParameters, + Client: options.HTTPClient, + Request: request, + Response: &response, + }, + ) + if err != nil { + return nil, err + } + return &core.Response[uuid.UUID]{ + StatusCode: raw.StatusCode, + Header: raw.Header, + Body: response, + }, nil +} + +func (r *RawClient) Delete( + ctx context.Context, + paymentId string, + opts ...option.RequestOption, +) (*core.Response[any], error) { + options := core.NewRequestOptions(opts...) + baseURL := internal.ResolveBaseURL( + options.BaseURL, + r.baseURL, + "", + ) + endpointURL := internal.EncodeURL( + baseURL+"/payment/%v", + paymentId, + ) + headers := internal.MergeHeaders( + r.options.ToHeader(), + options.ToHeader(), + ) + raw, err := r.caller.Call( + ctx, + &internal.CallParams{ + URL: endpointURL, + Method: http.MethodDelete, + Headers: headers, + MaxAttempts: options.MaxAttempts, + BodyProperties: options.BodyProperties, + QueryParameters: options.QueryParameters, + Client: options.HTTPClient, + }, + ) + if err != nil { + return nil, err + } + return &core.Response[any]{ + StatusCode: raw.StatusCode, + Header: raw.Header, + Body: nil, + }, nil +} diff --git a/seed/go-sdk/inferred-auth-explicit/dynamic-snippets/example0/snippet.go b/seed/go-sdk/inferred-auth-explicit/dynamic-snippets/example0/snippet.go index 027377289223..186e5e51c15b 100644 --- a/seed/go-sdk/inferred-auth-explicit/dynamic-snippets/example0/snippet.go +++ b/seed/go-sdk/inferred-auth-explicit/dynamic-snippets/example0/snippet.go @@ -12,6 +12,7 @@ func do() { option.WithBaseURL( "https://api.fern.com", ), + nil, ) request := &fern.GetTokenRequest{ XApiKey: "X-Api-Key", diff --git a/seed/go-sdk/inferred-auth-explicit/dynamic-snippets/example1/snippet.go b/seed/go-sdk/inferred-auth-explicit/dynamic-snippets/example1/snippet.go index 0e3860fc6d50..56d5afb371c7 100644 --- a/seed/go-sdk/inferred-auth-explicit/dynamic-snippets/example1/snippet.go +++ b/seed/go-sdk/inferred-auth-explicit/dynamic-snippets/example1/snippet.go @@ -12,6 +12,7 @@ func do() { option.WithBaseURL( "https://api.fern.com", ), + nil, ) request := &fern.RefreshTokenRequest{ XApiKey: "X-Api-Key", diff --git a/seed/go-sdk/inferred-auth-explicit/dynamic-snippets/example2/snippet.go b/seed/go-sdk/inferred-auth-explicit/dynamic-snippets/example2/snippet.go index 91e697c97db8..a66afe5cd265 100644 --- a/seed/go-sdk/inferred-auth-explicit/dynamic-snippets/example2/snippet.go +++ b/seed/go-sdk/inferred-auth-explicit/dynamic-snippets/example2/snippet.go @@ -11,6 +11,7 @@ func do() { option.WithBaseURL( "https://api.fern.com", ), + nil, ) client.NestedNoAuth.Api.GetSomething( context.TODO(), diff --git a/seed/go-sdk/inferred-auth-explicit/dynamic-snippets/example3/snippet.go b/seed/go-sdk/inferred-auth-explicit/dynamic-snippets/example3/snippet.go index e1d3ffbf2c44..dc234749f8ef 100644 --- a/seed/go-sdk/inferred-auth-explicit/dynamic-snippets/example3/snippet.go +++ b/seed/go-sdk/inferred-auth-explicit/dynamic-snippets/example3/snippet.go @@ -11,6 +11,7 @@ func do() { option.WithBaseURL( "https://api.fern.com", ), + nil, ) client.Nested.Api.GetSomething( context.TODO(), diff --git a/seed/go-sdk/inferred-auth-explicit/dynamic-snippets/example4/snippet.go b/seed/go-sdk/inferred-auth-explicit/dynamic-snippets/example4/snippet.go index 1ced2321514b..3711944e7e22 100644 --- a/seed/go-sdk/inferred-auth-explicit/dynamic-snippets/example4/snippet.go +++ b/seed/go-sdk/inferred-auth-explicit/dynamic-snippets/example4/snippet.go @@ -11,6 +11,7 @@ func do() { option.WithBaseURL( "https://api.fern.com", ), + nil, ) client.Simple.GetSomething( context.TODO(), diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/.fern/metadata.json b/seed/go-sdk/inferred-auth-implicit-api-key/.fern/metadata.json new file mode 100644 index 000000000000..13cf07b7fbfd --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/.fern/metadata.json @@ -0,0 +1,8 @@ +{ + "cliVersion": "DUMMY", + "generatorName": "fernapi/fern-go-sdk", + "generatorVersion": "latest", + "generatorConfig": { + "enableWireTests": false + } +} \ No newline at end of file diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/.github/workflows/ci.yml b/seed/go-sdk/inferred-auth-implicit-api-key/.github/workflows/ci.yml new file mode 100644 index 000000000000..56310d69624b --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/.github/workflows/ci.yml @@ -0,0 +1,35 @@ +name: ci + +on: [push] + +jobs: + compile: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up go + uses: actions/setup-go@v4 + + - name: Compile + run: go build ./... + test: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up go + uses: actions/setup-go@v4 + + - name: Setup wiremock server + run: | + if [ -f wiremock/docker-compose.test.yml ]; then docker compose -f wiremock/docker-compose.test.yml down && docker compose -f wiremock/docker-compose.test.yml up -d; fi + + - name: Test + run: go test ./... + + - name: Teardown wiremock server + run: | + if [ -f wiremock/docker-compose.test.yml ]; then docker compose -f wiremock/docker-compose.test.yml down; fi diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/README.md b/seed/go-sdk/inferred-auth-implicit-api-key/README.md new file mode 100644 index 000000000000..18092ea3824a --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/README.md @@ -0,0 +1,195 @@ +# Seed Go Library + +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FGo) + +The Seed Go library provides convenient access to the Seed APIs from Go. + +## Table of Contents + +- [Reference](#reference) +- [Usage](#usage) +- [Environments](#environments) +- [Errors](#errors) +- [Request Options](#request-options) +- [Advanced](#advanced) + - [Response Headers](#response-headers) + - [Retries](#retries) + - [Timeouts](#timeouts) + - [Explicit Null](#explicit-null) +- [Contributing](#contributing) + +## Reference + +A full reference for this library is available [here](./reference.md). + +## Usage + +Instantiate and use the client with the following: + +```go +package example + +import ( + client "github.com/inferred-auth-implicit-api-key/fern/client" + fern "github.com/inferred-auth-implicit-api-key/fern" + context "context" +) + +func do() { + client := client.NewClient( + nil, + ) + request := &fern.GetTokenRequest{ + ApiKey: "api_key", + } + client.Auth.GetToken( + context.TODO(), + request, + ) +} +``` + +## Environments + +You can choose between different environments by using the `option.WithBaseURL` option. You can configure any arbitrary base +URL, which is particularly useful in test environments. + +```go +client := client.NewClient( + option.WithBaseURL("https://example.com"), +) +``` + +## Errors + +Structured error types are returned from API calls that return non-success status codes. These errors are compatible +with the `errors.Is` and `errors.As` APIs, so you can access the error like so: + +```go +response, err := client.Auth.GetToken(...) +if err != nil { + var apiError *core.APIError + if errors.As(err, apiError) { + // Do something with the API error ... + } + return err +} +``` + +## Request Options + +A variety of request options are included to adapt the behavior of the library, which includes configuring +authorization tokens, or providing your own instrumented `*http.Client`. + +These request options can either be +specified on the client so that they're applied on every request, or for an individual request, like so: + +> Providing your own `*http.Client` is recommended. Otherwise, the `http.DefaultClient` will be used, +> and your client will wait indefinitely for a response (unless the per-request, context-based timeout +> is used). + +```go +// Specify default options applied on every request. +client := client.NewClient( + option.WithToken(""), + option.WithHTTPClient( + &http.Client{ + Timeout: 5 * time.Second, + }, + ), +) + +// Specify options for an individual request. +response, err := client.Auth.GetToken( + ..., + option.WithToken(""), +) +``` + +## Advanced + +### Response Headers + +You can access the raw HTTP response data by using the `WithRawResponse` field on the client. This is useful +when you need to examine the response headers received from the API call. (When the endpoint is paginated, +the raw HTTP response data will be included automatically in the Page response object.) + +```go +response, err := client.Auth.WithRawResponse.GetToken(...) +if err != nil { + return err +} +fmt.Printf("Got response headers: %v", response.Header) +fmt.Printf("Got status code: %d", response.StatusCode) +``` + +### Retries + +The SDK is instrumented with automatic retries with exponential backoff. A request will be retried as long +as the request is deemed retryable and the number of retry attempts has not grown larger than the configured +retry limit (default: 2). + +A request is deemed retryable when any of the following HTTP status codes is returned: + +- [408](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/408) (Timeout) +- [429](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429) (Too Many Requests) +- [5XX](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500) (Internal Server Errors) + +If the `Retry-After` header is present in the response, the SDK will prioritize respecting its value exactly +over the default exponential backoff. + +Use the `option.WithMaxAttempts` option to configure this behavior for the entire client or an individual request: + +```go +client := client.NewClient( + option.WithMaxAttempts(1), +) + +response, err := client.Auth.GetToken( + ..., + option.WithMaxAttempts(1), +) +``` + +### Timeouts + +Setting a timeout for each individual request is as simple as using the standard context library. Setting a one second timeout for an individual API call looks like the following: + +```go +ctx, cancel := context.WithTimeout(ctx, time.Second) +defer cancel() + +response, err := client.Auth.GetToken(ctx, ...) +``` + +### Explicit Null + +If you want to send the explicit `null` JSON value through an optional parameter, you can use the setters\ +that come with every object. Calling a setter method for a property will flip a bit in the `explicitFields` +bitfield for that setter's object; during serialization, any property with a flipped bit will have its +omittable status stripped, so zero or `nil` values will be sent explicitly rather than omitted altogether: + +```go +type ExampleRequest struct { + // An optional string parameter. + Name *string `json:"name,omitempty" url:"-"` + + // Private bitmask of fields set to an explicit value and therefore not to be omitted + explicitFields *big.Int `json:"-" url:"-"` +} + +request := &ExampleRequest{} +request.SetName(nil) + +response, err := client.Auth.GetToken(ctx, request, ...) +``` + +## Contributing + +While we value open-source contributions to this SDK, this library is generated programmatically. +Additions made directly to this library would have to be moved over to our generation code, +otherwise they would be overwritten upon the next generated release. Feel free to open a PR as +a proof of concept, but know that we will not be able to merge it as-is. We suggest opening +an issue first to discuss with us! + +On the other hand, contributions to the README are always very welcome! \ No newline at end of file diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/auth.go b/seed/go-sdk/inferred-auth-implicit-api-key/auth.go new file mode 100644 index 000000000000..d52177c7d502 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/auth.go @@ -0,0 +1,162 @@ +// Code generated by Fern. DO NOT EDIT. + +package inferredauthimplicitapikey + +import ( + json "encoding/json" + fmt "fmt" + internal "github.com/inferred-auth-implicit-api-key/fern/internal" + big "math/big" +) + +var ( + getTokenRequestFieldApiKey = big.NewInt(1 << 0) +) + +type GetTokenRequest struct { + ApiKey string `json:"-" url:"-"` + + // Private bitmask of fields set to an explicit value and therefore not to be omitted + explicitFields *big.Int `json:"-" url:"-"` +} + +func (g *GetTokenRequest) require(field *big.Int) { + if g.explicitFields == nil { + g.explicitFields = big.NewInt(0) + } + g.explicitFields.Or(g.explicitFields, field) +} + +// SetApiKey sets the ApiKey field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (g *GetTokenRequest) SetApiKey(apiKey string) { + g.ApiKey = apiKey + g.require(getTokenRequestFieldApiKey) +} + +// An auth token response. +var ( + tokenResponseFieldAccessToken = big.NewInt(1 << 0) + tokenResponseFieldTokenType = big.NewInt(1 << 1) + tokenResponseFieldExpiresIn = big.NewInt(1 << 2) + tokenResponseFieldScope = big.NewInt(1 << 3) +) + +type TokenResponse struct { + AccessToken string `json:"access_token" url:"access_token"` + TokenType string `json:"token_type" url:"token_type"` + ExpiresIn int `json:"expires_in" url:"expires_in"` + Scope *string `json:"scope,omitempty" url:"scope,omitempty"` + + // Private bitmask of fields set to an explicit value and therefore not to be omitted + explicitFields *big.Int `json:"-" url:"-"` + + extraProperties map[string]interface{} + rawJSON json.RawMessage +} + +func (t *TokenResponse) GetAccessToken() string { + if t == nil { + return "" + } + return t.AccessToken +} + +func (t *TokenResponse) GetTokenType() string { + if t == nil { + return "" + } + return t.TokenType +} + +func (t *TokenResponse) GetExpiresIn() int { + if t == nil { + return 0 + } + return t.ExpiresIn +} + +func (t *TokenResponse) GetScope() *string { + if t == nil { + return nil + } + return t.Scope +} + +func (t *TokenResponse) GetExtraProperties() map[string]interface{} { + return t.extraProperties +} + +func (t *TokenResponse) require(field *big.Int) { + if t.explicitFields == nil { + t.explicitFields = big.NewInt(0) + } + t.explicitFields.Or(t.explicitFields, field) +} + +// SetAccessToken sets the AccessToken field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (t *TokenResponse) SetAccessToken(accessToken string) { + t.AccessToken = accessToken + t.require(tokenResponseFieldAccessToken) +} + +// SetTokenType sets the TokenType field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (t *TokenResponse) SetTokenType(tokenType string) { + t.TokenType = tokenType + t.require(tokenResponseFieldTokenType) +} + +// SetExpiresIn sets the ExpiresIn field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (t *TokenResponse) SetExpiresIn(expiresIn int) { + t.ExpiresIn = expiresIn + t.require(tokenResponseFieldExpiresIn) +} + +// SetScope sets the Scope field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (t *TokenResponse) SetScope(scope *string) { + t.Scope = scope + t.require(tokenResponseFieldScope) +} + +func (t *TokenResponse) UnmarshalJSON(data []byte) error { + type unmarshaler TokenResponse + var value unmarshaler + if err := json.Unmarshal(data, &value); err != nil { + return err + } + *t = TokenResponse(value) + extraProperties, err := internal.ExtractExtraProperties(data, *t) + if err != nil { + return err + } + t.extraProperties = extraProperties + t.rawJSON = json.RawMessage(data) + return nil +} + +func (t *TokenResponse) MarshalJSON() ([]byte, error) { + type embed TokenResponse + var marshaler = struct { + embed + }{ + embed: embed(*t), + } + explicitMarshaler := internal.HandleExplicitFields(marshaler, t.explicitFields) + return json.Marshal(explicitMarshaler) +} + +func (t *TokenResponse) String() string { + if len(t.rawJSON) > 0 { + if value, err := internal.StringifyJSON(t.rawJSON); err == nil { + return value + } + } + if value, err := internal.StringifyJSON(t); err == nil { + return value + } + return fmt.Sprintf("%#v", t) +} diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/auth/client.go b/seed/go-sdk/inferred-auth-implicit-api-key/auth/client.go new file mode 100644 index 000000000000..d4b137c7c551 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/auth/client.go @@ -0,0 +1,49 @@ +// Code generated by Fern. DO NOT EDIT. + +package auth + +import ( + context "context" + fern "github.com/inferred-auth-implicit-api-key/fern" + core "github.com/inferred-auth-implicit-api-key/fern/core" + internal "github.com/inferred-auth-implicit-api-key/fern/internal" + option "github.com/inferred-auth-implicit-api-key/fern/option" +) + +type Client struct { + WithRawResponse *RawClient + + options *core.RequestOptions + baseURL string + caller *internal.Caller +} + +func NewClient(options *core.RequestOptions) *Client { + return &Client{ + WithRawResponse: NewRawClient(options), + options: options, + baseURL: options.BaseURL, + caller: internal.NewCaller( + &internal.CallerParams{ + Client: options.HTTPClient, + MaxAttempts: options.MaxAttempts, + }, + ), + } +} + +func (c *Client) GetToken( + ctx context.Context, + request *fern.GetTokenRequest, + opts ...option.RequestOption, +) (*fern.TokenResponse, error) { + response, err := c.WithRawResponse.GetToken( + ctx, + request, + opts..., + ) + if err != nil { + return nil, err + } + return response.Body, nil +} diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/auth/raw_client.go b/seed/go-sdk/inferred-auth-implicit-api-key/auth/raw_client.go new file mode 100644 index 000000000000..2b81caba9479 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/auth/raw_client.go @@ -0,0 +1,72 @@ +// Code generated by Fern. DO NOT EDIT. + +package auth + +import ( + context "context" + fern "github.com/inferred-auth-implicit-api-key/fern" + core "github.com/inferred-auth-implicit-api-key/fern/core" + internal "github.com/inferred-auth-implicit-api-key/fern/internal" + option "github.com/inferred-auth-implicit-api-key/fern/option" + http "net/http" +) + +type RawClient struct { + baseURL string + caller *internal.Caller + options *core.RequestOptions +} + +func NewRawClient(options *core.RequestOptions) *RawClient { + return &RawClient{ + options: options, + baseURL: options.BaseURL, + caller: internal.NewCaller( + &internal.CallerParams{ + Client: options.HTTPClient, + MaxAttempts: options.MaxAttempts, + }, + ), + } +} + +func (r *RawClient) GetToken( + ctx context.Context, + request *fern.GetTokenRequest, + opts ...option.RequestOption, +) (*core.Response[*fern.TokenResponse], error) { + options := core.NewRequestOptions(opts...) + baseURL := internal.ResolveBaseURL( + options.BaseURL, + r.baseURL, + "", + ) + endpointURL := baseURL + "/token" + headers := internal.MergeHeaders( + r.options.ToHeader(), + options.ToHeader(), + ) + headers.Add("X-Api-Key", request.ApiKey) + var response *fern.TokenResponse + raw, err := r.caller.Call( + ctx, + &internal.CallParams{ + URL: endpointURL, + Method: http.MethodPost, + Headers: headers, + MaxAttempts: options.MaxAttempts, + BodyProperties: options.BodyProperties, + QueryParameters: options.QueryParameters, + Client: options.HTTPClient, + Response: &response, + }, + ) + if err != nil { + return nil, err + } + return &core.Response[*fern.TokenResponse]{ + StatusCode: raw.StatusCode, + Header: raw.Header, + Body: response, + }, nil +} diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/client/client.go b/seed/go-sdk/inferred-auth-implicit-api-key/client/client.go new file mode 100644 index 000000000000..9eb13fb34c2d --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/client/client.go @@ -0,0 +1,42 @@ +// Code generated by Fern. DO NOT EDIT. + +package client + +import ( + auth "github.com/inferred-auth-implicit-api-key/fern/auth" + core "github.com/inferred-auth-implicit-api-key/fern/core" + internal "github.com/inferred-auth-implicit-api-key/fern/internal" + nestedclient "github.com/inferred-auth-implicit-api-key/fern/nested/client" + client "github.com/inferred-auth-implicit-api-key/fern/nestednoauth/client" + option "github.com/inferred-auth-implicit-api-key/fern/option" + simple "github.com/inferred-auth-implicit-api-key/fern/simple" +) + +type Client struct { + Auth *auth.Client + NestedNoAuth *client.Client + Nested *nestedclient.Client + Simple *simple.Client + + options *core.RequestOptions + baseURL string + caller *internal.Caller +} + +func NewClient(opts ...option.RequestOption) *Client { + options := core.NewRequestOptions(opts...) + return &Client{ + Auth: auth.NewClient(options), + NestedNoAuth: client.NewClient(options), + Nested: nestedclient.NewClient(options), + Simple: simple.NewClient(options), + options: options, + baseURL: options.BaseURL, + caller: internal.NewCaller( + &internal.CallerParams{ + Client: options.HTTPClient, + MaxAttempts: options.MaxAttempts, + }, + ), + } +} diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/client/client_test.go b/seed/go-sdk/inferred-auth-implicit-api-key/client/client_test.go new file mode 100644 index 000000000000..297d40f03a45 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/client/client_test.go @@ -0,0 +1,45 @@ +// Code generated by Fern. DO NOT EDIT. + +package client + +import ( + option "github.com/inferred-auth-implicit-api-key/fern/option" + assert "github.com/stretchr/testify/assert" + http "net/http" + testing "testing" + time "time" +) + +func TestNewClient(t *testing.T) { + t.Run("default", func(t *testing.T) { + c := NewClient() + assert.Empty(t, c.baseURL) + }) + + t.Run("base url", func(t *testing.T) { + c := NewClient( + option.WithBaseURL("test.co"), + ) + assert.Equal(t, "test.co", c.baseURL) + }) + + t.Run("http client", func(t *testing.T) { + httpClient := &http.Client{ + Timeout: 5 * time.Second, + } + c := NewClient( + option.WithHTTPClient(httpClient), + ) + assert.Empty(t, c.baseURL) + }) + + t.Run("http header", func(t *testing.T) { + header := make(http.Header) + header.Set("X-API-Tenancy", "test") + c := NewClient( + option.WithHTTPHeader(header), + ) + assert.Empty(t, c.baseURL) + assert.Equal(t, "test", c.options.HTTPHeader.Get("X-API-Tenancy")) + }) +} diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/core/api_error.go b/seed/go-sdk/inferred-auth-implicit-api-key/core/api_error.go new file mode 100644 index 000000000000..6168388541b4 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/core/api_error.go @@ -0,0 +1,47 @@ +package core + +import ( + "fmt" + "net/http" +) + +// APIError is a lightweight wrapper around the standard error +// interface that preserves the status code from the RPC, if any. +type APIError struct { + err error + + StatusCode int `json:"-"` + Header http.Header `json:"-"` +} + +// NewAPIError constructs a new API error. +func NewAPIError(statusCode int, header http.Header, err error) *APIError { + return &APIError{ + err: err, + Header: header, + StatusCode: statusCode, + } +} + +// Unwrap returns the underlying error. This also makes the error compatible +// with errors.As and errors.Is. +func (a *APIError) Unwrap() error { + if a == nil { + return nil + } + return a.err +} + +// Error returns the API error's message. +func (a *APIError) Error() string { + if a == nil || (a.err == nil && a.StatusCode == 0) { + return "" + } + if a.err == nil { + return fmt.Sprintf("%d", a.StatusCode) + } + if a.StatusCode == 0 { + return a.err.Error() + } + return fmt.Sprintf("%d: %s", a.StatusCode, a.err.Error()) +} diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/core/http.go b/seed/go-sdk/inferred-auth-implicit-api-key/core/http.go new file mode 100644 index 000000000000..92c435692940 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/core/http.go @@ -0,0 +1,15 @@ +package core + +import "net/http" + +// HTTPClient is an interface for a subset of the *http.Client. +type HTTPClient interface { + Do(*http.Request) (*http.Response, error) +} + +// Response is an HTTP response from an HTTP client. +type Response[T any] struct { + StatusCode int + Header http.Header + Body T +} diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/core/request_option.go b/seed/go-sdk/inferred-auth-implicit-api-key/core/request_option.go new file mode 100644 index 000000000000..00ef741df07b --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/core/request_option.go @@ -0,0 +1,112 @@ +// Code generated by Fern. DO NOT EDIT. + +package core + +import ( + http "net/http" + url "net/url" +) + +// RequestOption adapts the behavior of the client or an individual request. +type RequestOption interface { + applyRequestOptions(*RequestOptions) +} + +// RequestOptions defines all of the possible request options. +// +// This type is primarily used by the generated code and is not meant +// to be used directly; use the option package instead. +type RequestOptions struct { + BaseURL string + HTTPClient HTTPClient + HTTPHeader http.Header + BodyProperties map[string]interface{} + QueryParameters url.Values + MaxAttempts uint +} + +// NewRequestOptions returns a new *RequestOptions value. +// +// This function is primarily used by the generated code and is not meant +// to be used directly; use RequestOption instead. +func NewRequestOptions(opts ...RequestOption) *RequestOptions { + options := &RequestOptions{ + HTTPHeader: make(http.Header), + BodyProperties: make(map[string]interface{}), + QueryParameters: make(url.Values), + } + for _, opt := range opts { + opt.applyRequestOptions(options) + } + return options +} + +// ToHeader maps the configured request options into a http.Header used +// for the request(s). +func (r *RequestOptions) ToHeader() http.Header { + header := r.cloneHeader() + return header +} + +func (r *RequestOptions) cloneHeader() http.Header { + headers := r.HTTPHeader.Clone() + headers.Set("X-Fern-Language", "Go") + headers.Set("X-Fern-SDK-Name", "github.com/inferred-auth-implicit-api-key/fern") + headers.Set("X-Fern-SDK-Version", "v0.0.1") + headers.Set("User-Agent", "github.com/inferred-auth-implicit-api-key/fern/0.0.1") + return headers +} + +// BaseURLOption implements the RequestOption interface. +type BaseURLOption struct { + BaseURL string +} + +func (b *BaseURLOption) applyRequestOptions(opts *RequestOptions) { + opts.BaseURL = b.BaseURL +} + +// HTTPClientOption implements the RequestOption interface. +type HTTPClientOption struct { + HTTPClient HTTPClient +} + +func (h *HTTPClientOption) applyRequestOptions(opts *RequestOptions) { + opts.HTTPClient = h.HTTPClient +} + +// HTTPHeaderOption implements the RequestOption interface. +type HTTPHeaderOption struct { + HTTPHeader http.Header +} + +func (h *HTTPHeaderOption) applyRequestOptions(opts *RequestOptions) { + opts.HTTPHeader = h.HTTPHeader +} + +// BodyPropertiesOption implements the RequestOption interface. +type BodyPropertiesOption struct { + BodyProperties map[string]interface{} +} + +func (b *BodyPropertiesOption) applyRequestOptions(opts *RequestOptions) { + opts.BodyProperties = b.BodyProperties +} + +// QueryParametersOption implements the RequestOption interface. +type QueryParametersOption struct { + QueryParameters url.Values +} + +func (q *QueryParametersOption) applyRequestOptions(opts *RequestOptions) { + opts.QueryParameters = q.QueryParameters +} + +// MaxAttemptsOption implements the RequestOption interface. +type MaxAttemptsOption struct { + MaxAttempts uint +} + +func (m *MaxAttemptsOption) applyRequestOptions(opts *RequestOptions) { + opts.MaxAttempts = m.MaxAttempts +} diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/dynamic-snippets/example0/snippet.go b/seed/go-sdk/inferred-auth-implicit-api-key/dynamic-snippets/example0/snippet.go new file mode 100644 index 000000000000..6c3d84b35a54 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/dynamic-snippets/example0/snippet.go @@ -0,0 +1,24 @@ +package example + +import ( + client "github.com/inferred-auth-implicit-api-key/fern/client" + option "github.com/inferred-auth-implicit-api-key/fern/option" + fern "github.com/inferred-auth-implicit-api-key/fern" + context "context" +) + +func do() { + client := client.NewClient( + option.WithBaseURL( + "https://api.fern.com", + ), + nil, + ) + request := &fern.GetTokenRequest{ + ApiKey: "api_key", + } + client.Auth.GetToken( + context.TODO(), + request, + ) +} diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/dynamic-snippets/example1/snippet.go b/seed/go-sdk/inferred-auth-implicit-api-key/dynamic-snippets/example1/snippet.go new file mode 100644 index 000000000000..65e6e6e0c6d4 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/dynamic-snippets/example1/snippet.go @@ -0,0 +1,19 @@ +package example + +import ( + client "github.com/inferred-auth-implicit-api-key/fern/client" + option "github.com/inferred-auth-implicit-api-key/fern/option" + context "context" +) + +func do() { + client := client.NewClient( + option.WithBaseURL( + "https://api.fern.com", + ), + nil, + ) + client.NestedNoAuth.Api.GetSomething( + context.TODO(), + ) +} diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/dynamic-snippets/example2/snippet.go b/seed/go-sdk/inferred-auth-implicit-api-key/dynamic-snippets/example2/snippet.go new file mode 100644 index 000000000000..02ee484e7732 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/dynamic-snippets/example2/snippet.go @@ -0,0 +1,19 @@ +package example + +import ( + client "github.com/inferred-auth-implicit-api-key/fern/client" + option "github.com/inferred-auth-implicit-api-key/fern/option" + context "context" +) + +func do() { + client := client.NewClient( + option.WithBaseURL( + "https://api.fern.com", + ), + nil, + ) + client.Nested.Api.GetSomething( + context.TODO(), + ) +} diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/dynamic-snippets/example3/snippet.go b/seed/go-sdk/inferred-auth-implicit-api-key/dynamic-snippets/example3/snippet.go new file mode 100644 index 000000000000..197fc6d3baec --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/dynamic-snippets/example3/snippet.go @@ -0,0 +1,19 @@ +package example + +import ( + client "github.com/inferred-auth-implicit-api-key/fern/client" + option "github.com/inferred-auth-implicit-api-key/fern/option" + context "context" +) + +func do() { + client := client.NewClient( + option.WithBaseURL( + "https://api.fern.com", + ), + nil, + ) + client.Simple.GetSomething( + context.TODO(), + ) +} diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/error_codes.go b/seed/go-sdk/inferred-auth-implicit-api-key/error_codes.go new file mode 100644 index 000000000000..b791c83a2ac6 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/error_codes.go @@ -0,0 +1,9 @@ +// Code generated by Fern. DO NOT EDIT. + +package inferredauthimplicitapikey + +import ( + internal "github.com/inferred-auth-implicit-api-key/fern/internal" +) + +var ErrorCodes internal.ErrorCodes = internal.ErrorCodes{} diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/file_param.go b/seed/go-sdk/inferred-auth-implicit-api-key/file_param.go new file mode 100644 index 000000000000..4d44a3581c60 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/file_param.go @@ -0,0 +1,41 @@ +package inferredauthimplicitapikey + +import ( + "io" +) + +// FileParam is a file type suitable for multipart/form-data uploads. +type FileParam struct { + io.Reader + filename string + contentType string +} + +// FileParamOption adapts the behavior of the FileParam. No options are +// implemented yet, but this interface allows for future extensibility. +type FileParamOption interface { + apply() +} + +// NewFileParam returns a *FileParam type suitable for multipart/form-data uploads. All file +// upload endpoints accept a simple io.Reader, which is usually created by opening a file +// via os.Open. +// +// However, some endpoints require additional metadata about the file such as a specific +// Content-Type or custom filename. FileParam makes it easier to create the correct type +// signature for these endpoints. +func NewFileParam( + reader io.Reader, + filename string, + contentType string, + opts ...FileParamOption, +) *FileParam { + return &FileParam{ + Reader: reader, + filename: filename, + contentType: contentType, + } +} + +func (f *FileParam) Name() string { return f.filename } +func (f *FileParam) ContentType() string { return f.contentType } diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/go.mod b/seed/go-sdk/inferred-auth-implicit-api-key/go.mod new file mode 100644 index 000000000000..ba95e31c6545 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/go.mod @@ -0,0 +1,16 @@ +module github.com/inferred-auth-implicit-api-key/fern + +go 1.21 + +toolchain go1.23.8 + +require github.com/google/uuid v1.6.0 + +require github.com/stretchr/testify v1.8.4 + +require gopkg.in/yaml.v3 v3.0.1 // indirect + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect +) diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/go.sum b/seed/go-sdk/inferred-auth-implicit-api-key/go.sum new file mode 100644 index 000000000000..fcca6d128057 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/go.sum @@ -0,0 +1,12 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/internal/caller.go b/seed/go-sdk/inferred-auth-implicit-api-key/internal/caller.go new file mode 100644 index 000000000000..6997cd4ae783 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/internal/caller.go @@ -0,0 +1,250 @@ +package internal + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "reflect" + "strings" + + "github.com/inferred-auth-implicit-api-key/fern/core" +) + +const ( + // contentType specifies the JSON Content-Type header value. + contentType = "application/json" + contentTypeHeader = "Content-Type" +) + +// Caller calls APIs and deserializes their response, if any. +type Caller struct { + client core.HTTPClient + retrier *Retrier +} + +// CallerParams represents the parameters used to constrcut a new *Caller. +type CallerParams struct { + Client core.HTTPClient + MaxAttempts uint +} + +// NewCaller returns a new *Caller backed by the given parameters. +func NewCaller(params *CallerParams) *Caller { + var httpClient core.HTTPClient = http.DefaultClient + if params.Client != nil { + httpClient = params.Client + } + var retryOptions []RetryOption + if params.MaxAttempts > 0 { + retryOptions = append(retryOptions, WithMaxAttempts(params.MaxAttempts)) + } + return &Caller{ + client: httpClient, + retrier: NewRetrier(retryOptions...), + } +} + +// CallParams represents the parameters used to issue an API call. +type CallParams struct { + URL string + Method string + MaxAttempts uint + Headers http.Header + BodyProperties map[string]interface{} + QueryParameters url.Values + Client core.HTTPClient + Request interface{} + Response interface{} + ResponseIsOptional bool + ErrorDecoder ErrorDecoder +} + +// CallResponse is a parsed HTTP response from an API call. +type CallResponse struct { + StatusCode int + Header http.Header +} + +// Call issues an API call according to the given call parameters. +func (c *Caller) Call(ctx context.Context, params *CallParams) (*CallResponse, error) { + url := buildURL(params.URL, params.QueryParameters) + req, err := newRequest( + ctx, + url, + params.Method, + params.Headers, + params.Request, + params.BodyProperties, + ) + if err != nil { + return nil, err + } + + // If the call has been cancelled, don't issue the request. + if err := ctx.Err(); err != nil { + return nil, err + } + + client := c.client + if params.Client != nil { + // Use the HTTP client scoped to the request. + client = params.Client + } + + var retryOptions []RetryOption + if params.MaxAttempts > 0 { + retryOptions = append(retryOptions, WithMaxAttempts(params.MaxAttempts)) + } + + resp, err := c.retrier.Run( + client.Do, + req, + params.ErrorDecoder, + retryOptions..., + ) + if err != nil { + return nil, err + } + + // Close the response body after we're done. + defer resp.Body.Close() + + // Check if the call was cancelled before we return the error + // associated with the call and/or unmarshal the response data. + if err := ctx.Err(); err != nil { + return nil, err + } + + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + return nil, decodeError(resp, params.ErrorDecoder) + } + + // Mutate the response parameter in-place. + if params.Response != nil { + if writer, ok := params.Response.(io.Writer); ok { + _, err = io.Copy(writer, resp.Body) + } else { + err = json.NewDecoder(resp.Body).Decode(params.Response) + } + if err != nil { + if err == io.EOF { + if params.ResponseIsOptional { + // The response is optional, so we should ignore the + // io.EOF error + return &CallResponse{ + StatusCode: resp.StatusCode, + Header: resp.Header, + }, nil + } + return nil, fmt.Errorf("expected a %T response, but the server responded with nothing", params.Response) + } + return nil, err + } + } + + return &CallResponse{ + StatusCode: resp.StatusCode, + Header: resp.Header, + }, nil +} + +// buildURL constructs the final URL by appending the given query parameters (if any). +func buildURL( + url string, + queryParameters url.Values, +) string { + if len(queryParameters) == 0 { + return url + } + if strings.ContainsRune(url, '?') { + url += "&" + } else { + url += "?" + } + url += queryParameters.Encode() + return url +} + +// newRequest returns a new *http.Request with all of the fields +// required to issue the call. +func newRequest( + ctx context.Context, + url string, + method string, + endpointHeaders http.Header, + request interface{}, + bodyProperties map[string]interface{}, +) (*http.Request, error) { + requestBody, err := newRequestBody(request, bodyProperties) + if err != nil { + return nil, err + } + req, err := http.NewRequestWithContext(ctx, method, url, requestBody) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + req.Header.Set(contentTypeHeader, contentType) + for name, values := range endpointHeaders { + req.Header[name] = values + } + return req, nil +} + +// newRequestBody returns a new io.Reader that represents the HTTP request body. +func newRequestBody(request interface{}, bodyProperties map[string]interface{}) (io.Reader, error) { + if isNil(request) { + if len(bodyProperties) == 0 { + return nil, nil + } + requestBytes, err := json.Marshal(bodyProperties) + if err != nil { + return nil, err + } + return bytes.NewReader(requestBytes), nil + } + if body, ok := request.(io.Reader); ok { + return body, nil + } + requestBytes, err := MarshalJSONWithExtraProperties(request, bodyProperties) + if err != nil { + return nil, err + } + return bytes.NewReader(requestBytes), nil +} + +// decodeError decodes the error from the given HTTP response. Note that +// it's the caller's responsibility to close the response body. +func decodeError(response *http.Response, errorDecoder ErrorDecoder) error { + if errorDecoder != nil { + // This endpoint has custom errors, so we'll + // attempt to unmarshal the error into a structured + // type based on the status code. + return errorDecoder(response.StatusCode, response.Header, response.Body) + } + // This endpoint doesn't have any custom error + // types, so we just read the body as-is, and + // put it into a normal error. + bytes, err := io.ReadAll(response.Body) + if err != nil && err != io.EOF { + return err + } + if err == io.EOF { + // The error didn't have a response body, + // so all we can do is return an error + // with the status code. + return core.NewAPIError(response.StatusCode, response.Header, nil) + } + return core.NewAPIError(response.StatusCode, response.Header, errors.New(string(bytes))) +} + +// isNil is used to determine if the request value is equal to nil (i.e. an interface +// value that holds a nil concrete value is itself non-nil). +func isNil(value interface{}) bool { + return value == nil || reflect.ValueOf(value).IsNil() +} diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/internal/caller_test.go b/seed/go-sdk/inferred-auth-implicit-api-key/internal/caller_test.go new file mode 100644 index 000000000000..becac4479f49 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/internal/caller_test.go @@ -0,0 +1,395 @@ +package internal + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "net/http/httptest" + "net/url" + "strconv" + "testing" + + "github.com/inferred-auth-implicit-api-key/fern/core" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// InternalTestCase represents a single test case. +type InternalTestCase struct { + description string + + // Server-side assertions. + givePathSuffix string + giveMethod string + giveResponseIsOptional bool + giveHeader http.Header + giveErrorDecoder ErrorDecoder + giveRequest *InternalTestRequest + giveQueryParams url.Values + giveBodyProperties map[string]interface{} + + // Client-side assertions. + wantResponse *InternalTestResponse + wantHeaders http.Header + wantError error +} + +// InternalTestRequest a simple request body. +type InternalTestRequest struct { + Id string `json:"id"` +} + +// InternalTestResponse a simple response body. +type InternalTestResponse struct { + Id string `json:"id"` + ExtraBodyProperties map[string]interface{} `json:"extraBodyProperties,omitempty"` + QueryParameters url.Values `json:"queryParameters,omitempty"` +} + +// InternalTestNotFoundError represents a 404. +type InternalTestNotFoundError struct { + *core.APIError + + Message string `json:"message"` +} + +func TestCall(t *testing.T) { + tests := []*InternalTestCase{ + { + description: "GET success", + giveMethod: http.MethodGet, + giveHeader: http.Header{ + "X-API-Status": []string{"success"}, + }, + giveRequest: &InternalTestRequest{ + Id: "123", + }, + wantResponse: &InternalTestResponse{ + Id: "123", + }, + }, + { + description: "GET success with query", + givePathSuffix: "?limit=1", + giveMethod: http.MethodGet, + giveHeader: http.Header{ + "X-API-Status": []string{"success"}, + }, + giveRequest: &InternalTestRequest{ + Id: "123", + }, + wantResponse: &InternalTestResponse{ + Id: "123", + QueryParameters: url.Values{ + "limit": []string{"1"}, + }, + }, + }, + { + description: "GET not found", + giveMethod: http.MethodGet, + giveHeader: http.Header{ + "X-API-Status": []string{"fail"}, + }, + giveRequest: &InternalTestRequest{ + Id: strconv.Itoa(http.StatusNotFound), + }, + giveErrorDecoder: newTestErrorDecoder(t), + wantError: &InternalTestNotFoundError{ + APIError: core.NewAPIError( + http.StatusNotFound, + http.Header{}, + errors.New(`{"message":"ID \"404\" not found"}`), + ), + }, + }, + { + description: "POST empty body", + giveMethod: http.MethodPost, + giveHeader: http.Header{ + "X-API-Status": []string{"fail"}, + }, + giveRequest: nil, + wantError: core.NewAPIError( + http.StatusBadRequest, + http.Header{}, + errors.New("invalid request"), + ), + }, + { + description: "POST optional response", + giveMethod: http.MethodPost, + giveHeader: http.Header{ + "X-API-Status": []string{"success"}, + }, + giveRequest: &InternalTestRequest{ + Id: "123", + }, + giveResponseIsOptional: true, + }, + { + description: "POST API error", + giveMethod: http.MethodPost, + giveHeader: http.Header{ + "X-API-Status": []string{"fail"}, + }, + giveRequest: &InternalTestRequest{ + Id: strconv.Itoa(http.StatusInternalServerError), + }, + wantError: core.NewAPIError( + http.StatusInternalServerError, + http.Header{}, + errors.New("failed to process request"), + ), + }, + { + description: "POST extra properties", + giveMethod: http.MethodPost, + giveHeader: http.Header{ + "X-API-Status": []string{"success"}, + }, + giveRequest: new(InternalTestRequest), + giveBodyProperties: map[string]interface{}{ + "key": "value", + }, + wantResponse: &InternalTestResponse{ + ExtraBodyProperties: map[string]interface{}{ + "key": "value", + }, + }, + }, + { + description: "GET extra query parameters", + giveMethod: http.MethodGet, + giveHeader: http.Header{ + "X-API-Status": []string{"success"}, + }, + giveQueryParams: url.Values{ + "extra": []string{"true"}, + }, + giveRequest: &InternalTestRequest{ + Id: "123", + }, + wantResponse: &InternalTestResponse{ + Id: "123", + QueryParameters: url.Values{ + "extra": []string{"true"}, + }, + }, + }, + { + description: "GET merge extra query parameters", + givePathSuffix: "?limit=1", + giveMethod: http.MethodGet, + giveHeader: http.Header{ + "X-API-Status": []string{"success"}, + }, + giveRequest: &InternalTestRequest{ + Id: "123", + }, + giveQueryParams: url.Values{ + "extra": []string{"true"}, + }, + wantResponse: &InternalTestResponse{ + Id: "123", + QueryParameters: url.Values{ + "limit": []string{"1"}, + "extra": []string{"true"}, + }, + }, + }, + } + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + var ( + server = newTestServer(t, test) + client = server.Client() + ) + caller := NewCaller( + &CallerParams{ + Client: client, + }, + ) + var response *InternalTestResponse + _, err := caller.Call( + context.Background(), + &CallParams{ + URL: server.URL + test.givePathSuffix, + Method: test.giveMethod, + Headers: test.giveHeader, + BodyProperties: test.giveBodyProperties, + QueryParameters: test.giveQueryParams, + Request: test.giveRequest, + Response: &response, + ResponseIsOptional: test.giveResponseIsOptional, + ErrorDecoder: test.giveErrorDecoder, + }, + ) + if test.wantError != nil { + assert.EqualError(t, err, test.wantError.Error()) + return + } + require.NoError(t, err) + assert.Equal(t, test.wantResponse, response) + }) + } +} + +func TestMergeHeaders(t *testing.T) { + t.Run("both empty", func(t *testing.T) { + merged := MergeHeaders(make(http.Header), make(http.Header)) + assert.Empty(t, merged) + }) + + t.Run("empty left", func(t *testing.T) { + left := make(http.Header) + + right := make(http.Header) + right.Set("X-API-Version", "0.0.1") + + merged := MergeHeaders(left, right) + assert.Equal(t, "0.0.1", merged.Get("X-API-Version")) + }) + + t.Run("empty right", func(t *testing.T) { + left := make(http.Header) + left.Set("X-API-Version", "0.0.1") + + right := make(http.Header) + + merged := MergeHeaders(left, right) + assert.Equal(t, "0.0.1", merged.Get("X-API-Version")) + }) + + t.Run("single value override", func(t *testing.T) { + left := make(http.Header) + left.Set("X-API-Version", "0.0.0") + + right := make(http.Header) + right.Set("X-API-Version", "0.0.1") + + merged := MergeHeaders(left, right) + assert.Equal(t, []string{"0.0.1"}, merged.Values("X-API-Version")) + }) + + t.Run("multiple value override", func(t *testing.T) { + left := make(http.Header) + left.Set("X-API-Versions", "0.0.0") + + right := make(http.Header) + right.Add("X-API-Versions", "0.0.1") + right.Add("X-API-Versions", "0.0.2") + + merged := MergeHeaders(left, right) + assert.Equal(t, []string{"0.0.1", "0.0.2"}, merged.Values("X-API-Versions")) + }) + + t.Run("disjoint merge", func(t *testing.T) { + left := make(http.Header) + left.Set("X-API-Tenancy", "test") + + right := make(http.Header) + right.Set("X-API-Version", "0.0.1") + + merged := MergeHeaders(left, right) + assert.Equal(t, []string{"test"}, merged.Values("X-API-Tenancy")) + assert.Equal(t, []string{"0.0.1"}, merged.Values("X-API-Version")) + }) +} + +// newTestServer returns a new *httptest.Server configured with the +// given test parameters. +func newTestServer(t *testing.T, tc *InternalTestCase) *httptest.Server { + return httptest.NewServer( + http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, tc.giveMethod, r.Method) + assert.Equal(t, contentType, r.Header.Get(contentTypeHeader)) + for header, value := range tc.giveHeader { + assert.Equal(t, value, r.Header.Values(header)) + } + + request := new(InternalTestRequest) + + bytes, err := io.ReadAll(r.Body) + if tc.giveRequest == nil { + require.Empty(t, bytes) + w.WriteHeader(http.StatusBadRequest) + _, err = w.Write([]byte("invalid request")) + require.NoError(t, err) + return + } + require.NoError(t, err) + require.NoError(t, json.Unmarshal(bytes, request)) + + switch request.Id { + case strconv.Itoa(http.StatusNotFound): + notFoundError := &InternalTestNotFoundError{ + APIError: &core.APIError{ + StatusCode: http.StatusNotFound, + }, + Message: fmt.Sprintf("ID %q not found", request.Id), + } + bytes, err = json.Marshal(notFoundError) + require.NoError(t, err) + + w.WriteHeader(http.StatusNotFound) + _, err = w.Write(bytes) + require.NoError(t, err) + return + + case strconv.Itoa(http.StatusInternalServerError): + w.WriteHeader(http.StatusInternalServerError) + _, err = w.Write([]byte("failed to process request")) + require.NoError(t, err) + return + } + + if tc.giveResponseIsOptional { + w.WriteHeader(http.StatusOK) + return + } + + extraBodyProperties := make(map[string]interface{}) + require.NoError(t, json.Unmarshal(bytes, &extraBodyProperties)) + delete(extraBodyProperties, "id") + + response := &InternalTestResponse{ + Id: request.Id, + ExtraBodyProperties: extraBodyProperties, + QueryParameters: r.URL.Query(), + } + bytes, err = json.Marshal(response) + require.NoError(t, err) + + _, err = w.Write(bytes) + require.NoError(t, err) + }, + ), + ) +} + +// newTestErrorDecoder returns an error decoder suitable for tests. +func newTestErrorDecoder(t *testing.T) func(int, http.Header, io.Reader) error { + return func(statusCode int, header http.Header, body io.Reader) error { + raw, err := io.ReadAll(body) + require.NoError(t, err) + + var ( + apiError = core.NewAPIError(statusCode, header, errors.New(string(raw))) + decoder = json.NewDecoder(bytes.NewReader(raw)) + ) + if statusCode == http.StatusNotFound { + value := new(InternalTestNotFoundError) + value.APIError = apiError + require.NoError(t, decoder.Decode(value)) + + return value + } + return apiError + } +} diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/internal/error_decoder.go b/seed/go-sdk/inferred-auth-implicit-api-key/internal/error_decoder.go new file mode 100644 index 000000000000..d1fa9430bc6b --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/internal/error_decoder.go @@ -0,0 +1,64 @@ +package internal + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + + "github.com/inferred-auth-implicit-api-key/fern/core" +) + +// ErrorCodes maps HTTP status codes to error constructors. +type ErrorCodes map[int]func(*core.APIError) error + +// ErrorDecoder decodes *http.Response errors and returns a +// typed API error (e.g. *core.APIError). +type ErrorDecoder func(statusCode int, header http.Header, body io.Reader) error + +// NewErrorDecoder returns a new ErrorDecoder backed by the given error codes. +// errorCodesOverrides is optional and will be merged with the default error codes, +// with overrides taking precedence. +func NewErrorDecoder(errorCodes ErrorCodes, errorCodesOverrides ...ErrorCodes) ErrorDecoder { + // Merge default error codes with overrides + mergedErrorCodes := make(ErrorCodes) + + // Start with default error codes + for statusCode, errorFunc := range errorCodes { + mergedErrorCodes[statusCode] = errorFunc + } + + // Apply overrides if provided + if len(errorCodesOverrides) > 0 && errorCodesOverrides[0] != nil { + for statusCode, errorFunc := range errorCodesOverrides[0] { + mergedErrorCodes[statusCode] = errorFunc + } + } + + return func(statusCode int, header http.Header, body io.Reader) error { + raw, err := io.ReadAll(body) + if err != nil { + return fmt.Errorf("failed to read error from response body: %w", err) + } + apiError := core.NewAPIError( + statusCode, + header, + errors.New(string(raw)), + ) + newErrorFunc, ok := mergedErrorCodes[statusCode] + if !ok { + // This status code isn't recognized, so we return + // the API error as-is. + return apiError + } + customError := newErrorFunc(apiError) + if err := json.NewDecoder(bytes.NewReader(raw)).Decode(customError); err != nil { + // If we fail to decode the error, we return the + // API error as-is. + return apiError + } + return customError + } +} diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/internal/error_decoder_test.go b/seed/go-sdk/inferred-auth-implicit-api-key/internal/error_decoder_test.go new file mode 100644 index 000000000000..5dabf4de82bf --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/internal/error_decoder_test.go @@ -0,0 +1,59 @@ +package internal + +import ( + "bytes" + "errors" + "net/http" + "testing" + + "github.com/inferred-auth-implicit-api-key/fern/core" + "github.com/stretchr/testify/assert" +) + +func TestErrorDecoder(t *testing.T) { + decoder := NewErrorDecoder( + ErrorCodes{ + http.StatusNotFound: func(apiError *core.APIError) error { + return &InternalTestNotFoundError{APIError: apiError} + }, + }) + + tests := []struct { + description string + giveStatusCode int + giveHeader http.Header + giveBody string + wantError error + }{ + { + description: "unrecognized status code", + giveStatusCode: http.StatusInternalServerError, + giveHeader: http.Header{}, + giveBody: "Internal Server Error", + wantError: core.NewAPIError(http.StatusInternalServerError, http.Header{}, errors.New("Internal Server Error")), + }, + { + description: "not found with valid JSON", + giveStatusCode: http.StatusNotFound, + giveHeader: http.Header{}, + giveBody: `{"message": "Resource not found"}`, + wantError: &InternalTestNotFoundError{ + APIError: core.NewAPIError(http.StatusNotFound, http.Header{}, errors.New(`{"message": "Resource not found"}`)), + Message: "Resource not found", + }, + }, + { + description: "not found with invalid JSON", + giveStatusCode: http.StatusNotFound, + giveHeader: http.Header{}, + giveBody: `Resource not found`, + wantError: core.NewAPIError(http.StatusNotFound, http.Header{}, errors.New("Resource not found")), + }, + } + + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + assert.Equal(t, tt.wantError, decoder(tt.giveStatusCode, tt.giveHeader, bytes.NewReader([]byte(tt.giveBody)))) + }) + } +} diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/internal/explicit_fields.go b/seed/go-sdk/inferred-auth-implicit-api-key/internal/explicit_fields.go new file mode 100644 index 000000000000..4bdf34fc2b7c --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/internal/explicit_fields.go @@ -0,0 +1,116 @@ +package internal + +import ( + "math/big" + "reflect" + "strings" +) + +// HandleExplicitFields processes a struct to remove `omitempty` from +// fields that have been explicitly set (as indicated by their corresponding bit in explicitFields). +// Note that `marshaler` should be an embedded struct to avoid infinite recursion. +// Returns an interface{} that can be passed to json.Marshal. +func HandleExplicitFields(marshaler interface{}, explicitFields *big.Int) interface{} { + val := reflect.ValueOf(marshaler) + typ := reflect.TypeOf(marshaler) + + // Handle pointer types + if val.Kind() == reflect.Ptr { + if val.IsNil() { + return nil + } + val = val.Elem() + typ = typ.Elem() + } + + // Only handle struct types + if val.Kind() != reflect.Struct { + return marshaler + } + + // Handle embedded struct pattern + var sourceVal reflect.Value + var sourceType reflect.Type + + // Check if this is an embedded struct pattern + if typ.NumField() == 1 && typ.Field(0).Anonymous { + // This is likely an embedded struct, get the embedded value + embeddedField := val.Field(0) + sourceVal = embeddedField + sourceType = embeddedField.Type() + } else { + // Regular struct + sourceVal = val + sourceType = typ + } + + // If no explicit fields set, use standard marshaling + if explicitFields == nil || explicitFields.Sign() == 0 { + return marshaler + } + + // Create a new struct type with modified tags + fields := make([]reflect.StructField, 0, sourceType.NumField()) + + for i := 0; i < sourceType.NumField(); i++ { + field := sourceType.Field(i) + + // Skip unexported fields and the explicitFields field itself + if !field.IsExported() || field.Name == "explicitFields" { + continue + } + + // Check if this field has been explicitly set + fieldBit := big.NewInt(1) + fieldBit.Lsh(fieldBit, uint(i)) + if big.NewInt(0).And(explicitFields, fieldBit).Sign() != 0 { + // Remove omitempty from the json tag + tag := field.Tag.Get("json") + if tag != "" && tag != "-" { + // Parse the json tag, remove omitempty from options + parts := strings.Split(tag, ",") + if len(parts) > 1 { + var newParts []string + newParts = append(newParts, parts[0]) // Keep the field name + for _, part := range parts[1:] { + if strings.TrimSpace(part) != "omitempty" { + newParts = append(newParts, part) + } + } + tag = strings.Join(newParts, ",") + } + + // Reconstruct the struct tag + newTag := `json:"` + tag + `"` + if urlTag := field.Tag.Get("url"); urlTag != "" { + newTag += ` url:"` + urlTag + `"` + } + + field.Tag = reflect.StructTag(newTag) + } + } + + fields = append(fields, field) + } + + // Create new struct type with modified tags + newType := reflect.StructOf(fields) + newVal := reflect.New(newType).Elem() + + // Copy field values from original struct to new struct + fieldIndex := 0 + for i := 0; i < sourceType.NumField(); i++ { + originalField := sourceType.Field(i) + + // Skip unexported fields and the explicitFields field itself + if !originalField.IsExported() || originalField.Name == "explicitFields" { + continue + } + + originalValue := sourceVal.Field(i) + newVal.Field(fieldIndex).Set(originalValue) + fieldIndex++ + } + + return newVal.Interface() +} diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/internal/explicit_fields_test.go b/seed/go-sdk/inferred-auth-implicit-api-key/internal/explicit_fields_test.go new file mode 100644 index 000000000000..3d05e88a2ce9 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/internal/explicit_fields_test.go @@ -0,0 +1,497 @@ +package internal + +import ( + "encoding/json" + "math/big" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type testExplicitFieldsStruct struct { + Name *string `json:"name,omitempty"` + Code *string `json:"code,omitempty"` + Count *int `json:"count,omitempty"` + Enabled *bool `json:"enabled,omitempty"` + Tags []string `json:"tags,omitempty"` + //lint:ignore unused this field is intentionally unused for testing + unexported string `json:"-"` + explicitFields *big.Int `json:"-"` +} + +var ( + testFieldName = big.NewInt(1 << 0) + testFieldCode = big.NewInt(1 << 1) + testFieldCount = big.NewInt(1 << 2) + testFieldEnabled = big.NewInt(1 << 3) + testFieldTags = big.NewInt(1 << 4) +) + +func (t *testExplicitFieldsStruct) require(field *big.Int) { + if t.explicitFields == nil { + t.explicitFields = big.NewInt(0) + } + t.explicitFields.Or(t.explicitFields, field) +} + +func (t *testExplicitFieldsStruct) SetName(name *string) { + t.Name = name + t.require(testFieldName) +} + +func (t *testExplicitFieldsStruct) SetCode(code *string) { + t.Code = code + t.require(testFieldCode) +} + +func (t *testExplicitFieldsStruct) SetCount(count *int) { + t.Count = count + t.require(testFieldCount) +} + +func (t *testExplicitFieldsStruct) SetEnabled(enabled *bool) { + t.Enabled = enabled + t.require(testFieldEnabled) +} + +func (t *testExplicitFieldsStruct) SetTags(tags []string) { + t.Tags = tags + t.require(testFieldTags) +} + +func (t *testExplicitFieldsStruct) MarshalJSON() ([]byte, error) { + type embed testExplicitFieldsStruct + var marshaler = struct { + embed + }{ + embed: embed(*t), + } + return json.Marshal(HandleExplicitFields(marshaler, t.explicitFields)) +} + +type testStructWithoutExplicitFields struct { + Name *string `json:"name,omitempty"` + Code *string `json:"code,omitempty"` +} + +func TestHandleExplicitFields(t *testing.T) { + tests := []struct { + desc string + giveInput interface{} + wantBytes []byte + wantError string + }{ + { + desc: "nil input", + giveInput: nil, + wantBytes: []byte(`null`), + }, + { + desc: "non-struct input", + giveInput: "string", + wantBytes: []byte(`"string"`), + }, + { + desc: "slice input", + giveInput: []string{"a", "b"}, + wantBytes: []byte(`["a","b"]`), + }, + { + desc: "map input", + giveInput: map[string]interface{}{"key": "value"}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "struct without explicitFields field", + giveInput: &testStructWithoutExplicitFields{ + Name: stringPtr("test"), + Code: nil, + }, + wantBytes: []byte(`{"name":"test"}`), + }, + { + desc: "struct with no explicit fields set", + giveInput: &testExplicitFieldsStruct{ + Name: stringPtr("test"), + Code: nil, + }, + wantBytes: []byte(`{"name":"test"}`), + }, + { + desc: "struct with explicit nil field", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{ + Name: stringPtr("test"), + } + s.SetCode(nil) + return s + }(), + wantBytes: []byte(`{"name":"test","code":null}`), + }, + { + desc: "struct with explicit non-nil field", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{} + s.SetName(stringPtr("explicit")) + s.SetCode(stringPtr("also-explicit")) + return s + }(), + wantBytes: []byte(`{"name":"explicit","code":"also-explicit"}`), + }, + { + desc: "struct with mixed explicit and implicit fields", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{ + Name: stringPtr("implicit"), + Count: intPtr(42), + } + s.SetCode(nil) // explicit nil + return s + }(), + wantBytes: []byte(`{"name":"implicit","code":null,"count":42}`), + }, + { + desc: "struct with multiple explicit nil fields", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{ + Name: stringPtr("test"), + } + s.SetCode(nil) + s.SetCount(nil) + return s + }(), + wantBytes: []byte(`{"name":"test","code":null,"count":null}`), + }, + { + desc: "struct with slice field", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{ + Tags: []string{"tag1", "tag2"}, + } + s.SetTags(nil) // explicit nil slice + return s + }(), + wantBytes: []byte(`{"tags":null}`), + }, + { + desc: "struct with boolean field", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{} + s.SetEnabled(boolPtr(false)) // explicit false + return s + }(), + wantBytes: []byte(`{"enabled":false}`), + }, + { + desc: "struct with all fields explicit", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{} + s.SetName(stringPtr("test")) + s.SetCode(nil) + s.SetCount(intPtr(0)) + s.SetEnabled(boolPtr(false)) + s.SetTags([]string{}) + return s + }(), + wantBytes: []byte(`{"name":"test","code":null,"count":0,"enabled":false,"tags":[]}`), + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + var explicitFields *big.Int + if s, ok := tt.giveInput.(*testExplicitFieldsStruct); ok { + explicitFields = s.explicitFields + } + bytes, err := json.Marshal(HandleExplicitFields(tt.giveInput, explicitFields)) + if tt.wantError != "" { + require.EqualError(t, err, tt.wantError) + assert.Nil(t, tt.wantBytes) + return + } + require.NoError(t, err) + assert.JSONEq(t, string(tt.wantBytes), string(bytes)) + + // Verify it's valid JSON + var value interface{} + require.NoError(t, json.Unmarshal(bytes, &value)) + }) + } +} + +func TestHandleExplicitFieldsCustomMarshaler(t *testing.T) { + t.Run("custom marshaler with explicit fields", func(t *testing.T) { + s := &testExplicitFieldsStruct{} + s.SetName(nil) + s.SetCode(stringPtr("test-code")) + + bytes, err := s.MarshalJSON() + require.NoError(t, err) + assert.JSONEq(t, `{"name":null,"code":"test-code"}`, string(bytes)) + }) + + t.Run("custom marshaler with no explicit fields", func(t *testing.T) { + s := &testExplicitFieldsStruct{ + Name: stringPtr("implicit"), + Code: stringPtr("also-implicit"), + } + + bytes, err := s.MarshalJSON() + require.NoError(t, err) + assert.JSONEq(t, `{"name":"implicit","code":"also-implicit"}`, string(bytes)) + }) +} + +func TestHandleExplicitFieldsPointerHandling(t *testing.T) { + t.Run("nil pointer", func(t *testing.T) { + var s *testExplicitFieldsStruct + bytes, err := json.Marshal(HandleExplicitFields(s, nil)) + require.NoError(t, err) + assert.Equal(t, []byte(`null`), bytes) + }) + + t.Run("pointer to struct", func(t *testing.T) { + s := &testExplicitFieldsStruct{} + s.SetName(nil) + + bytes, err := json.Marshal(HandleExplicitFields(s, s.explicitFields)) + require.NoError(t, err) + assert.JSONEq(t, `{"name":null}`, string(bytes)) + }) +} + +func TestHandleExplicitFieldsEmbeddedStruct(t *testing.T) { + t.Run("embedded struct with explicit fields", func(t *testing.T) { + // Create a struct similar to what MarshalJSON creates + s := &testExplicitFieldsStruct{} + s.SetName(nil) + s.SetCode(stringPtr("test-code")) + + type embed testExplicitFieldsStruct + var marshaler = struct { + embed + }{ + embed: embed(*s), + } + + bytes, err := json.Marshal(HandleExplicitFields(marshaler, s.explicitFields)) + require.NoError(t, err) + // Should include both explicit fields (name as null, code as "test-code") + assert.JSONEq(t, `{"name":null,"code":"test-code"}`, string(bytes)) + }) + + t.Run("embedded struct with no explicit fields", func(t *testing.T) { + s := &testExplicitFieldsStruct{ + Name: stringPtr("implicit"), + Code: stringPtr("also-implicit"), + } + + type embed testExplicitFieldsStruct + var marshaler = struct { + embed + }{ + embed: embed(*s), + } + + bytes, err := json.Marshal(HandleExplicitFields(marshaler, s.explicitFields)) + require.NoError(t, err) + // Should only include non-nil fields (omitempty behavior) + assert.JSONEq(t, `{"name":"implicit","code":"also-implicit"}`, string(bytes)) + }) + + t.Run("embedded struct with mixed fields", func(t *testing.T) { + s := &testExplicitFieldsStruct{ + Count: intPtr(42), // implicit field + } + s.SetName(nil) // explicit nil + s.SetCode(stringPtr("explicit")) // explicit value + + type embed testExplicitFieldsStruct + var marshaler = struct { + embed + }{ + embed: embed(*s), + } + + bytes, err := json.Marshal(HandleExplicitFields(marshaler, s.explicitFields)) + require.NoError(t, err) + // Should include explicit null, explicit value, and implicit value + assert.JSONEq(t, `{"name":null,"code":"explicit","count":42}`, string(bytes)) + }) +} + +func TestHandleExplicitFieldsTagHandling(t *testing.T) { + type testStructWithComplexTags struct { + Field1 *string `json:"field1,omitempty" url:"field1,omitempty"` + Field2 *string `json:"field2,omitempty,string" url:"field2"` + Field3 *string `json:"-"` + Field4 *string `json:"field4"` + explicitFields *big.Int `json:"-"` + } + + s := &testStructWithComplexTags{ + Field1: stringPtr("test1"), + Field4: stringPtr("test4"), + explicitFields: big.NewInt(1), // Only first field is explicit + } + + bytes, err := json.Marshal(HandleExplicitFields(s, s.explicitFields)) + require.NoError(t, err) + + // Field1 should have omitempty removed, Field2 should keep omitempty, Field4 should be included + assert.JSONEq(t, `{"field1":"test1","field4":"test4"}`, string(bytes)) +} + +// Test types for nested struct explicit fields testing +type testNestedStruct struct { + NestedName *string `json:"nested_name,omitempty"` + NestedCode *string `json:"nested_code,omitempty"` + explicitFields *big.Int `json:"-"` +} + +type testParentStruct struct { + ParentName *string `json:"parent_name,omitempty"` + Nested *testNestedStruct `json:"nested,omitempty"` + explicitFields *big.Int `json:"-"` +} + +var ( + nestedFieldName = big.NewInt(1 << 0) + nestedFieldCode = big.NewInt(1 << 1) +) + +var ( + parentFieldName = big.NewInt(1 << 0) + parentFieldNested = big.NewInt(1 << 1) +) + +func (n *testNestedStruct) require(field *big.Int) { + if n.explicitFields == nil { + n.explicitFields = big.NewInt(0) + } + n.explicitFields.Or(n.explicitFields, field) +} + +func (n *testNestedStruct) SetNestedName(name *string) { + n.NestedName = name + n.require(nestedFieldName) +} + +func (n *testNestedStruct) SetNestedCode(code *string) { + n.NestedCode = code + n.require(nestedFieldCode) +} + +func (n *testNestedStruct) MarshalJSON() ([]byte, error) { + type embed testNestedStruct + var marshaler = struct { + embed + }{ + embed: embed(*n), + } + return json.Marshal(HandleExplicitFields(marshaler, n.explicitFields)) +} + +func (p *testParentStruct) require(field *big.Int) { + if p.explicitFields == nil { + p.explicitFields = big.NewInt(0) + } + p.explicitFields.Or(p.explicitFields, field) +} + +func (p *testParentStruct) SetParentName(name *string) { + p.ParentName = name + p.require(parentFieldName) +} + +func (p *testParentStruct) SetNested(nested *testNestedStruct) { + p.Nested = nested + p.require(parentFieldNested) +} + +func (p *testParentStruct) MarshalJSON() ([]byte, error) { + type embed testParentStruct + var marshaler = struct { + embed + }{ + embed: embed(*p), + } + return json.Marshal(HandleExplicitFields(marshaler, p.explicitFields)) +} + +func TestHandleExplicitFieldsNestedStruct(t *testing.T) { + tests := []struct { + desc string + setupFunc func() *testParentStruct + wantBytes []byte + }{ + { + desc: "nested struct with explicit nil in nested object", + setupFunc: func() *testParentStruct { + nested := &testNestedStruct{ + NestedName: stringPtr("implicit-nested"), + } + nested.SetNestedCode(nil) // explicit nil + + return &testParentStruct{ + ParentName: stringPtr("implicit-parent"), + Nested: nested, + } + }, + wantBytes: []byte(`{"parent_name":"implicit-parent","nested":{"nested_name":"implicit-nested","nested_code":null}}`), + }, + { + desc: "parent with explicit nil nested struct", + setupFunc: func() *testParentStruct { + parent := &testParentStruct{ + ParentName: stringPtr("implicit-parent"), + } + parent.SetNested(nil) // explicit nil nested struct + return parent + }, + wantBytes: []byte(`{"parent_name":"implicit-parent","nested":null}`), + }, + { + desc: "all explicit fields in nested structure", + setupFunc: func() *testParentStruct { + nested := &testNestedStruct{} + nested.SetNestedName(stringPtr("explicit-nested")) + nested.SetNestedCode(nil) // explicit nil + + parent := &testParentStruct{} + parent.SetParentName(nil) // explicit nil + parent.SetNested(nested) // explicit nested struct + + return parent + }, + wantBytes: []byte(`{"parent_name":null,"nested":{"nested_name":"explicit-nested","nested_code":null}}`), + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + parent := tt.setupFunc() + bytes, err := parent.MarshalJSON() + require.NoError(t, err) + assert.JSONEq(t, string(tt.wantBytes), string(bytes)) + + // Verify it's valid JSON + var value interface{} + require.NoError(t, json.Unmarshal(bytes, &value)) + }) + } +} + +// Helper functions +func stringPtr(s string) *string { + return &s +} + +func intPtr(i int) *int { + return &i +} + +func boolPtr(b bool) *bool { + return &b +} diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/internal/extra_properties.go b/seed/go-sdk/inferred-auth-implicit-api-key/internal/extra_properties.go new file mode 100644 index 000000000000..540c3fd89eeb --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/internal/extra_properties.go @@ -0,0 +1,141 @@ +package internal + +import ( + "bytes" + "encoding/json" + "fmt" + "reflect" + "strings" +) + +// MarshalJSONWithExtraProperty marshals the given value to JSON, including the extra property. +func MarshalJSONWithExtraProperty(marshaler interface{}, key string, value interface{}) ([]byte, error) { + return MarshalJSONWithExtraProperties(marshaler, map[string]interface{}{key: value}) +} + +// MarshalJSONWithExtraProperties marshals the given value to JSON, including any extra properties. +func MarshalJSONWithExtraProperties(marshaler interface{}, extraProperties map[string]interface{}) ([]byte, error) { + bytes, err := json.Marshal(marshaler) + if err != nil { + return nil, err + } + if len(extraProperties) == 0 { + return bytes, nil + } + keys, err := getKeys(marshaler) + if err != nil { + return nil, err + } + for _, key := range keys { + if _, ok := extraProperties[key]; ok { + return nil, fmt.Errorf("cannot add extra property %q because it is already defined on the type", key) + } + } + extraBytes, err := json.Marshal(extraProperties) + if err != nil { + return nil, err + } + if isEmptyJSON(bytes) { + if isEmptyJSON(extraBytes) { + return bytes, nil + } + return extraBytes, nil + } + result := bytes[:len(bytes)-1] + result = append(result, ',') + result = append(result, extraBytes[1:len(extraBytes)-1]...) + result = append(result, '}') + return result, nil +} + +// ExtractExtraProperties extracts any extra properties from the given value. +func ExtractExtraProperties(bytes []byte, value interface{}, exclude ...string) (map[string]interface{}, error) { + val := reflect.ValueOf(value) + for val.Kind() == reflect.Ptr { + if val.IsNil() { + return nil, fmt.Errorf("value must be non-nil to extract extra properties") + } + val = val.Elem() + } + if err := json.Unmarshal(bytes, &value); err != nil { + return nil, err + } + var extraProperties map[string]interface{} + if err := json.Unmarshal(bytes, &extraProperties); err != nil { + return nil, err + } + for i := 0; i < val.Type().NumField(); i++ { + key := jsonKey(val.Type().Field(i)) + if key == "" || key == "-" { + continue + } + delete(extraProperties, key) + } + for _, key := range exclude { + delete(extraProperties, key) + } + if len(extraProperties) == 0 { + return nil, nil + } + return extraProperties, nil +} + +// getKeys returns the keys associated with the given value. The value must be a +// a struct or a map with string keys. +func getKeys(value interface{}) ([]string, error) { + val := reflect.ValueOf(value) + if val.Kind() == reflect.Ptr { + val = val.Elem() + } + if !val.IsValid() { + return nil, nil + } + switch val.Kind() { + case reflect.Struct: + return getKeysForStructType(val.Type()), nil + case reflect.Map: + var keys []string + if val.Type().Key().Kind() != reflect.String { + return nil, fmt.Errorf("cannot extract keys from %T; only structs and maps with string keys are supported", value) + } + for _, key := range val.MapKeys() { + keys = append(keys, key.String()) + } + return keys, nil + default: + return nil, fmt.Errorf("cannot extract keys from %T; only structs and maps with string keys are supported", value) + } +} + +// getKeysForStructType returns all the keys associated with the given struct type, +// visiting embedded fields recursively. +func getKeysForStructType(structType reflect.Type) []string { + if structType.Kind() == reflect.Pointer { + structType = structType.Elem() + } + if structType.Kind() != reflect.Struct { + return nil + } + var keys []string + for i := 0; i < structType.NumField(); i++ { + field := structType.Field(i) + if field.Anonymous { + keys = append(keys, getKeysForStructType(field.Type)...) + continue + } + keys = append(keys, jsonKey(field)) + } + return keys +} + +// jsonKey returns the JSON key from the struct tag of the given field, +// excluding the omitempty flag (if any). +func jsonKey(field reflect.StructField) string { + return strings.TrimSuffix(field.Tag.Get("json"), ",omitempty") +} + +// isEmptyJSON returns true if the given data is empty, the empty JSON object, or +// an explicit null. +func isEmptyJSON(data []byte) bool { + return len(data) <= 2 || bytes.Equal(data, []byte("null")) +} diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/internal/extra_properties_test.go b/seed/go-sdk/inferred-auth-implicit-api-key/internal/extra_properties_test.go new file mode 100644 index 000000000000..aa2510ee5121 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/internal/extra_properties_test.go @@ -0,0 +1,228 @@ +package internal + +import ( + "encoding/json" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type testMarshaler struct { + Name string `json:"name"` + BirthDate time.Time `json:"birthDate"` + CreatedAt time.Time `json:"created_at"` +} + +func (t *testMarshaler) MarshalJSON() ([]byte, error) { + type embed testMarshaler + var marshaler = struct { + embed + BirthDate string `json:"birthDate"` + CreatedAt string `json:"created_at"` + }{ + embed: embed(*t), + BirthDate: t.BirthDate.Format("2006-01-02"), + CreatedAt: t.CreatedAt.Format(time.RFC3339), + } + return MarshalJSONWithExtraProperty(marshaler, "type", "test") +} + +func TestMarshalJSONWithExtraProperties(t *testing.T) { + tests := []struct { + desc string + giveMarshaler interface{} + giveExtraProperties map[string]interface{} + wantBytes []byte + wantError string + }{ + { + desc: "invalid type", + giveMarshaler: []string{"invalid"}, + giveExtraProperties: map[string]interface{}{"key": "overwrite"}, + wantError: `cannot extract keys from []string; only structs and maps with string keys are supported`, + }, + { + desc: "invalid key type", + giveMarshaler: map[int]interface{}{42: "value"}, + giveExtraProperties: map[string]interface{}{"key": "overwrite"}, + wantError: `cannot extract keys from map[int]interface {}; only structs and maps with string keys are supported`, + }, + { + desc: "invalid map overwrite", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{"key": "overwrite"}, + wantError: `cannot add extra property "key" because it is already defined on the type`, + }, + { + desc: "invalid struct overwrite", + giveMarshaler: new(testMarshaler), + giveExtraProperties: map[string]interface{}{"birthDate": "2000-01-01"}, + wantError: `cannot add extra property "birthDate" because it is already defined on the type`, + }, + { + desc: "invalid struct overwrite embedded type", + giveMarshaler: new(testMarshaler), + giveExtraProperties: map[string]interface{}{"name": "bob"}, + wantError: `cannot add extra property "name" because it is already defined on the type`, + }, + { + desc: "nil", + giveMarshaler: nil, + giveExtraProperties: nil, + wantBytes: []byte(`null`), + }, + { + desc: "empty", + giveMarshaler: map[string]interface{}{}, + giveExtraProperties: map[string]interface{}{}, + wantBytes: []byte(`{}`), + }, + { + desc: "no extra properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "only extra properties", + giveMarshaler: map[string]interface{}{}, + giveExtraProperties: map[string]interface{}{"key": "value"}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "single extra property", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{"extra": "property"}, + wantBytes: []byte(`{"key":"value","extra":"property"}`), + }, + { + desc: "multiple extra properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{"one": 1, "two": 2}, + wantBytes: []byte(`{"key":"value","one":1,"two":2}`), + }, + { + desc: "nested properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{ + "user": map[string]interface{}{ + "age": 42, + "name": "alice", + }, + }, + wantBytes: []byte(`{"key":"value","user":{"age":42,"name":"alice"}}`), + }, + { + desc: "multiple nested properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{ + "metadata": map[string]interface{}{ + "ip": "127.0.0.1", + }, + "user": map[string]interface{}{ + "age": 42, + "name": "alice", + }, + }, + wantBytes: []byte(`{"key":"value","metadata":{"ip":"127.0.0.1"},"user":{"age":42,"name":"alice"}}`), + }, + { + desc: "custom marshaler", + giveMarshaler: &testMarshaler{ + Name: "alice", + BirthDate: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), + CreatedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC), + }, + giveExtraProperties: map[string]interface{}{ + "extra": "property", + }, + wantBytes: []byte(`{"name":"alice","birthDate":"2000-01-01","created_at":"2024-01-01T00:00:00Z","type":"test","extra":"property"}`), + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + bytes, err := MarshalJSONWithExtraProperties(tt.giveMarshaler, tt.giveExtraProperties) + if tt.wantError != "" { + require.EqualError(t, err, tt.wantError) + assert.Nil(t, tt.wantBytes) + return + } + require.NoError(t, err) + assert.Equal(t, tt.wantBytes, bytes) + + value := make(map[string]interface{}) + require.NoError(t, json.Unmarshal(bytes, &value)) + }) + } +} + +func TestExtractExtraProperties(t *testing.T) { + t.Run("none", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice"}`), value) + require.NoError(t, err) + assert.Nil(t, extraProperties) + }) + + t.Run("non-nil pointer", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]interface{}{"age": float64(42)}, extraProperties) + }) + + t.Run("nil pointer", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + var value *user + _, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + assert.EqualError(t, err, "value must be non-nil to extract extra properties") + }) + + t.Run("non-zero value", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]interface{}{"age": float64(42)}, extraProperties) + }) + + t.Run("zero value", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + var value user + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]interface{}{"age": float64(42)}, extraProperties) + }) + + t.Run("exclude", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value, "age") + require.NoError(t, err) + assert.Nil(t, extraProperties) + }) +} diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/internal/http.go b/seed/go-sdk/inferred-auth-implicit-api-key/internal/http.go new file mode 100644 index 000000000000..77863752bb58 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/internal/http.go @@ -0,0 +1,71 @@ +package internal + +import ( + "fmt" + "net/http" + "net/url" + "reflect" +) + +// HTTPClient is an interface for a subset of the *http.Client. +type HTTPClient interface { + Do(*http.Request) (*http.Response, error) +} + +// ResolveBaseURL resolves the base URL from the given arguments, +// preferring the first non-empty value. +func ResolveBaseURL(values ...string) string { + for _, value := range values { + if value != "" { + return value + } + } + return "" +} + +// EncodeURL encodes the given arguments into the URL, escaping +// values as needed. Pointer arguments are dereferenced before processing. +func EncodeURL(urlFormat string, args ...interface{}) string { + escapedArgs := make([]interface{}, 0, len(args)) + for _, arg := range args { + // Dereference the argument if it's a pointer + value := dereferenceArg(arg) + escapedArgs = append(escapedArgs, url.PathEscape(fmt.Sprintf("%v", value))) + } + return fmt.Sprintf(urlFormat, escapedArgs...) +} + +// dereferenceArg dereferences a pointer argument if necessary, returning the underlying value. +// If the argument is not a pointer or is nil, it returns the argument as-is. +func dereferenceArg(arg interface{}) interface{} { + if arg == nil { + return arg + } + + v := reflect.ValueOf(arg) + + // Keep dereferencing until we get to a non-pointer value or hit nil + for v.Kind() == reflect.Ptr { + if v.IsNil() { + return nil + } + v = v.Elem() + } + + return v.Interface() +} + +// MergeHeaders merges the given headers together, where the right +// takes precedence over the left. +func MergeHeaders(left, right http.Header) http.Header { + for key, values := range right { + if len(values) > 1 { + left[key] = values + continue + } + if value := right.Get(key); value != "" { + left.Set(key, value) + } + } + return left +} diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/internal/query.go b/seed/go-sdk/inferred-auth-implicit-api-key/internal/query.go new file mode 100644 index 000000000000..1cbaf7fe1c02 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/internal/query.go @@ -0,0 +1,353 @@ +package internal + +import ( + "encoding/base64" + "fmt" + "net/url" + "reflect" + "strings" + "time" + + "github.com/google/uuid" +) + +var ( + bytesType = reflect.TypeOf([]byte{}) + queryEncoderType = reflect.TypeOf(new(QueryEncoder)).Elem() + timeType = reflect.TypeOf(time.Time{}) + uuidType = reflect.TypeOf(uuid.UUID{}) +) + +// QueryEncoder is an interface implemented by any type that wishes to encode +// itself into URL values in a non-standard way. +type QueryEncoder interface { + EncodeQueryValues(key string, v *url.Values) error +} + +// prepareValue handles common validation and unwrapping logic for both functions +func prepareValue(v interface{}) (reflect.Value, url.Values, error) { + values := make(url.Values) + val := reflect.ValueOf(v) + for val.Kind() == reflect.Ptr { + if val.IsNil() { + return reflect.Value{}, values, nil + } + val = val.Elem() + } + + if v == nil { + return reflect.Value{}, values, nil + } + + if val.Kind() != reflect.Struct { + return reflect.Value{}, nil, fmt.Errorf("query: Values() expects struct input. Got %v", val.Kind()) + } + + err := reflectValue(values, val, "") + if err != nil { + return reflect.Value{}, nil, err + } + + return val, values, nil +} + +// QueryValues encodes url.Values from request objects. +// +// Note: This type is inspired by Google's query encoding library, but +// supports far less customization and is tailored to fit this SDK's use case. +// +// Ref: https://github.com/google/go-querystring +func QueryValues(v interface{}) (url.Values, error) { + _, values, err := prepareValue(v) + return values, err +} + +// QueryValuesWithDefaults encodes url.Values from request objects +// and default values, merging the defaults into the request. +// It's expected that the values of defaults are wire names. +func QueryValuesWithDefaults(v interface{}, defaults map[string]interface{}) (url.Values, error) { + val, values, err := prepareValue(v) + if err != nil { + return values, err + } + if !val.IsValid() { + return values, nil + } + + // apply defaults to zero-value fields directly on the original struct + valType := val.Type() + for i := 0; i < val.NumField(); i++ { + field := val.Field(i) + fieldType := valType.Field(i) + fieldName := fieldType.Name + + if fieldType.PkgPath != "" && !fieldType.Anonymous { + // Skip unexported fields. + continue + } + + // check if field is zero value and we have a default for it + if field.CanSet() && field.IsZero() { + tag := fieldType.Tag.Get("url") + if tag == "" || tag == "-" { + continue + } + wireName, _ := parseTag(tag) + if wireName == "" { + wireName = fieldName + } + if defaultVal, exists := defaults[wireName]; exists { + values.Set(wireName, valueString(reflect.ValueOf(defaultVal), tagOptions{}, reflect.StructField{})) + } + } + } + + return values, err +} + +// reflectValue populates the values parameter from the struct fields in val. +// Embedded structs are followed recursively (using the rules defined in the +// Values function documentation) breadth-first. +func reflectValue(values url.Values, val reflect.Value, scope string) error { + typ := val.Type() + for i := 0; i < typ.NumField(); i++ { + sf := typ.Field(i) + if sf.PkgPath != "" && !sf.Anonymous { + // Skip unexported fields. + continue + } + + sv := val.Field(i) + tag := sf.Tag.Get("url") + if tag == "" || tag == "-" { + continue + } + + name, opts := parseTag(tag) + if name == "" { + name = sf.Name + } + + if scope != "" { + name = scope + "[" + name + "]" + } + + if opts.Contains("omitempty") && isEmptyValue(sv) { + continue + } + + if sv.Type().Implements(queryEncoderType) { + // If sv is a nil pointer and the custom encoder is defined on a non-pointer + // method receiver, set sv to the zero value of the underlying type + if !reflect.Indirect(sv).IsValid() && sv.Type().Elem().Implements(queryEncoderType) { + sv = reflect.New(sv.Type().Elem()) + } + + m := sv.Interface().(QueryEncoder) + if err := m.EncodeQueryValues(name, &values); err != nil { + return err + } + continue + } + + // Recursively dereference pointers, but stop at nil pointers. + for sv.Kind() == reflect.Ptr { + if sv.IsNil() { + break + } + sv = sv.Elem() + } + + if sv.Type() == uuidType || sv.Type() == bytesType || sv.Type() == timeType { + values.Add(name, valueString(sv, opts, sf)) + continue + } + + if sv.Kind() == reflect.Slice || sv.Kind() == reflect.Array { + if sv.Len() == 0 { + // Skip if slice or array is empty. + continue + } + for i := 0; i < sv.Len(); i++ { + value := sv.Index(i) + if isStructPointer(value) && !value.IsNil() { + if err := reflectValue(values, value.Elem(), name); err != nil { + return err + } + } else { + values.Add(name, valueString(value, opts, sf)) + } + } + continue + } + + if sv.Kind() == reflect.Map { + if err := reflectMap(values, sv, name); err != nil { + return err + } + continue + } + + if sv.Kind() == reflect.Struct { + if err := reflectValue(values, sv, name); err != nil { + return err + } + continue + } + + values.Add(name, valueString(sv, opts, sf)) + } + + return nil +} + +// reflectMap handles map types specifically, generating query parameters in the format key[mapkey]=value +func reflectMap(values url.Values, val reflect.Value, scope string) error { + if val.IsNil() { + return nil + } + + iter := val.MapRange() + for iter.Next() { + k := iter.Key() + v := iter.Value() + + key := fmt.Sprint(k.Interface()) + paramName := scope + "[" + key + "]" + + for v.Kind() == reflect.Ptr { + if v.IsNil() { + break + } + v = v.Elem() + } + + for v.Kind() == reflect.Interface { + v = v.Elem() + } + + if v.Kind() == reflect.Map { + if err := reflectMap(values, v, paramName); err != nil { + return err + } + continue + } + + if v.Kind() == reflect.Struct { + if err := reflectValue(values, v, paramName); err != nil { + return err + } + continue + } + + if v.Kind() == reflect.Slice || v.Kind() == reflect.Array { + if v.Len() == 0 { + continue + } + for i := 0; i < v.Len(); i++ { + value := v.Index(i) + if isStructPointer(value) && !value.IsNil() { + if err := reflectValue(values, value.Elem(), paramName); err != nil { + return err + } + } else { + values.Add(paramName, valueString(value, tagOptions{}, reflect.StructField{})) + } + } + continue + } + + values.Add(paramName, valueString(v, tagOptions{}, reflect.StructField{})) + } + + return nil +} + +// valueString returns the string representation of a value. +func valueString(v reflect.Value, opts tagOptions, sf reflect.StructField) string { + for v.Kind() == reflect.Ptr { + if v.IsNil() { + return "" + } + v = v.Elem() + } + + if v.Type() == timeType { + t := v.Interface().(time.Time) + if format := sf.Tag.Get("format"); format == "date" { + return t.Format("2006-01-02") + } + return t.Format(time.RFC3339) + } + + if v.Type() == uuidType { + u := v.Interface().(uuid.UUID) + return u.String() + } + + if v.Type() == bytesType { + b := v.Interface().([]byte) + return base64.StdEncoding.EncodeToString(b) + } + + return fmt.Sprint(v.Interface()) +} + +// isEmptyValue checks if a value should be considered empty for the purposes +// of omitting fields with the "omitempty" option. +func isEmptyValue(v reflect.Value) bool { + type zeroable interface { + IsZero() bool + } + + if !v.IsZero() { + if z, ok := v.Interface().(zeroable); ok { + return z.IsZero() + } + } + + switch v.Kind() { + case reflect.Array, reflect.Map, reflect.Slice, reflect.String: + return v.Len() == 0 + case reflect.Bool: + return !v.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Interface, reflect.Ptr: + return v.IsNil() + case reflect.Invalid, reflect.Complex64, reflect.Complex128, reflect.Chan, reflect.Func, reflect.Struct, reflect.UnsafePointer: + return false + } + + return false +} + +// isStructPointer returns true if the given reflect.Value is a pointer to a struct. +func isStructPointer(v reflect.Value) bool { + return v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct +} + +// tagOptions is the string following a comma in a struct field's "url" tag, or +// the empty string. It does not include the leading comma. +type tagOptions []string + +// parseTag splits a struct field's url tag into its name and comma-separated +// options. +func parseTag(tag string) (string, tagOptions) { + s := strings.Split(tag, ",") + return s[0], s[1:] +} + +// Contains checks whether the tagOptions contains the specified option. +func (o tagOptions) Contains(option string) bool { + for _, s := range o { + if s == option { + return true + } + } + return false +} diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/internal/query_test.go b/seed/go-sdk/inferred-auth-implicit-api-key/internal/query_test.go new file mode 100644 index 000000000000..2c28cb8acf68 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/internal/query_test.go @@ -0,0 +1,395 @@ +package internal + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestQueryValues(t *testing.T) { + t.Run("empty optional", func(t *testing.T) { + type nested struct { + Value *string `json:"value,omitempty" url:"value,omitempty"` + } + type example struct { + Nested *nested `json:"nested,omitempty" url:"nested,omitempty"` + } + + values, err := QueryValues(&example{}) + require.NoError(t, err) + assert.Empty(t, values) + }) + + t.Run("empty required", func(t *testing.T) { + type nested struct { + Value *string `json:"value,omitempty" url:"value,omitempty"` + } + type example struct { + Required string `json:"required" url:"required"` + Nested *nested `json:"nested,omitempty" url:"nested,omitempty"` + } + + values, err := QueryValues(&example{}) + require.NoError(t, err) + assert.Equal(t, "required=", values.Encode()) + }) + + t.Run("allow multiple", func(t *testing.T) { + type example struct { + Values []string `json:"values" url:"values"` + } + + values, err := QueryValues( + &example{ + Values: []string{"foo", "bar", "baz"}, + }, + ) + require.NoError(t, err) + assert.Equal(t, "values=foo&values=bar&values=baz", values.Encode()) + }) + + t.Run("nested object", func(t *testing.T) { + type nested struct { + Value *string `json:"value,omitempty" url:"value,omitempty"` + } + type example struct { + Required string `json:"required" url:"required"` + Nested *nested `json:"nested,omitempty" url:"nested,omitempty"` + } + + nestedValue := "nestedValue" + values, err := QueryValues( + &example{ + Required: "requiredValue", + Nested: &nested{ + Value: &nestedValue, + }, + }, + ) + require.NoError(t, err) + assert.Equal(t, "nested%5Bvalue%5D=nestedValue&required=requiredValue", values.Encode()) + }) + + t.Run("url unspecified", func(t *testing.T) { + type example struct { + Required string `json:"required" url:"required"` + NotFound string `json:"notFound"` + } + + values, err := QueryValues( + &example{ + Required: "requiredValue", + NotFound: "notFound", + }, + ) + require.NoError(t, err) + assert.Equal(t, "required=requiredValue", values.Encode()) + }) + + t.Run("url ignored", func(t *testing.T) { + type example struct { + Required string `json:"required" url:"required"` + NotFound string `json:"notFound" url:"-"` + } + + values, err := QueryValues( + &example{ + Required: "requiredValue", + NotFound: "notFound", + }, + ) + require.NoError(t, err) + assert.Equal(t, "required=requiredValue", values.Encode()) + }) + + t.Run("datetime", func(t *testing.T) { + type example struct { + DateTime time.Time `json:"dateTime" url:"dateTime"` + } + + values, err := QueryValues( + &example{ + DateTime: time.Date(1994, 3, 16, 12, 34, 56, 0, time.UTC), + }, + ) + require.NoError(t, err) + assert.Equal(t, "dateTime=1994-03-16T12%3A34%3A56Z", values.Encode()) + }) + + t.Run("date", func(t *testing.T) { + type example struct { + Date time.Time `json:"date" url:"date" format:"date"` + } + + values, err := QueryValues( + &example{ + Date: time.Date(1994, 3, 16, 12, 34, 56, 0, time.UTC), + }, + ) + require.NoError(t, err) + assert.Equal(t, "date=1994-03-16", values.Encode()) + }) + + t.Run("optional time", func(t *testing.T) { + type example struct { + Date *time.Time `json:"date,omitempty" url:"date,omitempty" format:"date"` + } + + values, err := QueryValues( + &example{}, + ) + require.NoError(t, err) + assert.Empty(t, values.Encode()) + }) + + t.Run("omitempty with non-pointer zero value", func(t *testing.T) { + type enum string + + type example struct { + Enum enum `json:"enum,omitempty" url:"enum,omitempty"` + } + + values, err := QueryValues( + &example{}, + ) + require.NoError(t, err) + assert.Empty(t, values.Encode()) + }) + + t.Run("object array", func(t *testing.T) { + type object struct { + Key string `json:"key" url:"key"` + Value string `json:"value" url:"value"` + } + type example struct { + Objects []*object `json:"objects,omitempty" url:"objects,omitempty"` + } + + values, err := QueryValues( + &example{ + Objects: []*object{ + { + Key: "hello", + Value: "world", + }, + { + Key: "foo", + Value: "bar", + }, + }, + }, + ) + require.NoError(t, err) + assert.Equal(t, "objects%5Bkey%5D=hello&objects%5Bkey%5D=foo&objects%5Bvalue%5D=world&objects%5Bvalue%5D=bar", values.Encode()) + }) + + t.Run("map", func(t *testing.T) { + type request struct { + Metadata map[string]interface{} `json:"metadata" url:"metadata"` + } + values, err := QueryValues( + &request{ + Metadata: map[string]interface{}{ + "foo": "bar", + "baz": "qux", + }, + }, + ) + require.NoError(t, err) + assert.Equal(t, "metadata%5Bbaz%5D=qux&metadata%5Bfoo%5D=bar", values.Encode()) + }) + + t.Run("nested map", func(t *testing.T) { + type request struct { + Metadata map[string]interface{} `json:"metadata" url:"metadata"` + } + values, err := QueryValues( + &request{ + Metadata: map[string]interface{}{ + "inner": map[string]interface{}{ + "foo": "bar", + }, + }, + }, + ) + require.NoError(t, err) + assert.Equal(t, "metadata%5Binner%5D%5Bfoo%5D=bar", values.Encode()) + }) + + t.Run("nested map array", func(t *testing.T) { + type request struct { + Metadata map[string]interface{} `json:"metadata" url:"metadata"` + } + values, err := QueryValues( + &request{ + Metadata: map[string]interface{}{ + "inner": []string{ + "one", + "two", + "three", + }, + }, + }, + ) + require.NoError(t, err) + assert.Equal(t, "metadata%5Binner%5D=one&metadata%5Binner%5D=two&metadata%5Binner%5D=three", values.Encode()) + }) +} + +func TestQueryValuesWithDefaults(t *testing.T) { + t.Run("apply defaults to zero values", func(t *testing.T) { + type example struct { + Name string `json:"name" url:"name"` + Age int `json:"age" url:"age"` + Enabled bool `json:"enabled" url:"enabled"` + } + + defaults := map[string]interface{}{ + "name": "default-name", + "age": 25, + "enabled": true, + } + + values, err := QueryValuesWithDefaults(&example{}, defaults) + require.NoError(t, err) + assert.Equal(t, "age=25&enabled=true&name=default-name", values.Encode()) + }) + + t.Run("preserve non-zero values over defaults", func(t *testing.T) { + type example struct { + Name string `json:"name" url:"name"` + Age int `json:"age" url:"age"` + Enabled bool `json:"enabled" url:"enabled"` + } + + defaults := map[string]interface{}{ + "name": "default-name", + "age": 25, + "enabled": true, + } + + values, err := QueryValuesWithDefaults(&example{ + Name: "actual-name", + Age: 30, + // Enabled remains false (zero value), should get default + }, defaults) + require.NoError(t, err) + assert.Equal(t, "age=30&enabled=true&name=actual-name", values.Encode()) + }) + + t.Run("ignore defaults for fields not in struct", func(t *testing.T) { + type example struct { + Name string `json:"name" url:"name"` + Age int `json:"age" url:"age"` + } + + defaults := map[string]interface{}{ + "name": "default-name", + "age": 25, + "nonexistent": "should-be-ignored", + } + + values, err := QueryValuesWithDefaults(&example{}, defaults) + require.NoError(t, err) + assert.Equal(t, "age=25&name=default-name", values.Encode()) + }) + + t.Run("type conversion for compatible defaults", func(t *testing.T) { + type example struct { + Count int64 `json:"count" url:"count"` + Rate float64 `json:"rate" url:"rate"` + Message string `json:"message" url:"message"` + } + + defaults := map[string]interface{}{ + "count": int(100), // int -> int64 conversion + "rate": float32(2.5), // float32 -> float64 conversion + "message": "hello", // string -> string (no conversion needed) + } + + values, err := QueryValuesWithDefaults(&example{}, defaults) + require.NoError(t, err) + assert.Equal(t, "count=100&message=hello&rate=2.5", values.Encode()) + }) + + t.Run("mixed with pointer fields and omitempty", func(t *testing.T) { + type example struct { + Required string `json:"required" url:"required"` + Optional *string `json:"optional,omitempty" url:"optional,omitempty"` + Count int `json:"count,omitempty" url:"count,omitempty"` + } + + defaultOptional := "default-optional" + defaults := map[string]interface{}{ + "required": "default-required", + "optional": &defaultOptional, // pointer type + "count": 42, + } + + values, err := QueryValuesWithDefaults(&example{ + Required: "custom-required", // should override default + // Optional is nil, should get default + // Count is 0, should get default + }, defaults) + require.NoError(t, err) + assert.Equal(t, "count=42&optional=default-optional&required=custom-required", values.Encode()) + }) + + t.Run("override non-zero defaults with explicit zero values", func(t *testing.T) { + type example struct { + Name *string `json:"name" url:"name"` + Age *int `json:"age" url:"age"` + Enabled *bool `json:"enabled" url:"enabled"` + } + + defaults := map[string]interface{}{ + "name": "default-name", + "age": 25, + "enabled": true, + } + + // first, test that a properly empty request is overridden: + { + values, err := QueryValuesWithDefaults(&example{}, defaults) + require.NoError(t, err) + assert.Equal(t, "age=25&enabled=true&name=default-name", values.Encode()) + } + + // second, test that a request that contains zeros is not overridden: + var ( + name = "" + age = 0 + enabled = false + ) + values, err := QueryValuesWithDefaults(&example{ + Name: &name, // explicit empty string should override default + Age: &age, // explicit zero should override default + Enabled: &enabled, // explicit false should override default + }, defaults) + require.NoError(t, err) + assert.Equal(t, "age=0&enabled=false&name=", values.Encode()) + }) + + t.Run("nil input returns empty values", func(t *testing.T) { + defaults := map[string]any{ + "name": "default-name", + "age": 25, + } + + // Test with nil + values, err := QueryValuesWithDefaults(nil, defaults) + require.NoError(t, err) + assert.Empty(t, values) + + // Test with nil pointer + type example struct { + Name string `json:"name" url:"name"` + } + var nilPtr *example + values, err = QueryValuesWithDefaults(nilPtr, defaults) + require.NoError(t, err) + assert.Empty(t, values) + }) +} diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/internal/retrier.go b/seed/go-sdk/inferred-auth-implicit-api-key/internal/retrier.go new file mode 100644 index 000000000000..4efae1b4c286 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/internal/retrier.go @@ -0,0 +1,230 @@ +package internal + +import ( + "crypto/rand" + "math/big" + "net/http" + "strconv" + "time" +) + +const ( + defaultRetryAttempts = 2 + minRetryDelay = 1000 * time.Millisecond + maxRetryDelay = 60000 * time.Millisecond +) + +// RetryOption adapts the behavior the *Retrier. +type RetryOption func(*retryOptions) + +// RetryFunc is a retryable HTTP function call (i.e. *http.Client.Do). +type RetryFunc func(*http.Request) (*http.Response, error) + +// WithMaxAttempts configures the maximum number of attempts +// of the *Retrier. +func WithMaxAttempts(attempts uint) RetryOption { + return func(opts *retryOptions) { + opts.attempts = attempts + } +} + +// Retrier retries failed requests a configurable number of times with an +// exponential back-off between each retry. +type Retrier struct { + attempts uint +} + +// NewRetrier constructs a new *Retrier with the given options, if any. +func NewRetrier(opts ...RetryOption) *Retrier { + options := new(retryOptions) + for _, opt := range opts { + opt(options) + } + attempts := uint(defaultRetryAttempts) + if options.attempts > 0 { + attempts = options.attempts + } + return &Retrier{ + attempts: attempts, + } +} + +// Run issues the request and, upon failure, retries the request if possible. +// +// The request will be retried as long as the request is deemed retryable and the +// number of retry attempts has not grown larger than the configured retry limit. +func (r *Retrier) Run( + fn RetryFunc, + request *http.Request, + errorDecoder ErrorDecoder, + opts ...RetryOption, +) (*http.Response, error) { + options := new(retryOptions) + for _, opt := range opts { + opt(options) + } + maxRetryAttempts := r.attempts + if options.attempts > 0 { + maxRetryAttempts = options.attempts + } + var ( + retryAttempt uint + previousError error + ) + return r.run( + fn, + request, + errorDecoder, + maxRetryAttempts, + retryAttempt, + previousError, + ) +} + +func (r *Retrier) run( + fn RetryFunc, + request *http.Request, + errorDecoder ErrorDecoder, + maxRetryAttempts uint, + retryAttempt uint, + previousError error, +) (*http.Response, error) { + if retryAttempt >= maxRetryAttempts { + return nil, previousError + } + + // If the call has been cancelled, don't issue the request. + if err := request.Context().Err(); err != nil { + return nil, err + } + + response, err := fn(request) + if err != nil { + return nil, err + } + + if r.shouldRetry(response) { + defer response.Body.Close() + + delay, err := r.retryDelay(response, retryAttempt) + if err != nil { + return nil, err + } + + time.Sleep(delay) + + return r.run( + fn, + request, + errorDecoder, + maxRetryAttempts, + retryAttempt+1, + decodeError(response, errorDecoder), + ) + } + + return response, nil +} + +// shouldRetry returns true if the request should be retried based on the given +// response status code. +func (r *Retrier) shouldRetry(response *http.Response) bool { + return response.StatusCode == http.StatusTooManyRequests || + response.StatusCode == http.StatusRequestTimeout || + response.StatusCode >= http.StatusInternalServerError +} + +// retryDelay calculates the delay time based on response headers, +// falling back to exponential backoff if no headers are present. +func (r *Retrier) retryDelay(response *http.Response, retryAttempt uint) (time.Duration, error) { + // Check for Retry-After header first (RFC 7231), applying no jitter + if retryAfter := response.Header.Get("Retry-After"); retryAfter != "" { + // Parse as number of seconds... + if seconds, err := strconv.Atoi(retryAfter); err == nil { + delay := time.Duration(seconds) * time.Second + if delay > 0 { + if delay > maxRetryDelay { + delay = maxRetryDelay + } + return delay, nil + } + } + + // ...or as an HTTP date; both are valid + if retryTime, err := time.Parse(time.RFC1123, retryAfter); err == nil { + delay := time.Until(retryTime) + if delay > 0 { + if delay > maxRetryDelay { + delay = maxRetryDelay + } + return delay, nil + } + } + } + + // Then check for industry-standard X-RateLimit-Reset header, applying positive jitter + if rateLimitReset := response.Header.Get("X-RateLimit-Reset"); rateLimitReset != "" { + if resetTimestamp, err := strconv.ParseInt(rateLimitReset, 10, 64); err == nil { + // Assume Unix timestamp in seconds + resetTime := time.Unix(resetTimestamp, 0) + delay := time.Until(resetTime) + if delay > 0 { + if delay > maxRetryDelay { + delay = maxRetryDelay + } + return r.addPositiveJitter(delay) + } + } + } + + // Fall back to exponential backoff + return r.exponentialBackoff(retryAttempt) +} + +// exponentialBackoff calculates the delay time based on the retry attempt +// and applies symmetric jitter (±10% around the delay). +func (r *Retrier) exponentialBackoff(retryAttempt uint) (time.Duration, error) { + if retryAttempt > 63 { // 2^63+ would overflow uint64 + retryAttempt = 63 + } + + delay := minRetryDelay << retryAttempt + if delay > maxRetryDelay { + delay = maxRetryDelay + } + + return r.addSymmetricJitter(delay) +} + +// addJitterWithRange applies jitter to the given delay. +// minPercent and maxPercent define the jitter range (e.g., 100, 120 for +0% to +20%). +func (r *Retrier) addJitterWithRange(delay time.Duration, minPercent, maxPercent int) (time.Duration, error) { + jitterRange := big.NewInt(int64(delay * time.Duration(maxPercent-minPercent) / 100)) + jitter, err := rand.Int(rand.Reader, jitterRange) + if err != nil { + return 0, err + } + + jitteredDelay := delay + time.Duration(jitter.Int64()) + delay*time.Duration(minPercent-100)/100 + if jitteredDelay < minRetryDelay { + jitteredDelay = minRetryDelay + } + if jitteredDelay > maxRetryDelay { + jitteredDelay = maxRetryDelay + } + return jitteredDelay, nil +} + +// addPositiveJitter applies positive jitter to the given delay (100%-120% range). +func (r *Retrier) addPositiveJitter(delay time.Duration) (time.Duration, error) { + return r.addJitterWithRange(delay, 100, 120) +} + +// addSymmetricJitter applies symmetric jitter to the given delay (90%-110% range). +func (r *Retrier) addSymmetricJitter(delay time.Duration) (time.Duration, error) { + return r.addJitterWithRange(delay, 90, 110) +} + +type retryOptions struct { + attempts uint +} diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/internal/retrier_test.go b/seed/go-sdk/inferred-auth-implicit-api-key/internal/retrier_test.go new file mode 100644 index 000000000000..636ed3c4265d --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/internal/retrier_test.go @@ -0,0 +1,300 @@ +package internal + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/inferred-auth-implicit-api-key/fern/core" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type RetryTestCase struct { + description string + + giveAttempts uint + giveStatusCodes []int + giveResponse *InternalTestResponse + + wantResponse *InternalTestResponse + wantError *core.APIError +} + +func TestRetrier(t *testing.T) { + tests := []*RetryTestCase{ + { + description: "retry request succeeds after multiple failures", + giveAttempts: 3, + giveStatusCodes: []int{ + http.StatusServiceUnavailable, + http.StatusServiceUnavailable, + http.StatusOK, + }, + giveResponse: &InternalTestResponse{ + Id: "1", + }, + wantResponse: &InternalTestResponse{ + Id: "1", + }, + }, + { + description: "retry request fails if MaxAttempts is exceeded", + giveAttempts: 3, + giveStatusCodes: []int{ + http.StatusRequestTimeout, + http.StatusRequestTimeout, + http.StatusRequestTimeout, + http.StatusOK, + }, + wantError: &core.APIError{ + StatusCode: http.StatusRequestTimeout, + }, + }, + { + description: "retry durations increase exponentially and stay within the min and max delay values", + giveAttempts: 4, + giveStatusCodes: []int{ + http.StatusServiceUnavailable, + http.StatusServiceUnavailable, + http.StatusServiceUnavailable, + http.StatusOK, + }, + }, + { + description: "retry does not occur on status code 404", + giveAttempts: 2, + giveStatusCodes: []int{http.StatusNotFound, http.StatusOK}, + wantError: &core.APIError{ + StatusCode: http.StatusNotFound, + }, + }, + { + description: "retries occur on status code 429", + giveAttempts: 2, + giveStatusCodes: []int{http.StatusTooManyRequests, http.StatusOK}, + }, + { + description: "retries occur on status code 408", + giveAttempts: 2, + giveStatusCodes: []int{http.StatusRequestTimeout, http.StatusOK}, + }, + { + description: "retries occur on status code 500", + giveAttempts: 2, + giveStatusCodes: []int{http.StatusInternalServerError, http.StatusOK}, + }, + } + + for _, tc := range tests { + t.Run(tc.description, func(t *testing.T) { + var ( + test = tc + server = newTestRetryServer(t, test) + client = server.Client() + ) + + t.Parallel() + + caller := NewCaller( + &CallerParams{ + Client: client, + }, + ) + + var response *InternalTestResponse + _, err := caller.Call( + context.Background(), + &CallParams{ + URL: server.URL, + Method: http.MethodGet, + Request: &InternalTestRequest{}, + Response: &response, + MaxAttempts: test.giveAttempts, + ResponseIsOptional: true, + }, + ) + + if test.wantError != nil { + require.IsType(t, err, &core.APIError{}) + expectedErrorCode := test.wantError.StatusCode + actualErrorCode := err.(*core.APIError).StatusCode + assert.Equal(t, expectedErrorCode, actualErrorCode) + return + } + + require.NoError(t, err) + assert.Equal(t, test.wantResponse, response) + }) + } +} + +// newTestRetryServer returns a new *httptest.Server configured with the +// given test parameters, suitable for testing retries. +func newTestRetryServer(t *testing.T, tc *RetryTestCase) *httptest.Server { + var index int + timestamps := make([]time.Time, 0, len(tc.giveStatusCodes)) + + return httptest.NewServer( + http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + timestamps = append(timestamps, time.Now()) + if index > 0 && index < len(expectedRetryDurations) { + // Ensure that the duration between retries increases exponentially, + // and that it is within the minimum and maximum retry delay values. + actualDuration := timestamps[index].Sub(timestamps[index-1]) + expectedDurationMin := expectedRetryDurations[index-1] * 50 / 100 + expectedDurationMax := expectedRetryDurations[index-1] * 150 / 100 + assert.True( + t, + actualDuration >= expectedDurationMin && actualDuration <= expectedDurationMax, + "expected duration to be in range [%v, %v], got %v", + expectedDurationMin, + expectedDurationMax, + actualDuration, + ) + assert.LessOrEqual( + t, + actualDuration, + maxRetryDelay, + "expected duration to be less than the maxRetryDelay (%v), got %v", + maxRetryDelay, + actualDuration, + ) + assert.GreaterOrEqual( + t, + actualDuration, + minRetryDelay, + "expected duration to be greater than the minRetryDelay (%v), got %v", + minRetryDelay, + actualDuration, + ) + } + + request := new(InternalTestRequest) + bytes, err := io.ReadAll(r.Body) + require.NoError(t, err) + require.NoError(t, json.Unmarshal(bytes, request)) + require.LessOrEqual(t, index, len(tc.giveStatusCodes)) + + statusCode := tc.giveStatusCodes[index] + + w.WriteHeader(statusCode) + + if tc.giveResponse != nil && statusCode == http.StatusOK { + bytes, err = json.Marshal(tc.giveResponse) + require.NoError(t, err) + _, err = w.Write(bytes) + require.NoError(t, err) + } + + index++ + }, + ), + ) +} + +// expectedRetryDurations holds an array of calculated retry durations, +// where the index of the array should correspond to the retry attempt. +// +// Values are calculated based off of `minRetryDelay * 2^i`. +var expectedRetryDurations = []time.Duration{ + 1000 * time.Millisecond, // 500ms * 2^1 = 1000ms + 2000 * time.Millisecond, // 500ms * 2^2 = 2000ms + 4000 * time.Millisecond, // 500ms * 2^3 = 4000ms + 8000 * time.Millisecond, // 500ms * 2^4 = 8000ms +} + +func TestRetryDelayTiming(t *testing.T) { + tests := []struct { + name string + headerName string + headerValueFunc func() string + expectedMinMs int64 + expectedMaxMs int64 + }{ + { + name: "retry-after with seconds value", + headerName: "retry-after", + headerValueFunc: func() string { + return "1" + }, + expectedMinMs: 500, + expectedMaxMs: 1500, + }, + { + name: "retry-after with HTTP date", + headerName: "retry-after", + headerValueFunc: func() string { + return time.Now().Add(3 * time.Second).Format(time.RFC1123) + }, + expectedMinMs: 1500, + expectedMaxMs: 4500, + }, + { + name: "x-ratelimit-reset with future timestamp", + headerName: "x-ratelimit-reset", + headerValueFunc: func() string { + return fmt.Sprintf("%d", time.Now().Add(3*time.Second).Unix()) + }, + expectedMinMs: 1500, + expectedMaxMs: 4500, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + var timestamps []time.Time + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + timestamps = append(timestamps, time.Now()) + if len(timestamps) == 1 { + // First request - return retryable error with header + w.Header().Set(tt.headerName, tt.headerValueFunc()) + w.WriteHeader(http.StatusTooManyRequests) + } else { + // Second request - return success + w.WriteHeader(http.StatusOK) + response := &InternalTestResponse{Id: "success"} + bytes, _ := json.Marshal(response) + w.Write(bytes) + } + })) + defer server.Close() + + caller := NewCaller(&CallerParams{ + Client: server.Client(), + }) + + var response *InternalTestResponse + _, err := caller.Call( + context.Background(), + &CallParams{ + URL: server.URL, + Method: http.MethodGet, + Request: &InternalTestRequest{}, + Response: &response, + MaxAttempts: 2, + ResponseIsOptional: true, + }, + ) + + require.NoError(t, err) + require.Len(t, timestamps, 2, "Expected exactly 2 requests") + + actualDelayMs := timestamps[1].Sub(timestamps[0]).Milliseconds() + + assert.GreaterOrEqual(t, actualDelayMs, tt.expectedMinMs, + "Actual delay %dms should be >= expected min %dms", actualDelayMs, tt.expectedMinMs) + assert.LessOrEqual(t, actualDelayMs, tt.expectedMaxMs, + "Actual delay %dms should be <= expected max %dms", actualDelayMs, tt.expectedMaxMs) + }) + } +} diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/internal/stringer.go b/seed/go-sdk/inferred-auth-implicit-api-key/internal/stringer.go new file mode 100644 index 000000000000..312801851e0e --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/internal/stringer.go @@ -0,0 +1,13 @@ +package internal + +import "encoding/json" + +// StringifyJSON returns a pretty JSON string representation of +// the given value. +func StringifyJSON(value interface{}) (string, error) { + bytes, err := json.MarshalIndent(value, "", " ") + if err != nil { + return "", err + } + return string(bytes), nil +} diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/internal/time.go b/seed/go-sdk/inferred-auth-implicit-api-key/internal/time.go new file mode 100644 index 000000000000..ab0e269fade3 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/internal/time.go @@ -0,0 +1,137 @@ +package internal + +import ( + "encoding/json" + "time" +) + +const dateFormat = "2006-01-02" + +// DateTime wraps time.Time and adapts its JSON representation +// to conform to a RFC3339 date (e.g. 2006-01-02). +// +// Ref: https://ijmacd.github.io/rfc3339-iso8601 +type Date struct { + t *time.Time +} + +// NewDate returns a new *Date. If the given time.Time +// is nil, nil will be returned. +func NewDate(t time.Time) *Date { + return &Date{t: &t} +} + +// NewOptionalDate returns a new *Date. If the given time.Time +// is nil, nil will be returned. +func NewOptionalDate(t *time.Time) *Date { + if t == nil { + return nil + } + return &Date{t: t} +} + +// Time returns the Date's underlying time, if any. If the +// date is nil, the zero value is returned. +func (d *Date) Time() time.Time { + if d == nil || d.t == nil { + return time.Time{} + } + return *d.t +} + +// TimePtr returns a pointer to the Date's underlying time.Time, if any. +func (d *Date) TimePtr() *time.Time { + if d == nil || d.t == nil { + return nil + } + if d.t.IsZero() { + return nil + } + return d.t +} + +func (d *Date) MarshalJSON() ([]byte, error) { + if d == nil || d.t == nil { + return nil, nil + } + return json.Marshal(d.t.Format(dateFormat)) +} + +func (d *Date) UnmarshalJSON(data []byte) error { + var raw string + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + + parsedTime, err := time.Parse(dateFormat, raw) + if err != nil { + return err + } + + *d = Date{t: &parsedTime} + return nil +} + +// DateTime wraps time.Time and adapts its JSON representation +// to conform to a RFC3339 date-time (e.g. 2017-07-21T17:32:28Z). +// +// Ref: https://ijmacd.github.io/rfc3339-iso8601 +type DateTime struct { + t *time.Time +} + +// NewDateTime returns a new *DateTime. +func NewDateTime(t time.Time) *DateTime { + return &DateTime{t: &t} +} + +// NewOptionalDateTime returns a new *DateTime. If the given time.Time +// is nil, nil will be returned. +func NewOptionalDateTime(t *time.Time) *DateTime { + if t == nil { + return nil + } + return &DateTime{t: t} +} + +// Time returns the DateTime's underlying time, if any. If the +// date-time is nil, the zero value is returned. +func (d *DateTime) Time() time.Time { + if d == nil || d.t == nil { + return time.Time{} + } + return *d.t +} + +// TimePtr returns a pointer to the DateTime's underlying time.Time, if any. +func (d *DateTime) TimePtr() *time.Time { + if d == nil || d.t == nil { + return nil + } + if d.t.IsZero() { + return nil + } + return d.t +} + +func (d *DateTime) MarshalJSON() ([]byte, error) { + if d == nil || d.t == nil { + return nil, nil + } + return json.Marshal(d.t.Format(time.RFC3339)) +} + +func (d *DateTime) UnmarshalJSON(data []byte) error { + var raw string + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + + parsedTime, err := time.Parse(time.RFC3339, raw) + if err != nil { + return err + } + + *d = DateTime{t: &parsedTime} + return nil +} diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/nested/api/client.go b/seed/go-sdk/inferred-auth-implicit-api-key/nested/api/client.go new file mode 100644 index 000000000000..43582086763b --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/nested/api/client.go @@ -0,0 +1,46 @@ +// Code generated by Fern. DO NOT EDIT. + +package api + +import ( + context "context" + core "github.com/inferred-auth-implicit-api-key/fern/core" + internal "github.com/inferred-auth-implicit-api-key/fern/internal" + option "github.com/inferred-auth-implicit-api-key/fern/option" +) + +type Client struct { + WithRawResponse *RawClient + + options *core.RequestOptions + baseURL string + caller *internal.Caller +} + +func NewClient(options *core.RequestOptions) *Client { + return &Client{ + WithRawResponse: NewRawClient(options), + options: options, + baseURL: options.BaseURL, + caller: internal.NewCaller( + &internal.CallerParams{ + Client: options.HTTPClient, + MaxAttempts: options.MaxAttempts, + }, + ), + } +} + +func (c *Client) GetSomething( + ctx context.Context, + opts ...option.RequestOption, +) error { + _, err := c.WithRawResponse.GetSomething( + ctx, + opts..., + ) + if err != nil { + return err + } + return nil +} diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/nested/api/raw_client.go b/seed/go-sdk/inferred-auth-implicit-api-key/nested/api/raw_client.go new file mode 100644 index 000000000000..e42cb4c4dbe0 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/nested/api/raw_client.go @@ -0,0 +1,67 @@ +// Code generated by Fern. DO NOT EDIT. + +package api + +import ( + context "context" + core "github.com/inferred-auth-implicit-api-key/fern/core" + internal "github.com/inferred-auth-implicit-api-key/fern/internal" + option "github.com/inferred-auth-implicit-api-key/fern/option" + http "net/http" +) + +type RawClient struct { + baseURL string + caller *internal.Caller + options *core.RequestOptions +} + +func NewRawClient(options *core.RequestOptions) *RawClient { + return &RawClient{ + options: options, + baseURL: options.BaseURL, + caller: internal.NewCaller( + &internal.CallerParams{ + Client: options.HTTPClient, + MaxAttempts: options.MaxAttempts, + }, + ), + } +} + +func (r *RawClient) GetSomething( + ctx context.Context, + opts ...option.RequestOption, +) (*core.Response[any], error) { + options := core.NewRequestOptions(opts...) + baseURL := internal.ResolveBaseURL( + options.BaseURL, + r.baseURL, + "", + ) + endpointURL := baseURL + "/nested/get-something" + headers := internal.MergeHeaders( + r.options.ToHeader(), + options.ToHeader(), + ) + raw, err := r.caller.Call( + ctx, + &internal.CallParams{ + URL: endpointURL, + Method: http.MethodGet, + Headers: headers, + MaxAttempts: options.MaxAttempts, + BodyProperties: options.BodyProperties, + QueryParameters: options.QueryParameters, + Client: options.HTTPClient, + }, + ) + if err != nil { + return nil, err + } + return &core.Response[any]{ + StatusCode: raw.StatusCode, + Header: raw.Header, + Body: nil, + }, nil +} diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/nested/client/client.go b/seed/go-sdk/inferred-auth-implicit-api-key/nested/client/client.go new file mode 100644 index 000000000000..17167777390c --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/nested/client/client.go @@ -0,0 +1,31 @@ +// Code generated by Fern. DO NOT EDIT. + +package client + +import ( + core "github.com/inferred-auth-implicit-api-key/fern/core" + internal "github.com/inferred-auth-implicit-api-key/fern/internal" + api "github.com/inferred-auth-implicit-api-key/fern/nested/api" +) + +type Client struct { + Api *api.Client + + options *core.RequestOptions + baseURL string + caller *internal.Caller +} + +func NewClient(options *core.RequestOptions) *Client { + return &Client{ + Api: api.NewClient(options), + options: options, + baseURL: options.BaseURL, + caller: internal.NewCaller( + &internal.CallerParams{ + Client: options.HTTPClient, + MaxAttempts: options.MaxAttempts, + }, + ), + } +} diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/nestednoauth/api/client.go b/seed/go-sdk/inferred-auth-implicit-api-key/nestednoauth/api/client.go new file mode 100644 index 000000000000..43582086763b --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/nestednoauth/api/client.go @@ -0,0 +1,46 @@ +// Code generated by Fern. DO NOT EDIT. + +package api + +import ( + context "context" + core "github.com/inferred-auth-implicit-api-key/fern/core" + internal "github.com/inferred-auth-implicit-api-key/fern/internal" + option "github.com/inferred-auth-implicit-api-key/fern/option" +) + +type Client struct { + WithRawResponse *RawClient + + options *core.RequestOptions + baseURL string + caller *internal.Caller +} + +func NewClient(options *core.RequestOptions) *Client { + return &Client{ + WithRawResponse: NewRawClient(options), + options: options, + baseURL: options.BaseURL, + caller: internal.NewCaller( + &internal.CallerParams{ + Client: options.HTTPClient, + MaxAttempts: options.MaxAttempts, + }, + ), + } +} + +func (c *Client) GetSomething( + ctx context.Context, + opts ...option.RequestOption, +) error { + _, err := c.WithRawResponse.GetSomething( + ctx, + opts..., + ) + if err != nil { + return err + } + return nil +} diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/nestednoauth/api/raw_client.go b/seed/go-sdk/inferred-auth-implicit-api-key/nestednoauth/api/raw_client.go new file mode 100644 index 000000000000..b1bb367a9913 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/nestednoauth/api/raw_client.go @@ -0,0 +1,67 @@ +// Code generated by Fern. DO NOT EDIT. + +package api + +import ( + context "context" + core "github.com/inferred-auth-implicit-api-key/fern/core" + internal "github.com/inferred-auth-implicit-api-key/fern/internal" + option "github.com/inferred-auth-implicit-api-key/fern/option" + http "net/http" +) + +type RawClient struct { + baseURL string + caller *internal.Caller + options *core.RequestOptions +} + +func NewRawClient(options *core.RequestOptions) *RawClient { + return &RawClient{ + options: options, + baseURL: options.BaseURL, + caller: internal.NewCaller( + &internal.CallerParams{ + Client: options.HTTPClient, + MaxAttempts: options.MaxAttempts, + }, + ), + } +} + +func (r *RawClient) GetSomething( + ctx context.Context, + opts ...option.RequestOption, +) (*core.Response[any], error) { + options := core.NewRequestOptions(opts...) + baseURL := internal.ResolveBaseURL( + options.BaseURL, + r.baseURL, + "", + ) + endpointURL := baseURL + "/nested-no-auth/get-something" + headers := internal.MergeHeaders( + r.options.ToHeader(), + options.ToHeader(), + ) + raw, err := r.caller.Call( + ctx, + &internal.CallParams{ + URL: endpointURL, + Method: http.MethodGet, + Headers: headers, + MaxAttempts: options.MaxAttempts, + BodyProperties: options.BodyProperties, + QueryParameters: options.QueryParameters, + Client: options.HTTPClient, + }, + ) + if err != nil { + return nil, err + } + return &core.Response[any]{ + StatusCode: raw.StatusCode, + Header: raw.Header, + Body: nil, + }, nil +} diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/nestednoauth/client/client.go b/seed/go-sdk/inferred-auth-implicit-api-key/nestednoauth/client/client.go new file mode 100644 index 000000000000..cfae9909fc67 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/nestednoauth/client/client.go @@ -0,0 +1,31 @@ +// Code generated by Fern. DO NOT EDIT. + +package client + +import ( + core "github.com/inferred-auth-implicit-api-key/fern/core" + internal "github.com/inferred-auth-implicit-api-key/fern/internal" + api "github.com/inferred-auth-implicit-api-key/fern/nestednoauth/api" +) + +type Client struct { + Api *api.Client + + options *core.RequestOptions + baseURL string + caller *internal.Caller +} + +func NewClient(options *core.RequestOptions) *Client { + return &Client{ + Api: api.NewClient(options), + options: options, + baseURL: options.BaseURL, + caller: internal.NewCaller( + &internal.CallerParams{ + Client: options.HTTPClient, + MaxAttempts: options.MaxAttempts, + }, + ), + } +} diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/option/request_option.go b/seed/go-sdk/inferred-auth-implicit-api-key/option/request_option.go new file mode 100644 index 000000000000..ad670357a468 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/option/request_option.go @@ -0,0 +1,64 @@ +// Code generated by Fern. DO NOT EDIT. + +package option + +import ( + core "github.com/inferred-auth-implicit-api-key/fern/core" + http "net/http" + url "net/url" +) + +// RequestOption adapts the behavior of an individual request. +type RequestOption = core.RequestOption + +// WithBaseURL sets the base URL, overriding the default +// environment, if any. +func WithBaseURL(baseURL string) *core.BaseURLOption { + return &core.BaseURLOption{ + BaseURL: baseURL, + } +} + +// WithHTTPClient uses the given HTTPClient to issue the request. +func WithHTTPClient(httpClient core.HTTPClient) *core.HTTPClientOption { + return &core.HTTPClientOption{ + HTTPClient: httpClient, + } +} + +// WithHTTPHeader adds the given http.Header to the request. +func WithHTTPHeader(httpHeader http.Header) *core.HTTPHeaderOption { + return &core.HTTPHeaderOption{ + // Clone the headers so they can't be modified after the option call. + HTTPHeader: httpHeader.Clone(), + } +} + +// WithBodyProperties adds the given body properties to the request. +func WithBodyProperties(bodyProperties map[string]interface{}) *core.BodyPropertiesOption { + copiedBodyProperties := make(map[string]interface{}, len(bodyProperties)) + for key, value := range bodyProperties { + copiedBodyProperties[key] = value + } + return &core.BodyPropertiesOption{ + BodyProperties: copiedBodyProperties, + } +} + +// WithQueryParameters adds the given query parameters to the request. +func WithQueryParameters(queryParameters url.Values) *core.QueryParametersOption { + copiedQueryParameters := make(url.Values, len(queryParameters)) + for key, values := range queryParameters { + copiedQueryParameters[key] = values + } + return &core.QueryParametersOption{ + QueryParameters: copiedQueryParameters, + } +} + +// WithMaxAttempts configures the maximum number of retry attempts. +func WithMaxAttempts(attempts uint) *core.MaxAttemptsOption { + return &core.MaxAttemptsOption{ + MaxAttempts: attempts, + } +} diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/pointer.go b/seed/go-sdk/inferred-auth-implicit-api-key/pointer.go new file mode 100644 index 000000000000..4b836333367a --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/pointer.go @@ -0,0 +1,132 @@ +package inferredauthimplicitapikey + +import ( + "time" + + "github.com/google/uuid" +) + +// Bool returns a pointer to the given bool value. +func Bool(b bool) *bool { + return &b +} + +// Byte returns a pointer to the given byte value. +func Byte(b byte) *byte { + return &b +} + +// Complex64 returns a pointer to the given complex64 value. +func Complex64(c complex64) *complex64 { + return &c +} + +// Complex128 returns a pointer to the given complex128 value. +func Complex128(c complex128) *complex128 { + return &c +} + +// Float32 returns a pointer to the given float32 value. +func Float32(f float32) *float32 { + return &f +} + +// Float64 returns a pointer to the given float64 value. +func Float64(f float64) *float64 { + return &f +} + +// Int returns a pointer to the given int value. +func Int(i int) *int { + return &i +} + +// Int8 returns a pointer to the given int8 value. +func Int8(i int8) *int8 { + return &i +} + +// Int16 returns a pointer to the given int16 value. +func Int16(i int16) *int16 { + return &i +} + +// Int32 returns a pointer to the given int32 value. +func Int32(i int32) *int32 { + return &i +} + +// Int64 returns a pointer to the given int64 value. +func Int64(i int64) *int64 { + return &i +} + +// Rune returns a pointer to the given rune value. +func Rune(r rune) *rune { + return &r +} + +// String returns a pointer to the given string value. +func String(s string) *string { + return &s +} + +// Uint returns a pointer to the given uint value. +func Uint(u uint) *uint { + return &u +} + +// Uint8 returns a pointer to the given uint8 value. +func Uint8(u uint8) *uint8 { + return &u +} + +// Uint16 returns a pointer to the given uint16 value. +func Uint16(u uint16) *uint16 { + return &u +} + +// Uint32 returns a pointer to the given uint32 value. +func Uint32(u uint32) *uint32 { + return &u +} + +// Uint64 returns a pointer to the given uint64 value. +func Uint64(u uint64) *uint64 { + return &u +} + +// Uintptr returns a pointer to the given uintptr value. +func Uintptr(u uintptr) *uintptr { + return &u +} + +// UUID returns a pointer to the given uuid.UUID value. +func UUID(u uuid.UUID) *uuid.UUID { + return &u +} + +// Time returns a pointer to the given time.Time value. +func Time(t time.Time) *time.Time { + return &t +} + +// MustParseDate attempts to parse the given string as a +// date time.Time, and panics upon failure. +func MustParseDate(date string) time.Time { + t, err := time.Parse("2006-01-02", date) + if err != nil { + panic(err) + } + return t +} + +// MustParseDateTime attempts to parse the given string as a +// datetime time.Time, and panics upon failure. +func MustParseDateTime(datetime string) time.Time { + t, err := time.Parse(time.RFC3339, datetime) + if err != nil { + panic(err) + } + return t +} diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/reference.md b/seed/go-sdk/inferred-auth-implicit-api-key/reference.md new file mode 100644 index 000000000000..92a4bdbdbd96 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/reference.md @@ -0,0 +1,135 @@ +# Reference +## Auth +
client.Auth.GetToken() -> *fern.TokenResponse +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```go +request := &fern.GetTokenRequest{ + ApiKey: "api_key", + } +client.Auth.GetToken( + context.TODO(), + request, + ) +} +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**apiKey:** `string` + +
+
+
+
+ + +
+
+
+ +## NestedNoAuth Api +
client.NestedNoAuth.Api.GetSomething() -> error +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```go +client.NestedNoAuth.Api.GetSomething( + context.TODO(), + ) +} +``` +
+
+
+
+ + +
+
+
+ +## Nested Api +
client.Nested.Api.GetSomething() -> error +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```go +client.Nested.Api.GetSomething( + context.TODO(), + ) +} +``` +
+
+
+
+ + +
+
+
+ +## Simple +
client.Simple.GetSomething() -> error +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```go +client.Simple.GetSomething( + context.TODO(), + ) +} +``` +
+
+
+
+ + +
+
+
diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/simple/client.go b/seed/go-sdk/inferred-auth-implicit-api-key/simple/client.go new file mode 100644 index 000000000000..46d60770680b --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/simple/client.go @@ -0,0 +1,46 @@ +// Code generated by Fern. DO NOT EDIT. + +package simple + +import ( + context "context" + core "github.com/inferred-auth-implicit-api-key/fern/core" + internal "github.com/inferred-auth-implicit-api-key/fern/internal" + option "github.com/inferred-auth-implicit-api-key/fern/option" +) + +type Client struct { + WithRawResponse *RawClient + + options *core.RequestOptions + baseURL string + caller *internal.Caller +} + +func NewClient(options *core.RequestOptions) *Client { + return &Client{ + WithRawResponse: NewRawClient(options), + options: options, + baseURL: options.BaseURL, + caller: internal.NewCaller( + &internal.CallerParams{ + Client: options.HTTPClient, + MaxAttempts: options.MaxAttempts, + }, + ), + } +} + +func (c *Client) GetSomething( + ctx context.Context, + opts ...option.RequestOption, +) error { + _, err := c.WithRawResponse.GetSomething( + ctx, + opts..., + ) + if err != nil { + return err + } + return nil +} diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/simple/raw_client.go b/seed/go-sdk/inferred-auth-implicit-api-key/simple/raw_client.go new file mode 100644 index 000000000000..858ffaf64559 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/simple/raw_client.go @@ -0,0 +1,67 @@ +// Code generated by Fern. DO NOT EDIT. + +package simple + +import ( + context "context" + core "github.com/inferred-auth-implicit-api-key/fern/core" + internal "github.com/inferred-auth-implicit-api-key/fern/internal" + option "github.com/inferred-auth-implicit-api-key/fern/option" + http "net/http" +) + +type RawClient struct { + baseURL string + caller *internal.Caller + options *core.RequestOptions +} + +func NewRawClient(options *core.RequestOptions) *RawClient { + return &RawClient{ + options: options, + baseURL: options.BaseURL, + caller: internal.NewCaller( + &internal.CallerParams{ + Client: options.HTTPClient, + MaxAttempts: options.MaxAttempts, + }, + ), + } +} + +func (r *RawClient) GetSomething( + ctx context.Context, + opts ...option.RequestOption, +) (*core.Response[any], error) { + options := core.NewRequestOptions(opts...) + baseURL := internal.ResolveBaseURL( + options.BaseURL, + r.baseURL, + "", + ) + endpointURL := baseURL + "/get-something" + headers := internal.MergeHeaders( + r.options.ToHeader(), + options.ToHeader(), + ) + raw, err := r.caller.Call( + ctx, + &internal.CallParams{ + URL: endpointURL, + Method: http.MethodGet, + Headers: headers, + MaxAttempts: options.MaxAttempts, + BodyProperties: options.BodyProperties, + QueryParameters: options.QueryParameters, + Client: options.HTTPClient, + }, + ) + if err != nil { + return nil, err + } + return &core.Response[any]{ + StatusCode: raw.StatusCode, + Header: raw.Header, + Body: nil, + }, nil +} diff --git a/seed/go-sdk/inferred-auth-implicit-api-key/snippet.json b/seed/go-sdk/inferred-auth-implicit-api-key/snippet.json new file mode 100644 index 000000000000..a1ab5b90a4e0 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-api-key/snippet.json @@ -0,0 +1,48 @@ +{ + "endpoints": [ + { + "id": { + "path": "/get-something", + "method": "GET", + "identifier_override": "endpoint_simple.getSomething" + }, + "snippet": { + "type": "go", + "client": "import (\n\tcontext \"context\"\n\tfernclient \"github.com/inferred-auth-implicit-api-key/fern/client\"\n)\n\nclient := fernclient.NewClient()\nerr := client.Simple.GetSomething(\n\tcontext.TODO(),\n)\n" + } + }, + { + "id": { + "path": "/nested-no-auth/get-something", + "method": "GET", + "identifier_override": "endpoint_nested-no-auth/api.getSomething" + }, + "snippet": { + "type": "go", + "client": "import (\n\tcontext \"context\"\n\tfernclient \"github.com/inferred-auth-implicit-api-key/fern/client\"\n)\n\nclient := fernclient.NewClient()\nerr := client.NestedNoAuth.Api.GetSomething(\n\tcontext.TODO(),\n)\n" + } + }, + { + "id": { + "path": "/nested/get-something", + "method": "GET", + "identifier_override": "endpoint_nested/api.getSomething" + }, + "snippet": { + "type": "go", + "client": "import (\n\tcontext \"context\"\n\tfernclient \"github.com/inferred-auth-implicit-api-key/fern/client\"\n)\n\nclient := fernclient.NewClient()\nerr := client.Nested.Api.GetSomething(\n\tcontext.TODO(),\n)\n" + } + }, + { + "id": { + "path": "/token", + "method": "POST", + "identifier_override": "endpoint_auth.getToken" + }, + "snippet": { + "type": "go", + "client": "import (\n\tcontext \"context\"\n\tfern \"github.com/inferred-auth-implicit-api-key/fern\"\n\tfernclient \"github.com/inferred-auth-implicit-api-key/fern/client\"\n)\n\nclient := fernclient.NewClient()\nresponse, err := client.Auth.GetToken(\n\tcontext.TODO(),\n\t\u0026fern.GetTokenRequest{\n\t\tApiKey: \"api_key\",\n\t},\n)\n" + } + } + ] +} \ No newline at end of file diff --git a/seed/go-sdk/inferred-auth-implicit-no-expiry/dynamic-snippets/example0/snippet.go b/seed/go-sdk/inferred-auth-implicit-no-expiry/dynamic-snippets/example0/snippet.go index b3f0c78fc38e..74276b60b4b1 100644 --- a/seed/go-sdk/inferred-auth-implicit-no-expiry/dynamic-snippets/example0/snippet.go +++ b/seed/go-sdk/inferred-auth-implicit-no-expiry/dynamic-snippets/example0/snippet.go @@ -12,6 +12,7 @@ func do() { option.WithBaseURL( "https://api.fern.com", ), + nil, ) request := &fern.GetTokenRequest{ XApiKey: "X-Api-Key", diff --git a/seed/go-sdk/inferred-auth-implicit-no-expiry/dynamic-snippets/example1/snippet.go b/seed/go-sdk/inferred-auth-implicit-no-expiry/dynamic-snippets/example1/snippet.go index 78fd790af1ce..aca35ee79af5 100644 --- a/seed/go-sdk/inferred-auth-implicit-no-expiry/dynamic-snippets/example1/snippet.go +++ b/seed/go-sdk/inferred-auth-implicit-no-expiry/dynamic-snippets/example1/snippet.go @@ -12,6 +12,7 @@ func do() { option.WithBaseURL( "https://api.fern.com", ), + nil, ) request := &fern.RefreshTokenRequest{ XApiKey: "X-Api-Key", diff --git a/seed/go-sdk/inferred-auth-implicit-no-expiry/dynamic-snippets/example2/snippet.go b/seed/go-sdk/inferred-auth-implicit-no-expiry/dynamic-snippets/example2/snippet.go index 795cf7a7ce96..e1c5cee5c9cf 100644 --- a/seed/go-sdk/inferred-auth-implicit-no-expiry/dynamic-snippets/example2/snippet.go +++ b/seed/go-sdk/inferred-auth-implicit-no-expiry/dynamic-snippets/example2/snippet.go @@ -11,6 +11,7 @@ func do() { option.WithBaseURL( "https://api.fern.com", ), + nil, ) client.NestedNoAuth.Api.GetSomething( context.TODO(), diff --git a/seed/go-sdk/inferred-auth-implicit-no-expiry/dynamic-snippets/example3/snippet.go b/seed/go-sdk/inferred-auth-implicit-no-expiry/dynamic-snippets/example3/snippet.go index c94dadfe7f6e..314ba12c4593 100644 --- a/seed/go-sdk/inferred-auth-implicit-no-expiry/dynamic-snippets/example3/snippet.go +++ b/seed/go-sdk/inferred-auth-implicit-no-expiry/dynamic-snippets/example3/snippet.go @@ -11,6 +11,7 @@ func do() { option.WithBaseURL( "https://api.fern.com", ), + nil, ) client.Nested.Api.GetSomething( context.TODO(), diff --git a/seed/go-sdk/inferred-auth-implicit-no-expiry/dynamic-snippets/example4/snippet.go b/seed/go-sdk/inferred-auth-implicit-no-expiry/dynamic-snippets/example4/snippet.go index 9545c02c0cd1..51e51d756bd4 100644 --- a/seed/go-sdk/inferred-auth-implicit-no-expiry/dynamic-snippets/example4/snippet.go +++ b/seed/go-sdk/inferred-auth-implicit-no-expiry/dynamic-snippets/example4/snippet.go @@ -11,6 +11,7 @@ func do() { option.WithBaseURL( "https://api.fern.com", ), + nil, ) client.Simple.GetSomething( context.TODO(), diff --git a/seed/go-sdk/inferred-auth-implicit-reference/.fern/metadata.json b/seed/go-sdk/inferred-auth-implicit-reference/.fern/metadata.json new file mode 100644 index 000000000000..13cf07b7fbfd --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/.fern/metadata.json @@ -0,0 +1,8 @@ +{ + "cliVersion": "DUMMY", + "generatorName": "fernapi/fern-go-sdk", + "generatorVersion": "latest", + "generatorConfig": { + "enableWireTests": false + } +} \ No newline at end of file diff --git a/seed/go-sdk/inferred-auth-implicit-reference/.github/workflows/ci.yml b/seed/go-sdk/inferred-auth-implicit-reference/.github/workflows/ci.yml new file mode 100644 index 000000000000..56310d69624b --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/.github/workflows/ci.yml @@ -0,0 +1,35 @@ +name: ci + +on: [push] + +jobs: + compile: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up go + uses: actions/setup-go@v4 + + - name: Compile + run: go build ./... + test: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up go + uses: actions/setup-go@v4 + + - name: Setup wiremock server + run: | + if [ -f wiremock/docker-compose.test.yml ]; then docker compose -f wiremock/docker-compose.test.yml down && docker compose -f wiremock/docker-compose.test.yml up -d; fi + + - name: Test + run: go test ./... + + - name: Teardown wiremock server + run: | + if [ -f wiremock/docker-compose.test.yml ]; then docker compose -f wiremock/docker-compose.test.yml down; fi diff --git a/seed/go-sdk/inferred-auth-implicit-reference/README.md b/seed/go-sdk/inferred-auth-implicit-reference/README.md new file mode 100644 index 000000000000..0fbb762bddec --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/README.md @@ -0,0 +1,199 @@ +# Seed Go Library + +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FGo) + +The Seed Go library provides convenient access to the Seed APIs from Go. + +## Table of Contents + +- [Reference](#reference) +- [Usage](#usage) +- [Environments](#environments) +- [Errors](#errors) +- [Request Options](#request-options) +- [Advanced](#advanced) + - [Response Headers](#response-headers) + - [Retries](#retries) + - [Timeouts](#timeouts) + - [Explicit Null](#explicit-null) +- [Contributing](#contributing) + +## Reference + +A full reference for this library is available [here](./reference.md). + +## Usage + +Instantiate and use the client with the following: + +```go +package example + +import ( + client "github.com/inferred-auth-implicit-reference/fern/client" + fern "github.com/inferred-auth-implicit-reference/fern" + context "context" +) + +func do() { + client := client.NewClient( + nil, + ) + request := &fern.GetTokenRequest{ + ClientId: "client_id", + ClientSecret: "client_secret", + Scope: fern.String( + "scope", + ), + } + client.Auth.GetTokenWithClientCredentials( + context.TODO(), + request, + ) +} +``` + +## Environments + +You can choose between different environments by using the `option.WithBaseURL` option. You can configure any arbitrary base +URL, which is particularly useful in test environments. + +```go +client := client.NewClient( + option.WithBaseURL("https://example.com"), +) +``` + +## Errors + +Structured error types are returned from API calls that return non-success status codes. These errors are compatible +with the `errors.Is` and `errors.As` APIs, so you can access the error like so: + +```go +response, err := client.Auth.GetTokenWithClientCredentials(...) +if err != nil { + var apiError *core.APIError + if errors.As(err, apiError) { + // Do something with the API error ... + } + return err +} +``` + +## Request Options + +A variety of request options are included to adapt the behavior of the library, which includes configuring +authorization tokens, or providing your own instrumented `*http.Client`. + +These request options can either be +specified on the client so that they're applied on every request, or for an individual request, like so: + +> Providing your own `*http.Client` is recommended. Otherwise, the `http.DefaultClient` will be used, +> and your client will wait indefinitely for a response (unless the per-request, context-based timeout +> is used). + +```go +// Specify default options applied on every request. +client := client.NewClient( + option.WithToken(""), + option.WithHTTPClient( + &http.Client{ + Timeout: 5 * time.Second, + }, + ), +) + +// Specify options for an individual request. +response, err := client.Auth.GetTokenWithClientCredentials( + ..., + option.WithToken(""), +) +``` + +## Advanced + +### Response Headers + +You can access the raw HTTP response data by using the `WithRawResponse` field on the client. This is useful +when you need to examine the response headers received from the API call. (When the endpoint is paginated, +the raw HTTP response data will be included automatically in the Page response object.) + +```go +response, err := client.Auth.WithRawResponse.GetTokenWithClientCredentials(...) +if err != nil { + return err +} +fmt.Printf("Got response headers: %v", response.Header) +fmt.Printf("Got status code: %d", response.StatusCode) +``` + +### Retries + +The SDK is instrumented with automatic retries with exponential backoff. A request will be retried as long +as the request is deemed retryable and the number of retry attempts has not grown larger than the configured +retry limit (default: 2). + +A request is deemed retryable when any of the following HTTP status codes is returned: + +- [408](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/408) (Timeout) +- [429](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429) (Too Many Requests) +- [5XX](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500) (Internal Server Errors) + +If the `Retry-After` header is present in the response, the SDK will prioritize respecting its value exactly +over the default exponential backoff. + +Use the `option.WithMaxAttempts` option to configure this behavior for the entire client or an individual request: + +```go +client := client.NewClient( + option.WithMaxAttempts(1), +) + +response, err := client.Auth.GetTokenWithClientCredentials( + ..., + option.WithMaxAttempts(1), +) +``` + +### Timeouts + +Setting a timeout for each individual request is as simple as using the standard context library. Setting a one second timeout for an individual API call looks like the following: + +```go +ctx, cancel := context.WithTimeout(ctx, time.Second) +defer cancel() + +response, err := client.Auth.GetTokenWithClientCredentials(ctx, ...) +``` + +### Explicit Null + +If you want to send the explicit `null` JSON value through an optional parameter, you can use the setters\ +that come with every object. Calling a setter method for a property will flip a bit in the `explicitFields` +bitfield for that setter's object; during serialization, any property with a flipped bit will have its +omittable status stripped, so zero or `nil` values will be sent explicitly rather than omitted altogether: + +```go +type ExampleRequest struct { + // An optional string parameter. + Name *string `json:"name,omitempty" url:"-"` + + // Private bitmask of fields set to an explicit value and therefore not to be omitted + explicitFields *big.Int `json:"-" url:"-"` +} + +request := &ExampleRequest{} +request.SetName(nil) + +response, err := client.Auth.GetTokenWithClientCredentials(ctx, request, ...) +``` + +## Contributing + +While we value open-source contributions to this SDK, this library is generated programmatically. +Additions made directly to this library would have to be moved over to our generation code, +otherwise they would be overwritten upon the next generated release. Feel free to open a PR as +a proof of concept, but know that we will not be able to merge it as-is. We suggest opening +an issue first to discuss with us! + +On the other hand, contributions to the README are always very welcome! \ No newline at end of file diff --git a/seed/go-sdk/inferred-auth-implicit-reference/auth.go b/seed/go-sdk/inferred-auth-implicit-reference/auth.go new file mode 100644 index 000000000000..593873f3bd35 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/auth.go @@ -0,0 +1,415 @@ +// Code generated by Fern. DO NOT EDIT. + +package inferredauthimplicit + +import ( + json "encoding/json" + fmt "fmt" + internal "github.com/inferred-auth-implicit-reference/fern/internal" + big "math/big" +) + +// A request to obtain an OAuth token. +var ( + getTokenRequestFieldClientId = big.NewInt(1 << 0) + getTokenRequestFieldClientSecret = big.NewInt(1 << 1) + getTokenRequestFieldScope = big.NewInt(1 << 2) +) + +type GetTokenRequest struct { + ClientId string `json:"client_id" url:"client_id"` + ClientSecret string `json:"client_secret" url:"client_secret"` + Scope *string `json:"scope,omitempty" url:"scope,omitempty"` + + // Private bitmask of fields set to an explicit value and therefore not to be omitted + explicitFields *big.Int `json:"-" url:"-"` + audience string + grantType string + + extraProperties map[string]interface{} + rawJSON json.RawMessage +} + +func (g *GetTokenRequest) GetClientId() string { + if g == nil { + return "" + } + return g.ClientId +} + +func (g *GetTokenRequest) GetClientSecret() string { + if g == nil { + return "" + } + return g.ClientSecret +} + +func (g *GetTokenRequest) GetScope() *string { + if g == nil { + return nil + } + return g.Scope +} + +func (g *GetTokenRequest) Audience() string { + return g.audience +} + +func (g *GetTokenRequest) GrantType() string { + return g.grantType +} + +func (g *GetTokenRequest) GetExtraProperties() map[string]interface{} { + return g.extraProperties +} + +func (g *GetTokenRequest) require(field *big.Int) { + if g.explicitFields == nil { + g.explicitFields = big.NewInt(0) + } + g.explicitFields.Or(g.explicitFields, field) +} + +// SetClientId sets the ClientId field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (g *GetTokenRequest) SetClientId(clientId string) { + g.ClientId = clientId + g.require(getTokenRequestFieldClientId) +} + +// SetClientSecret sets the ClientSecret field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (g *GetTokenRequest) SetClientSecret(clientSecret string) { + g.ClientSecret = clientSecret + g.require(getTokenRequestFieldClientSecret) +} + +// SetScope sets the Scope field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (g *GetTokenRequest) SetScope(scope *string) { + g.Scope = scope + g.require(getTokenRequestFieldScope) +} + +func (g *GetTokenRequest) UnmarshalJSON(data []byte) error { + type embed GetTokenRequest + var unmarshaler = struct { + embed + Audience string `json:"audience"` + GrantType string `json:"grant_type"` + }{ + embed: embed(*g), + } + if err := json.Unmarshal(data, &unmarshaler); err != nil { + return err + } + *g = GetTokenRequest(unmarshaler.embed) + if unmarshaler.Audience != "https://api.example.com" { + return fmt.Errorf("unexpected value for literal on type %T; expected %v got %v", g, "https://api.example.com", unmarshaler.Audience) + } + g.audience = unmarshaler.Audience + if unmarshaler.GrantType != "client_credentials" { + return fmt.Errorf("unexpected value for literal on type %T; expected %v got %v", g, "client_credentials", unmarshaler.GrantType) + } + g.grantType = unmarshaler.GrantType + extraProperties, err := internal.ExtractExtraProperties(data, *g, "audience", "grant_type") + if err != nil { + return err + } + g.extraProperties = extraProperties + g.rawJSON = json.RawMessage(data) + return nil +} + +func (g *GetTokenRequest) MarshalJSON() ([]byte, error) { + type embed GetTokenRequest + var marshaler = struct { + embed + Audience string `json:"audience"` + GrantType string `json:"grant_type"` + }{ + embed: embed(*g), + Audience: "https://api.example.com", + GrantType: "client_credentials", + } + explicitMarshaler := internal.HandleExplicitFields(marshaler, g.explicitFields) + return json.Marshal(explicitMarshaler) +} + +func (g *GetTokenRequest) String() string { + if len(g.rawJSON) > 0 { + if value, err := internal.StringifyJSON(g.rawJSON); err == nil { + return value + } + } + if value, err := internal.StringifyJSON(g); err == nil { + return value + } + return fmt.Sprintf("%#v", g) +} + +// A request to refresh an OAuth token. +var ( + refreshTokenRequestFieldClientId = big.NewInt(1 << 0) + refreshTokenRequestFieldClientSecret = big.NewInt(1 << 1) + refreshTokenRequestFieldRefreshToken = big.NewInt(1 << 2) + refreshTokenRequestFieldScope = big.NewInt(1 << 3) +) + +type RefreshTokenRequest struct { + ClientId string `json:"client_id" url:"client_id"` + ClientSecret string `json:"client_secret" url:"client_secret"` + RefreshToken string `json:"refresh_token" url:"refresh_token"` + Scope *string `json:"scope,omitempty" url:"scope,omitempty"` + + // Private bitmask of fields set to an explicit value and therefore not to be omitted + explicitFields *big.Int `json:"-" url:"-"` + audience string + grantType string + + extraProperties map[string]interface{} + rawJSON json.RawMessage +} + +func (r *RefreshTokenRequest) GetClientId() string { + if r == nil { + return "" + } + return r.ClientId +} + +func (r *RefreshTokenRequest) GetClientSecret() string { + if r == nil { + return "" + } + return r.ClientSecret +} + +func (r *RefreshTokenRequest) GetRefreshToken() string { + if r == nil { + return "" + } + return r.RefreshToken +} + +func (r *RefreshTokenRequest) GetScope() *string { + if r == nil { + return nil + } + return r.Scope +} + +func (r *RefreshTokenRequest) Audience() string { + return r.audience +} + +func (r *RefreshTokenRequest) GrantType() string { + return r.grantType +} + +func (r *RefreshTokenRequest) GetExtraProperties() map[string]interface{} { + return r.extraProperties +} + +func (r *RefreshTokenRequest) require(field *big.Int) { + if r.explicitFields == nil { + r.explicitFields = big.NewInt(0) + } + r.explicitFields.Or(r.explicitFields, field) +} + +// SetClientId sets the ClientId field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (r *RefreshTokenRequest) SetClientId(clientId string) { + r.ClientId = clientId + r.require(refreshTokenRequestFieldClientId) +} + +// SetClientSecret sets the ClientSecret field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (r *RefreshTokenRequest) SetClientSecret(clientSecret string) { + r.ClientSecret = clientSecret + r.require(refreshTokenRequestFieldClientSecret) +} + +// SetRefreshToken sets the RefreshToken field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (r *RefreshTokenRequest) SetRefreshToken(refreshToken string) { + r.RefreshToken = refreshToken + r.require(refreshTokenRequestFieldRefreshToken) +} + +// SetScope sets the Scope field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (r *RefreshTokenRequest) SetScope(scope *string) { + r.Scope = scope + r.require(refreshTokenRequestFieldScope) +} + +func (r *RefreshTokenRequest) UnmarshalJSON(data []byte) error { + type embed RefreshTokenRequest + var unmarshaler = struct { + embed + Audience string `json:"audience"` + GrantType string `json:"grant_type"` + }{ + embed: embed(*r), + } + if err := json.Unmarshal(data, &unmarshaler); err != nil { + return err + } + *r = RefreshTokenRequest(unmarshaler.embed) + if unmarshaler.Audience != "https://api.example.com" { + return fmt.Errorf("unexpected value for literal on type %T; expected %v got %v", r, "https://api.example.com", unmarshaler.Audience) + } + r.audience = unmarshaler.Audience + if unmarshaler.GrantType != "refresh_token" { + return fmt.Errorf("unexpected value for literal on type %T; expected %v got %v", r, "refresh_token", unmarshaler.GrantType) + } + r.grantType = unmarshaler.GrantType + extraProperties, err := internal.ExtractExtraProperties(data, *r, "audience", "grant_type") + if err != nil { + return err + } + r.extraProperties = extraProperties + r.rawJSON = json.RawMessage(data) + return nil +} + +func (r *RefreshTokenRequest) MarshalJSON() ([]byte, error) { + type embed RefreshTokenRequest + var marshaler = struct { + embed + Audience string `json:"audience"` + GrantType string `json:"grant_type"` + }{ + embed: embed(*r), + Audience: "https://api.example.com", + GrantType: "refresh_token", + } + explicitMarshaler := internal.HandleExplicitFields(marshaler, r.explicitFields) + return json.Marshal(explicitMarshaler) +} + +func (r *RefreshTokenRequest) String() string { + if len(r.rawJSON) > 0 { + if value, err := internal.StringifyJSON(r.rawJSON); err == nil { + return value + } + } + if value, err := internal.StringifyJSON(r); err == nil { + return value + } + return fmt.Sprintf("%#v", r) +} + +// An OAuth token response. +var ( + tokenResponseFieldAccessToken = big.NewInt(1 << 0) + tokenResponseFieldExpiresIn = big.NewInt(1 << 1) + tokenResponseFieldRefreshToken = big.NewInt(1 << 2) +) + +type TokenResponse struct { + AccessToken string `json:"access_token" url:"access_token"` + ExpiresIn int `json:"expires_in" url:"expires_in"` + RefreshToken *string `json:"refresh_token,omitempty" url:"refresh_token,omitempty"` + + // Private bitmask of fields set to an explicit value and therefore not to be omitted + explicitFields *big.Int `json:"-" url:"-"` + + extraProperties map[string]interface{} + rawJSON json.RawMessage +} + +func (t *TokenResponse) GetAccessToken() string { + if t == nil { + return "" + } + return t.AccessToken +} + +func (t *TokenResponse) GetExpiresIn() int { + if t == nil { + return 0 + } + return t.ExpiresIn +} + +func (t *TokenResponse) GetRefreshToken() *string { + if t == nil { + return nil + } + return t.RefreshToken +} + +func (t *TokenResponse) GetExtraProperties() map[string]interface{} { + return t.extraProperties +} + +func (t *TokenResponse) require(field *big.Int) { + if t.explicitFields == nil { + t.explicitFields = big.NewInt(0) + } + t.explicitFields.Or(t.explicitFields, field) +} + +// SetAccessToken sets the AccessToken field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (t *TokenResponse) SetAccessToken(accessToken string) { + t.AccessToken = accessToken + t.require(tokenResponseFieldAccessToken) +} + +// SetExpiresIn sets the ExpiresIn field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (t *TokenResponse) SetExpiresIn(expiresIn int) { + t.ExpiresIn = expiresIn + t.require(tokenResponseFieldExpiresIn) +} + +// SetRefreshToken sets the RefreshToken field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (t *TokenResponse) SetRefreshToken(refreshToken *string) { + t.RefreshToken = refreshToken + t.require(tokenResponseFieldRefreshToken) +} + +func (t *TokenResponse) UnmarshalJSON(data []byte) error { + type unmarshaler TokenResponse + var value unmarshaler + if err := json.Unmarshal(data, &value); err != nil { + return err + } + *t = TokenResponse(value) + extraProperties, err := internal.ExtractExtraProperties(data, *t) + if err != nil { + return err + } + t.extraProperties = extraProperties + t.rawJSON = json.RawMessage(data) + return nil +} + +func (t *TokenResponse) MarshalJSON() ([]byte, error) { + type embed TokenResponse + var marshaler = struct { + embed + }{ + embed: embed(*t), + } + explicitMarshaler := internal.HandleExplicitFields(marshaler, t.explicitFields) + return json.Marshal(explicitMarshaler) +} + +func (t *TokenResponse) String() string { + if len(t.rawJSON) > 0 { + if value, err := internal.StringifyJSON(t.rawJSON); err == nil { + return value + } + } + if value, err := internal.StringifyJSON(t); err == nil { + return value + } + return fmt.Sprintf("%#v", t) +} diff --git a/seed/go-sdk/inferred-auth-implicit-reference/auth/client.go b/seed/go-sdk/inferred-auth-implicit-reference/auth/client.go new file mode 100644 index 000000000000..e8a98ec4b17b --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/auth/client.go @@ -0,0 +1,65 @@ +// Code generated by Fern. DO NOT EDIT. + +package auth + +import ( + context "context" + fern "github.com/inferred-auth-implicit-reference/fern" + core "github.com/inferred-auth-implicit-reference/fern/core" + internal "github.com/inferred-auth-implicit-reference/fern/internal" + option "github.com/inferred-auth-implicit-reference/fern/option" +) + +type Client struct { + WithRawResponse *RawClient + + options *core.RequestOptions + baseURL string + caller *internal.Caller +} + +func NewClient(options *core.RequestOptions) *Client { + return &Client{ + WithRawResponse: NewRawClient(options), + options: options, + baseURL: options.BaseURL, + caller: internal.NewCaller( + &internal.CallerParams{ + Client: options.HTTPClient, + MaxAttempts: options.MaxAttempts, + }, + ), + } +} + +func (c *Client) GetTokenWithClientCredentials( + ctx context.Context, + request *fern.GetTokenRequest, + opts ...option.RequestOption, +) (*fern.TokenResponse, error) { + response, err := c.WithRawResponse.GetTokenWithClientCredentials( + ctx, + request, + opts..., + ) + if err != nil { + return nil, err + } + return response.Body, nil +} + +func (c *Client) RefreshToken( + ctx context.Context, + request *fern.RefreshTokenRequest, + opts ...option.RequestOption, +) (*fern.TokenResponse, error) { + response, err := c.WithRawResponse.RefreshToken( + ctx, + request, + opts..., + ) + if err != nil { + return nil, err + } + return response.Body, nil +} diff --git a/seed/go-sdk/inferred-auth-implicit-reference/auth/raw_client.go b/seed/go-sdk/inferred-auth-implicit-reference/auth/raw_client.go new file mode 100644 index 000000000000..387c9c9c588c --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/auth/raw_client.go @@ -0,0 +1,113 @@ +// Code generated by Fern. DO NOT EDIT. + +package auth + +import ( + context "context" + fern "github.com/inferred-auth-implicit-reference/fern" + core "github.com/inferred-auth-implicit-reference/fern/core" + internal "github.com/inferred-auth-implicit-reference/fern/internal" + option "github.com/inferred-auth-implicit-reference/fern/option" + http "net/http" +) + +type RawClient struct { + baseURL string + caller *internal.Caller + options *core.RequestOptions +} + +func NewRawClient(options *core.RequestOptions) *RawClient { + return &RawClient{ + options: options, + baseURL: options.BaseURL, + caller: internal.NewCaller( + &internal.CallerParams{ + Client: options.HTTPClient, + MaxAttempts: options.MaxAttempts, + }, + ), + } +} + +func (r *RawClient) GetTokenWithClientCredentials( + ctx context.Context, + request *fern.GetTokenRequest, + opts ...option.RequestOption, +) (*core.Response[*fern.TokenResponse], error) { + options := core.NewRequestOptions(opts...) + baseURL := internal.ResolveBaseURL( + options.BaseURL, + r.baseURL, + "", + ) + endpointURL := baseURL + "/token" + headers := internal.MergeHeaders( + r.options.ToHeader(), + options.ToHeader(), + ) + var response *fern.TokenResponse + raw, err := r.caller.Call( + ctx, + &internal.CallParams{ + URL: endpointURL, + Method: http.MethodPost, + Headers: headers, + MaxAttempts: options.MaxAttempts, + BodyProperties: options.BodyProperties, + QueryParameters: options.QueryParameters, + Client: options.HTTPClient, + Request: request, + Response: &response, + }, + ) + if err != nil { + return nil, err + } + return &core.Response[*fern.TokenResponse]{ + StatusCode: raw.StatusCode, + Header: raw.Header, + Body: response, + }, nil +} + +func (r *RawClient) RefreshToken( + ctx context.Context, + request *fern.RefreshTokenRequest, + opts ...option.RequestOption, +) (*core.Response[*fern.TokenResponse], error) { + options := core.NewRequestOptions(opts...) + baseURL := internal.ResolveBaseURL( + options.BaseURL, + r.baseURL, + "", + ) + endpointURL := baseURL + "/token/refresh" + headers := internal.MergeHeaders( + r.options.ToHeader(), + options.ToHeader(), + ) + var response *fern.TokenResponse + raw, err := r.caller.Call( + ctx, + &internal.CallParams{ + URL: endpointURL, + Method: http.MethodPost, + Headers: headers, + MaxAttempts: options.MaxAttempts, + BodyProperties: options.BodyProperties, + QueryParameters: options.QueryParameters, + Client: options.HTTPClient, + Request: request, + Response: &response, + }, + ) + if err != nil { + return nil, err + } + return &core.Response[*fern.TokenResponse]{ + StatusCode: raw.StatusCode, + Header: raw.Header, + Body: response, + }, nil +} diff --git a/seed/go-sdk/inferred-auth-implicit-reference/client/client.go b/seed/go-sdk/inferred-auth-implicit-reference/client/client.go new file mode 100644 index 000000000000..f3b16283cbf0 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/client/client.go @@ -0,0 +1,42 @@ +// Code generated by Fern. DO NOT EDIT. + +package client + +import ( + auth "github.com/inferred-auth-implicit-reference/fern/auth" + core "github.com/inferred-auth-implicit-reference/fern/core" + internal "github.com/inferred-auth-implicit-reference/fern/internal" + nestedclient "github.com/inferred-auth-implicit-reference/fern/nested/client" + client "github.com/inferred-auth-implicit-reference/fern/nestednoauth/client" + option "github.com/inferred-auth-implicit-reference/fern/option" + simple "github.com/inferred-auth-implicit-reference/fern/simple" +) + +type Client struct { + Auth *auth.Client + NestedNoAuth *client.Client + Nested *nestedclient.Client + Simple *simple.Client + + options *core.RequestOptions + baseURL string + caller *internal.Caller +} + +func NewClient(opts ...option.RequestOption) *Client { + options := core.NewRequestOptions(opts...) + return &Client{ + Auth: auth.NewClient(options), + NestedNoAuth: client.NewClient(options), + Nested: nestedclient.NewClient(options), + Simple: simple.NewClient(options), + options: options, + baseURL: options.BaseURL, + caller: internal.NewCaller( + &internal.CallerParams{ + Client: options.HTTPClient, + MaxAttempts: options.MaxAttempts, + }, + ), + } +} diff --git a/seed/go-sdk/inferred-auth-implicit-reference/client/client_test.go b/seed/go-sdk/inferred-auth-implicit-reference/client/client_test.go new file mode 100644 index 000000000000..4b6cf8630aab --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/client/client_test.go @@ -0,0 +1,45 @@ +// Code generated by Fern. DO NOT EDIT. + +package client + +import ( + option "github.com/inferred-auth-implicit-reference/fern/option" + assert "github.com/stretchr/testify/assert" + http "net/http" + testing "testing" + time "time" +) + +func TestNewClient(t *testing.T) { + t.Run("default", func(t *testing.T) { + c := NewClient() + assert.Empty(t, c.baseURL) + }) + + t.Run("base url", func(t *testing.T) { + c := NewClient( + option.WithBaseURL("test.co"), + ) + assert.Equal(t, "test.co", c.baseURL) + }) + + t.Run("http client", func(t *testing.T) { + httpClient := &http.Client{ + Timeout: 5 * time.Second, + } + c := NewClient( + option.WithHTTPClient(httpClient), + ) + assert.Empty(t, c.baseURL) + }) + + t.Run("http header", func(t *testing.T) { + header := make(http.Header) + header.Set("X-API-Tenancy", "test") + c := NewClient( + option.WithHTTPHeader(header), + ) + assert.Empty(t, c.baseURL) + assert.Equal(t, "test", c.options.HTTPHeader.Get("X-API-Tenancy")) + }) +} diff --git a/seed/go-sdk/inferred-auth-implicit-reference/core/api_error.go b/seed/go-sdk/inferred-auth-implicit-reference/core/api_error.go new file mode 100644 index 000000000000..6168388541b4 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/core/api_error.go @@ -0,0 +1,47 @@ +package core + +import ( + "fmt" + "net/http" +) + +// APIError is a lightweight wrapper around the standard error +// interface that preserves the status code from the RPC, if any. +type APIError struct { + err error + + StatusCode int `json:"-"` + Header http.Header `json:"-"` +} + +// NewAPIError constructs a new API error. +func NewAPIError(statusCode int, header http.Header, err error) *APIError { + return &APIError{ + err: err, + Header: header, + StatusCode: statusCode, + } +} + +// Unwrap returns the underlying error. This also makes the error compatible +// with errors.As and errors.Is. +func (a *APIError) Unwrap() error { + if a == nil { + return nil + } + return a.err +} + +// Error returns the API error's message. +func (a *APIError) Error() string { + if a == nil || (a.err == nil && a.StatusCode == 0) { + return "" + } + if a.err == nil { + return fmt.Sprintf("%d", a.StatusCode) + } + if a.StatusCode == 0 { + return a.err.Error() + } + return fmt.Sprintf("%d: %s", a.StatusCode, a.err.Error()) +} diff --git a/seed/go-sdk/inferred-auth-implicit-reference/core/http.go b/seed/go-sdk/inferred-auth-implicit-reference/core/http.go new file mode 100644 index 000000000000..92c435692940 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/core/http.go @@ -0,0 +1,15 @@ +package core + +import "net/http" + +// HTTPClient is an interface for a subset of the *http.Client. +type HTTPClient interface { + Do(*http.Request) (*http.Response, error) +} + +// Response is an HTTP response from an HTTP client. +type Response[T any] struct { + StatusCode int + Header http.Header + Body T +} diff --git a/seed/go-sdk/inferred-auth-implicit-reference/core/request_option.go b/seed/go-sdk/inferred-auth-implicit-reference/core/request_option.go new file mode 100644 index 000000000000..3dd7305ee1ab --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/core/request_option.go @@ -0,0 +1,112 @@ +// Code generated by Fern. DO NOT EDIT. + +package core + +import ( + http "net/http" + url "net/url" +) + +// RequestOption adapts the behavior of the client or an individual request. +type RequestOption interface { + applyRequestOptions(*RequestOptions) +} + +// RequestOptions defines all of the possible request options. +// +// This type is primarily used by the generated code and is not meant +// to be used directly; use the option package instead. +type RequestOptions struct { + BaseURL string + HTTPClient HTTPClient + HTTPHeader http.Header + BodyProperties map[string]interface{} + QueryParameters url.Values + MaxAttempts uint +} + +// NewRequestOptions returns a new *RequestOptions value. +// +// This function is primarily used by the generated code and is not meant +// to be used directly; use RequestOption instead. +func NewRequestOptions(opts ...RequestOption) *RequestOptions { + options := &RequestOptions{ + HTTPHeader: make(http.Header), + BodyProperties: make(map[string]interface{}), + QueryParameters: make(url.Values), + } + for _, opt := range opts { + opt.applyRequestOptions(options) + } + return options +} + +// ToHeader maps the configured request options into a http.Header used +// for the request(s). +func (r *RequestOptions) ToHeader() http.Header { + header := r.cloneHeader() + return header +} + +func (r *RequestOptions) cloneHeader() http.Header { + headers := r.HTTPHeader.Clone() + headers.Set("X-Fern-Language", "Go") + headers.Set("X-Fern-SDK-Name", "github.com/inferred-auth-implicit-reference/fern") + headers.Set("X-Fern-SDK-Version", "v0.0.1") + headers.Set("User-Agent", "github.com/inferred-auth-implicit-reference/fern/0.0.1") + return headers +} + +// BaseURLOption implements the RequestOption interface. +type BaseURLOption struct { + BaseURL string +} + +func (b *BaseURLOption) applyRequestOptions(opts *RequestOptions) { + opts.BaseURL = b.BaseURL +} + +// HTTPClientOption implements the RequestOption interface. +type HTTPClientOption struct { + HTTPClient HTTPClient +} + +func (h *HTTPClientOption) applyRequestOptions(opts *RequestOptions) { + opts.HTTPClient = h.HTTPClient +} + +// HTTPHeaderOption implements the RequestOption interface. +type HTTPHeaderOption struct { + HTTPHeader http.Header +} + +func (h *HTTPHeaderOption) applyRequestOptions(opts *RequestOptions) { + opts.HTTPHeader = h.HTTPHeader +} + +// BodyPropertiesOption implements the RequestOption interface. +type BodyPropertiesOption struct { + BodyProperties map[string]interface{} +} + +func (b *BodyPropertiesOption) applyRequestOptions(opts *RequestOptions) { + opts.BodyProperties = b.BodyProperties +} + +// QueryParametersOption implements the RequestOption interface. +type QueryParametersOption struct { + QueryParameters url.Values +} + +func (q *QueryParametersOption) applyRequestOptions(opts *RequestOptions) { + opts.QueryParameters = q.QueryParameters +} + +// MaxAttemptsOption implements the RequestOption interface. +type MaxAttemptsOption struct { + MaxAttempts uint +} + +func (m *MaxAttemptsOption) applyRequestOptions(opts *RequestOptions) { + opts.MaxAttempts = m.MaxAttempts +} diff --git a/seed/go-sdk/inferred-auth-implicit-reference/dynamic-snippets/example0/snippet.go b/seed/go-sdk/inferred-auth-implicit-reference/dynamic-snippets/example0/snippet.go new file mode 100644 index 000000000000..19c7d23a5499 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/dynamic-snippets/example0/snippet.go @@ -0,0 +1,28 @@ +package example + +import ( + client "github.com/inferred-auth-implicit-reference/fern/client" + option "github.com/inferred-auth-implicit-reference/fern/option" + fern "github.com/inferred-auth-implicit-reference/fern" + context "context" +) + +func do() { + client := client.NewClient( + option.WithBaseURL( + "https://api.fern.com", + ), + nil, + ) + request := &fern.GetTokenRequest{ + ClientId: "client_id", + ClientSecret: "client_secret", + Scope: fern.String( + "scope", + ), + } + client.Auth.GetTokenWithClientCredentials( + context.TODO(), + request, + ) +} diff --git a/seed/go-sdk/inferred-auth-implicit-reference/dynamic-snippets/example1/snippet.go b/seed/go-sdk/inferred-auth-implicit-reference/dynamic-snippets/example1/snippet.go new file mode 100644 index 000000000000..ee38113f1642 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/dynamic-snippets/example1/snippet.go @@ -0,0 +1,29 @@ +package example + +import ( + client "github.com/inferred-auth-implicit-reference/fern/client" + option "github.com/inferred-auth-implicit-reference/fern/option" + fern "github.com/inferred-auth-implicit-reference/fern" + context "context" +) + +func do() { + client := client.NewClient( + option.WithBaseURL( + "https://api.fern.com", + ), + nil, + ) + request := &fern.RefreshTokenRequest{ + ClientId: "client_id", + ClientSecret: "client_secret", + RefreshToken: "refresh_token", + Scope: fern.String( + "scope", + ), + } + client.Auth.RefreshToken( + context.TODO(), + request, + ) +} diff --git a/seed/go-sdk/inferred-auth-implicit-reference/dynamic-snippets/example2/snippet.go b/seed/go-sdk/inferred-auth-implicit-reference/dynamic-snippets/example2/snippet.go new file mode 100644 index 000000000000..dbb58bda7294 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/dynamic-snippets/example2/snippet.go @@ -0,0 +1,19 @@ +package example + +import ( + client "github.com/inferred-auth-implicit-reference/fern/client" + option "github.com/inferred-auth-implicit-reference/fern/option" + context "context" +) + +func do() { + client := client.NewClient( + option.WithBaseURL( + "https://api.fern.com", + ), + nil, + ) + client.NestedNoAuth.Api.GetSomething( + context.TODO(), + ) +} diff --git a/seed/go-sdk/inferred-auth-implicit-reference/dynamic-snippets/example3/snippet.go b/seed/go-sdk/inferred-auth-implicit-reference/dynamic-snippets/example3/snippet.go new file mode 100644 index 000000000000..508e4ece0cae --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/dynamic-snippets/example3/snippet.go @@ -0,0 +1,19 @@ +package example + +import ( + client "github.com/inferred-auth-implicit-reference/fern/client" + option "github.com/inferred-auth-implicit-reference/fern/option" + context "context" +) + +func do() { + client := client.NewClient( + option.WithBaseURL( + "https://api.fern.com", + ), + nil, + ) + client.Nested.Api.GetSomething( + context.TODO(), + ) +} diff --git a/seed/go-sdk/inferred-auth-implicit-reference/dynamic-snippets/example4/snippet.go b/seed/go-sdk/inferred-auth-implicit-reference/dynamic-snippets/example4/snippet.go new file mode 100644 index 000000000000..f5a8ed0b696a --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/dynamic-snippets/example4/snippet.go @@ -0,0 +1,19 @@ +package example + +import ( + client "github.com/inferred-auth-implicit-reference/fern/client" + option "github.com/inferred-auth-implicit-reference/fern/option" + context "context" +) + +func do() { + client := client.NewClient( + option.WithBaseURL( + "https://api.fern.com", + ), + nil, + ) + client.Simple.GetSomething( + context.TODO(), + ) +} diff --git a/seed/go-sdk/inferred-auth-implicit-reference/error_codes.go b/seed/go-sdk/inferred-auth-implicit-reference/error_codes.go new file mode 100644 index 000000000000..b31aaa602312 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/error_codes.go @@ -0,0 +1,9 @@ +// Code generated by Fern. DO NOT EDIT. + +package inferredauthimplicit + +import ( + internal "github.com/inferred-auth-implicit-reference/fern/internal" +) + +var ErrorCodes internal.ErrorCodes = internal.ErrorCodes{} diff --git a/seed/go-sdk/inferred-auth-implicit-reference/file_param.go b/seed/go-sdk/inferred-auth-implicit-reference/file_param.go new file mode 100644 index 000000000000..c9976e8aaa09 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/file_param.go @@ -0,0 +1,41 @@ +package inferredauthimplicit + +import ( + "io" +) + +// FileParam is a file type suitable for multipart/form-data uploads. +type FileParam struct { + io.Reader + filename string + contentType string +} + +// FileParamOption adapts the behavior of the FileParam. No options are +// implemented yet, but this interface allows for future extensibility. +type FileParamOption interface { + apply() +} + +// NewFileParam returns a *FileParam type suitable for multipart/form-data uploads. All file +// upload endpoints accept a simple io.Reader, which is usually created by opening a file +// via os.Open. +// +// However, some endpoints require additional metadata about the file such as a specific +// Content-Type or custom filename. FileParam makes it easier to create the correct type +// signature for these endpoints. +func NewFileParam( + reader io.Reader, + filename string, + contentType string, + opts ...FileParamOption, +) *FileParam { + return &FileParam{ + Reader: reader, + filename: filename, + contentType: contentType, + } +} + +func (f *FileParam) Name() string { return f.filename } +func (f *FileParam) ContentType() string { return f.contentType } diff --git a/seed/go-sdk/inferred-auth-implicit-reference/go.mod b/seed/go-sdk/inferred-auth-implicit-reference/go.mod new file mode 100644 index 000000000000..f8dfa14e6b6e --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/go.mod @@ -0,0 +1,16 @@ +module github.com/inferred-auth-implicit-reference/fern + +go 1.21 + +toolchain go1.23.8 + +require github.com/google/uuid v1.6.0 + +require github.com/stretchr/testify v1.8.4 + +require gopkg.in/yaml.v3 v3.0.1 // indirect + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect +) diff --git a/seed/go-sdk/inferred-auth-implicit-reference/go.sum b/seed/go-sdk/inferred-auth-implicit-reference/go.sum new file mode 100644 index 000000000000..fcca6d128057 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/go.sum @@ -0,0 +1,12 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/seed/go-sdk/inferred-auth-implicit-reference/internal/caller.go b/seed/go-sdk/inferred-auth-implicit-reference/internal/caller.go new file mode 100644 index 000000000000..0c33aa2362e3 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/internal/caller.go @@ -0,0 +1,250 @@ +package internal + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "reflect" + "strings" + + "github.com/inferred-auth-implicit-reference/fern/core" +) + +const ( + // contentType specifies the JSON Content-Type header value. + contentType = "application/json" + contentTypeHeader = "Content-Type" +) + +// Caller calls APIs and deserializes their response, if any. +type Caller struct { + client core.HTTPClient + retrier *Retrier +} + +// CallerParams represents the parameters used to constrcut a new *Caller. +type CallerParams struct { + Client core.HTTPClient + MaxAttempts uint +} + +// NewCaller returns a new *Caller backed by the given parameters. +func NewCaller(params *CallerParams) *Caller { + var httpClient core.HTTPClient = http.DefaultClient + if params.Client != nil { + httpClient = params.Client + } + var retryOptions []RetryOption + if params.MaxAttempts > 0 { + retryOptions = append(retryOptions, WithMaxAttempts(params.MaxAttempts)) + } + return &Caller{ + client: httpClient, + retrier: NewRetrier(retryOptions...), + } +} + +// CallParams represents the parameters used to issue an API call. +type CallParams struct { + URL string + Method string + MaxAttempts uint + Headers http.Header + BodyProperties map[string]interface{} + QueryParameters url.Values + Client core.HTTPClient + Request interface{} + Response interface{} + ResponseIsOptional bool + ErrorDecoder ErrorDecoder +} + +// CallResponse is a parsed HTTP response from an API call. +type CallResponse struct { + StatusCode int + Header http.Header +} + +// Call issues an API call according to the given call parameters. +func (c *Caller) Call(ctx context.Context, params *CallParams) (*CallResponse, error) { + url := buildURL(params.URL, params.QueryParameters) + req, err := newRequest( + ctx, + url, + params.Method, + params.Headers, + params.Request, + params.BodyProperties, + ) + if err != nil { + return nil, err + } + + // If the call has been cancelled, don't issue the request. + if err := ctx.Err(); err != nil { + return nil, err + } + + client := c.client + if params.Client != nil { + // Use the HTTP client scoped to the request. + client = params.Client + } + + var retryOptions []RetryOption + if params.MaxAttempts > 0 { + retryOptions = append(retryOptions, WithMaxAttempts(params.MaxAttempts)) + } + + resp, err := c.retrier.Run( + client.Do, + req, + params.ErrorDecoder, + retryOptions..., + ) + if err != nil { + return nil, err + } + + // Close the response body after we're done. + defer resp.Body.Close() + + // Check if the call was cancelled before we return the error + // associated with the call and/or unmarshal the response data. + if err := ctx.Err(); err != nil { + return nil, err + } + + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + return nil, decodeError(resp, params.ErrorDecoder) + } + + // Mutate the response parameter in-place. + if params.Response != nil { + if writer, ok := params.Response.(io.Writer); ok { + _, err = io.Copy(writer, resp.Body) + } else { + err = json.NewDecoder(resp.Body).Decode(params.Response) + } + if err != nil { + if err == io.EOF { + if params.ResponseIsOptional { + // The response is optional, so we should ignore the + // io.EOF error + return &CallResponse{ + StatusCode: resp.StatusCode, + Header: resp.Header, + }, nil + } + return nil, fmt.Errorf("expected a %T response, but the server responded with nothing", params.Response) + } + return nil, err + } + } + + return &CallResponse{ + StatusCode: resp.StatusCode, + Header: resp.Header, + }, nil +} + +// buildURL constructs the final URL by appending the given query parameters (if any). +func buildURL( + url string, + queryParameters url.Values, +) string { + if len(queryParameters) == 0 { + return url + } + if strings.ContainsRune(url, '?') { + url += "&" + } else { + url += "?" + } + url += queryParameters.Encode() + return url +} + +// newRequest returns a new *http.Request with all of the fields +// required to issue the call. +func newRequest( + ctx context.Context, + url string, + method string, + endpointHeaders http.Header, + request interface{}, + bodyProperties map[string]interface{}, +) (*http.Request, error) { + requestBody, err := newRequestBody(request, bodyProperties) + if err != nil { + return nil, err + } + req, err := http.NewRequestWithContext(ctx, method, url, requestBody) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + req.Header.Set(contentTypeHeader, contentType) + for name, values := range endpointHeaders { + req.Header[name] = values + } + return req, nil +} + +// newRequestBody returns a new io.Reader that represents the HTTP request body. +func newRequestBody(request interface{}, bodyProperties map[string]interface{}) (io.Reader, error) { + if isNil(request) { + if len(bodyProperties) == 0 { + return nil, nil + } + requestBytes, err := json.Marshal(bodyProperties) + if err != nil { + return nil, err + } + return bytes.NewReader(requestBytes), nil + } + if body, ok := request.(io.Reader); ok { + return body, nil + } + requestBytes, err := MarshalJSONWithExtraProperties(request, bodyProperties) + if err != nil { + return nil, err + } + return bytes.NewReader(requestBytes), nil +} + +// decodeError decodes the error from the given HTTP response. Note that +// it's the caller's responsibility to close the response body. +func decodeError(response *http.Response, errorDecoder ErrorDecoder) error { + if errorDecoder != nil { + // This endpoint has custom errors, so we'll + // attempt to unmarshal the error into a structured + // type based on the status code. + return errorDecoder(response.StatusCode, response.Header, response.Body) + } + // This endpoint doesn't have any custom error + // types, so we just read the body as-is, and + // put it into a normal error. + bytes, err := io.ReadAll(response.Body) + if err != nil && err != io.EOF { + return err + } + if err == io.EOF { + // The error didn't have a response body, + // so all we can do is return an error + // with the status code. + return core.NewAPIError(response.StatusCode, response.Header, nil) + } + return core.NewAPIError(response.StatusCode, response.Header, errors.New(string(bytes))) +} + +// isNil is used to determine if the request value is equal to nil (i.e. an interface +// value that holds a nil concrete value is itself non-nil). +func isNil(value interface{}) bool { + return value == nil || reflect.ValueOf(value).IsNil() +} diff --git a/seed/go-sdk/inferred-auth-implicit-reference/internal/caller_test.go b/seed/go-sdk/inferred-auth-implicit-reference/internal/caller_test.go new file mode 100644 index 000000000000..425299d61f1b --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/internal/caller_test.go @@ -0,0 +1,395 @@ +package internal + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "net/http/httptest" + "net/url" + "strconv" + "testing" + + "github.com/inferred-auth-implicit-reference/fern/core" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// InternalTestCase represents a single test case. +type InternalTestCase struct { + description string + + // Server-side assertions. + givePathSuffix string + giveMethod string + giveResponseIsOptional bool + giveHeader http.Header + giveErrorDecoder ErrorDecoder + giveRequest *InternalTestRequest + giveQueryParams url.Values + giveBodyProperties map[string]interface{} + + // Client-side assertions. + wantResponse *InternalTestResponse + wantHeaders http.Header + wantError error +} + +// InternalTestRequest a simple request body. +type InternalTestRequest struct { + Id string `json:"id"` +} + +// InternalTestResponse a simple response body. +type InternalTestResponse struct { + Id string `json:"id"` + ExtraBodyProperties map[string]interface{} `json:"extraBodyProperties,omitempty"` + QueryParameters url.Values `json:"queryParameters,omitempty"` +} + +// InternalTestNotFoundError represents a 404. +type InternalTestNotFoundError struct { + *core.APIError + + Message string `json:"message"` +} + +func TestCall(t *testing.T) { + tests := []*InternalTestCase{ + { + description: "GET success", + giveMethod: http.MethodGet, + giveHeader: http.Header{ + "X-API-Status": []string{"success"}, + }, + giveRequest: &InternalTestRequest{ + Id: "123", + }, + wantResponse: &InternalTestResponse{ + Id: "123", + }, + }, + { + description: "GET success with query", + givePathSuffix: "?limit=1", + giveMethod: http.MethodGet, + giveHeader: http.Header{ + "X-API-Status": []string{"success"}, + }, + giveRequest: &InternalTestRequest{ + Id: "123", + }, + wantResponse: &InternalTestResponse{ + Id: "123", + QueryParameters: url.Values{ + "limit": []string{"1"}, + }, + }, + }, + { + description: "GET not found", + giveMethod: http.MethodGet, + giveHeader: http.Header{ + "X-API-Status": []string{"fail"}, + }, + giveRequest: &InternalTestRequest{ + Id: strconv.Itoa(http.StatusNotFound), + }, + giveErrorDecoder: newTestErrorDecoder(t), + wantError: &InternalTestNotFoundError{ + APIError: core.NewAPIError( + http.StatusNotFound, + http.Header{}, + errors.New(`{"message":"ID \"404\" not found"}`), + ), + }, + }, + { + description: "POST empty body", + giveMethod: http.MethodPost, + giveHeader: http.Header{ + "X-API-Status": []string{"fail"}, + }, + giveRequest: nil, + wantError: core.NewAPIError( + http.StatusBadRequest, + http.Header{}, + errors.New("invalid request"), + ), + }, + { + description: "POST optional response", + giveMethod: http.MethodPost, + giveHeader: http.Header{ + "X-API-Status": []string{"success"}, + }, + giveRequest: &InternalTestRequest{ + Id: "123", + }, + giveResponseIsOptional: true, + }, + { + description: "POST API error", + giveMethod: http.MethodPost, + giveHeader: http.Header{ + "X-API-Status": []string{"fail"}, + }, + giveRequest: &InternalTestRequest{ + Id: strconv.Itoa(http.StatusInternalServerError), + }, + wantError: core.NewAPIError( + http.StatusInternalServerError, + http.Header{}, + errors.New("failed to process request"), + ), + }, + { + description: "POST extra properties", + giveMethod: http.MethodPost, + giveHeader: http.Header{ + "X-API-Status": []string{"success"}, + }, + giveRequest: new(InternalTestRequest), + giveBodyProperties: map[string]interface{}{ + "key": "value", + }, + wantResponse: &InternalTestResponse{ + ExtraBodyProperties: map[string]interface{}{ + "key": "value", + }, + }, + }, + { + description: "GET extra query parameters", + giveMethod: http.MethodGet, + giveHeader: http.Header{ + "X-API-Status": []string{"success"}, + }, + giveQueryParams: url.Values{ + "extra": []string{"true"}, + }, + giveRequest: &InternalTestRequest{ + Id: "123", + }, + wantResponse: &InternalTestResponse{ + Id: "123", + QueryParameters: url.Values{ + "extra": []string{"true"}, + }, + }, + }, + { + description: "GET merge extra query parameters", + givePathSuffix: "?limit=1", + giveMethod: http.MethodGet, + giveHeader: http.Header{ + "X-API-Status": []string{"success"}, + }, + giveRequest: &InternalTestRequest{ + Id: "123", + }, + giveQueryParams: url.Values{ + "extra": []string{"true"}, + }, + wantResponse: &InternalTestResponse{ + Id: "123", + QueryParameters: url.Values{ + "limit": []string{"1"}, + "extra": []string{"true"}, + }, + }, + }, + } + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + var ( + server = newTestServer(t, test) + client = server.Client() + ) + caller := NewCaller( + &CallerParams{ + Client: client, + }, + ) + var response *InternalTestResponse + _, err := caller.Call( + context.Background(), + &CallParams{ + URL: server.URL + test.givePathSuffix, + Method: test.giveMethod, + Headers: test.giveHeader, + BodyProperties: test.giveBodyProperties, + QueryParameters: test.giveQueryParams, + Request: test.giveRequest, + Response: &response, + ResponseIsOptional: test.giveResponseIsOptional, + ErrorDecoder: test.giveErrorDecoder, + }, + ) + if test.wantError != nil { + assert.EqualError(t, err, test.wantError.Error()) + return + } + require.NoError(t, err) + assert.Equal(t, test.wantResponse, response) + }) + } +} + +func TestMergeHeaders(t *testing.T) { + t.Run("both empty", func(t *testing.T) { + merged := MergeHeaders(make(http.Header), make(http.Header)) + assert.Empty(t, merged) + }) + + t.Run("empty left", func(t *testing.T) { + left := make(http.Header) + + right := make(http.Header) + right.Set("X-API-Version", "0.0.1") + + merged := MergeHeaders(left, right) + assert.Equal(t, "0.0.1", merged.Get("X-API-Version")) + }) + + t.Run("empty right", func(t *testing.T) { + left := make(http.Header) + left.Set("X-API-Version", "0.0.1") + + right := make(http.Header) + + merged := MergeHeaders(left, right) + assert.Equal(t, "0.0.1", merged.Get("X-API-Version")) + }) + + t.Run("single value override", func(t *testing.T) { + left := make(http.Header) + left.Set("X-API-Version", "0.0.0") + + right := make(http.Header) + right.Set("X-API-Version", "0.0.1") + + merged := MergeHeaders(left, right) + assert.Equal(t, []string{"0.0.1"}, merged.Values("X-API-Version")) + }) + + t.Run("multiple value override", func(t *testing.T) { + left := make(http.Header) + left.Set("X-API-Versions", "0.0.0") + + right := make(http.Header) + right.Add("X-API-Versions", "0.0.1") + right.Add("X-API-Versions", "0.0.2") + + merged := MergeHeaders(left, right) + assert.Equal(t, []string{"0.0.1", "0.0.2"}, merged.Values("X-API-Versions")) + }) + + t.Run("disjoint merge", func(t *testing.T) { + left := make(http.Header) + left.Set("X-API-Tenancy", "test") + + right := make(http.Header) + right.Set("X-API-Version", "0.0.1") + + merged := MergeHeaders(left, right) + assert.Equal(t, []string{"test"}, merged.Values("X-API-Tenancy")) + assert.Equal(t, []string{"0.0.1"}, merged.Values("X-API-Version")) + }) +} + +// newTestServer returns a new *httptest.Server configured with the +// given test parameters. +func newTestServer(t *testing.T, tc *InternalTestCase) *httptest.Server { + return httptest.NewServer( + http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, tc.giveMethod, r.Method) + assert.Equal(t, contentType, r.Header.Get(contentTypeHeader)) + for header, value := range tc.giveHeader { + assert.Equal(t, value, r.Header.Values(header)) + } + + request := new(InternalTestRequest) + + bytes, err := io.ReadAll(r.Body) + if tc.giveRequest == nil { + require.Empty(t, bytes) + w.WriteHeader(http.StatusBadRequest) + _, err = w.Write([]byte("invalid request")) + require.NoError(t, err) + return + } + require.NoError(t, err) + require.NoError(t, json.Unmarshal(bytes, request)) + + switch request.Id { + case strconv.Itoa(http.StatusNotFound): + notFoundError := &InternalTestNotFoundError{ + APIError: &core.APIError{ + StatusCode: http.StatusNotFound, + }, + Message: fmt.Sprintf("ID %q not found", request.Id), + } + bytes, err = json.Marshal(notFoundError) + require.NoError(t, err) + + w.WriteHeader(http.StatusNotFound) + _, err = w.Write(bytes) + require.NoError(t, err) + return + + case strconv.Itoa(http.StatusInternalServerError): + w.WriteHeader(http.StatusInternalServerError) + _, err = w.Write([]byte("failed to process request")) + require.NoError(t, err) + return + } + + if tc.giveResponseIsOptional { + w.WriteHeader(http.StatusOK) + return + } + + extraBodyProperties := make(map[string]interface{}) + require.NoError(t, json.Unmarshal(bytes, &extraBodyProperties)) + delete(extraBodyProperties, "id") + + response := &InternalTestResponse{ + Id: request.Id, + ExtraBodyProperties: extraBodyProperties, + QueryParameters: r.URL.Query(), + } + bytes, err = json.Marshal(response) + require.NoError(t, err) + + _, err = w.Write(bytes) + require.NoError(t, err) + }, + ), + ) +} + +// newTestErrorDecoder returns an error decoder suitable for tests. +func newTestErrorDecoder(t *testing.T) func(int, http.Header, io.Reader) error { + return func(statusCode int, header http.Header, body io.Reader) error { + raw, err := io.ReadAll(body) + require.NoError(t, err) + + var ( + apiError = core.NewAPIError(statusCode, header, errors.New(string(raw))) + decoder = json.NewDecoder(bytes.NewReader(raw)) + ) + if statusCode == http.StatusNotFound { + value := new(InternalTestNotFoundError) + value.APIError = apiError + require.NoError(t, decoder.Decode(value)) + + return value + } + return apiError + } +} diff --git a/seed/go-sdk/inferred-auth-implicit-reference/internal/error_decoder.go b/seed/go-sdk/inferred-auth-implicit-reference/internal/error_decoder.go new file mode 100644 index 000000000000..b88b29be9afc --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/internal/error_decoder.go @@ -0,0 +1,64 @@ +package internal + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + + "github.com/inferred-auth-implicit-reference/fern/core" +) + +// ErrorCodes maps HTTP status codes to error constructors. +type ErrorCodes map[int]func(*core.APIError) error + +// ErrorDecoder decodes *http.Response errors and returns a +// typed API error (e.g. *core.APIError). +type ErrorDecoder func(statusCode int, header http.Header, body io.Reader) error + +// NewErrorDecoder returns a new ErrorDecoder backed by the given error codes. +// errorCodesOverrides is optional and will be merged with the default error codes, +// with overrides taking precedence. +func NewErrorDecoder(errorCodes ErrorCodes, errorCodesOverrides ...ErrorCodes) ErrorDecoder { + // Merge default error codes with overrides + mergedErrorCodes := make(ErrorCodes) + + // Start with default error codes + for statusCode, errorFunc := range errorCodes { + mergedErrorCodes[statusCode] = errorFunc + } + + // Apply overrides if provided + if len(errorCodesOverrides) > 0 && errorCodesOverrides[0] != nil { + for statusCode, errorFunc := range errorCodesOverrides[0] { + mergedErrorCodes[statusCode] = errorFunc + } + } + + return func(statusCode int, header http.Header, body io.Reader) error { + raw, err := io.ReadAll(body) + if err != nil { + return fmt.Errorf("failed to read error from response body: %w", err) + } + apiError := core.NewAPIError( + statusCode, + header, + errors.New(string(raw)), + ) + newErrorFunc, ok := mergedErrorCodes[statusCode] + if !ok { + // This status code isn't recognized, so we return + // the API error as-is. + return apiError + } + customError := newErrorFunc(apiError) + if err := json.NewDecoder(bytes.NewReader(raw)).Decode(customError); err != nil { + // If we fail to decode the error, we return the + // API error as-is. + return apiError + } + return customError + } +} diff --git a/seed/go-sdk/inferred-auth-implicit-reference/internal/error_decoder_test.go b/seed/go-sdk/inferred-auth-implicit-reference/internal/error_decoder_test.go new file mode 100644 index 000000000000..b5494fd862e4 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/internal/error_decoder_test.go @@ -0,0 +1,59 @@ +package internal + +import ( + "bytes" + "errors" + "net/http" + "testing" + + "github.com/inferred-auth-implicit-reference/fern/core" + "github.com/stretchr/testify/assert" +) + +func TestErrorDecoder(t *testing.T) { + decoder := NewErrorDecoder( + ErrorCodes{ + http.StatusNotFound: func(apiError *core.APIError) error { + return &InternalTestNotFoundError{APIError: apiError} + }, + }) + + tests := []struct { + description string + giveStatusCode int + giveHeader http.Header + giveBody string + wantError error + }{ + { + description: "unrecognized status code", + giveStatusCode: http.StatusInternalServerError, + giveHeader: http.Header{}, + giveBody: "Internal Server Error", + wantError: core.NewAPIError(http.StatusInternalServerError, http.Header{}, errors.New("Internal Server Error")), + }, + { + description: "not found with valid JSON", + giveStatusCode: http.StatusNotFound, + giveHeader: http.Header{}, + giveBody: `{"message": "Resource not found"}`, + wantError: &InternalTestNotFoundError{ + APIError: core.NewAPIError(http.StatusNotFound, http.Header{}, errors.New(`{"message": "Resource not found"}`)), + Message: "Resource not found", + }, + }, + { + description: "not found with invalid JSON", + giveStatusCode: http.StatusNotFound, + giveHeader: http.Header{}, + giveBody: `Resource not found`, + wantError: core.NewAPIError(http.StatusNotFound, http.Header{}, errors.New("Resource not found")), + }, + } + + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + assert.Equal(t, tt.wantError, decoder(tt.giveStatusCode, tt.giveHeader, bytes.NewReader([]byte(tt.giveBody)))) + }) + } +} diff --git a/seed/go-sdk/inferred-auth-implicit-reference/internal/explicit_fields.go b/seed/go-sdk/inferred-auth-implicit-reference/internal/explicit_fields.go new file mode 100644 index 000000000000..4bdf34fc2b7c --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/internal/explicit_fields.go @@ -0,0 +1,116 @@ +package internal + +import ( + "math/big" + "reflect" + "strings" +) + +// HandleExplicitFields processes a struct to remove `omitempty` from +// fields that have been explicitly set (as indicated by their corresponding bit in explicitFields). +// Note that `marshaler` should be an embedded struct to avoid infinite recursion. +// Returns an interface{} that can be passed to json.Marshal. +func HandleExplicitFields(marshaler interface{}, explicitFields *big.Int) interface{} { + val := reflect.ValueOf(marshaler) + typ := reflect.TypeOf(marshaler) + + // Handle pointer types + if val.Kind() == reflect.Ptr { + if val.IsNil() { + return nil + } + val = val.Elem() + typ = typ.Elem() + } + + // Only handle struct types + if val.Kind() != reflect.Struct { + return marshaler + } + + // Handle embedded struct pattern + var sourceVal reflect.Value + var sourceType reflect.Type + + // Check if this is an embedded struct pattern + if typ.NumField() == 1 && typ.Field(0).Anonymous { + // This is likely an embedded struct, get the embedded value + embeddedField := val.Field(0) + sourceVal = embeddedField + sourceType = embeddedField.Type() + } else { + // Regular struct + sourceVal = val + sourceType = typ + } + + // If no explicit fields set, use standard marshaling + if explicitFields == nil || explicitFields.Sign() == 0 { + return marshaler + } + + // Create a new struct type with modified tags + fields := make([]reflect.StructField, 0, sourceType.NumField()) + + for i := 0; i < sourceType.NumField(); i++ { + field := sourceType.Field(i) + + // Skip unexported fields and the explicitFields field itself + if !field.IsExported() || field.Name == "explicitFields" { + continue + } + + // Check if this field has been explicitly set + fieldBit := big.NewInt(1) + fieldBit.Lsh(fieldBit, uint(i)) + if big.NewInt(0).And(explicitFields, fieldBit).Sign() != 0 { + // Remove omitempty from the json tag + tag := field.Tag.Get("json") + if tag != "" && tag != "-" { + // Parse the json tag, remove omitempty from options + parts := strings.Split(tag, ",") + if len(parts) > 1 { + var newParts []string + newParts = append(newParts, parts[0]) // Keep the field name + for _, part := range parts[1:] { + if strings.TrimSpace(part) != "omitempty" { + newParts = append(newParts, part) + } + } + tag = strings.Join(newParts, ",") + } + + // Reconstruct the struct tag + newTag := `json:"` + tag + `"` + if urlTag := field.Tag.Get("url"); urlTag != "" { + newTag += ` url:"` + urlTag + `"` + } + + field.Tag = reflect.StructTag(newTag) + } + } + + fields = append(fields, field) + } + + // Create new struct type with modified tags + newType := reflect.StructOf(fields) + newVal := reflect.New(newType).Elem() + + // Copy field values from original struct to new struct + fieldIndex := 0 + for i := 0; i < sourceType.NumField(); i++ { + originalField := sourceType.Field(i) + + // Skip unexported fields and the explicitFields field itself + if !originalField.IsExported() || originalField.Name == "explicitFields" { + continue + } + + originalValue := sourceVal.Field(i) + newVal.Field(fieldIndex).Set(originalValue) + fieldIndex++ + } + + return newVal.Interface() +} diff --git a/seed/go-sdk/inferred-auth-implicit-reference/internal/explicit_fields_test.go b/seed/go-sdk/inferred-auth-implicit-reference/internal/explicit_fields_test.go new file mode 100644 index 000000000000..3d05e88a2ce9 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/internal/explicit_fields_test.go @@ -0,0 +1,497 @@ +package internal + +import ( + "encoding/json" + "math/big" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type testExplicitFieldsStruct struct { + Name *string `json:"name,omitempty"` + Code *string `json:"code,omitempty"` + Count *int `json:"count,omitempty"` + Enabled *bool `json:"enabled,omitempty"` + Tags []string `json:"tags,omitempty"` + //lint:ignore unused this field is intentionally unused for testing + unexported string `json:"-"` + explicitFields *big.Int `json:"-"` +} + +var ( + testFieldName = big.NewInt(1 << 0) + testFieldCode = big.NewInt(1 << 1) + testFieldCount = big.NewInt(1 << 2) + testFieldEnabled = big.NewInt(1 << 3) + testFieldTags = big.NewInt(1 << 4) +) + +func (t *testExplicitFieldsStruct) require(field *big.Int) { + if t.explicitFields == nil { + t.explicitFields = big.NewInt(0) + } + t.explicitFields.Or(t.explicitFields, field) +} + +func (t *testExplicitFieldsStruct) SetName(name *string) { + t.Name = name + t.require(testFieldName) +} + +func (t *testExplicitFieldsStruct) SetCode(code *string) { + t.Code = code + t.require(testFieldCode) +} + +func (t *testExplicitFieldsStruct) SetCount(count *int) { + t.Count = count + t.require(testFieldCount) +} + +func (t *testExplicitFieldsStruct) SetEnabled(enabled *bool) { + t.Enabled = enabled + t.require(testFieldEnabled) +} + +func (t *testExplicitFieldsStruct) SetTags(tags []string) { + t.Tags = tags + t.require(testFieldTags) +} + +func (t *testExplicitFieldsStruct) MarshalJSON() ([]byte, error) { + type embed testExplicitFieldsStruct + var marshaler = struct { + embed + }{ + embed: embed(*t), + } + return json.Marshal(HandleExplicitFields(marshaler, t.explicitFields)) +} + +type testStructWithoutExplicitFields struct { + Name *string `json:"name,omitempty"` + Code *string `json:"code,omitempty"` +} + +func TestHandleExplicitFields(t *testing.T) { + tests := []struct { + desc string + giveInput interface{} + wantBytes []byte + wantError string + }{ + { + desc: "nil input", + giveInput: nil, + wantBytes: []byte(`null`), + }, + { + desc: "non-struct input", + giveInput: "string", + wantBytes: []byte(`"string"`), + }, + { + desc: "slice input", + giveInput: []string{"a", "b"}, + wantBytes: []byte(`["a","b"]`), + }, + { + desc: "map input", + giveInput: map[string]interface{}{"key": "value"}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "struct without explicitFields field", + giveInput: &testStructWithoutExplicitFields{ + Name: stringPtr("test"), + Code: nil, + }, + wantBytes: []byte(`{"name":"test"}`), + }, + { + desc: "struct with no explicit fields set", + giveInput: &testExplicitFieldsStruct{ + Name: stringPtr("test"), + Code: nil, + }, + wantBytes: []byte(`{"name":"test"}`), + }, + { + desc: "struct with explicit nil field", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{ + Name: stringPtr("test"), + } + s.SetCode(nil) + return s + }(), + wantBytes: []byte(`{"name":"test","code":null}`), + }, + { + desc: "struct with explicit non-nil field", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{} + s.SetName(stringPtr("explicit")) + s.SetCode(stringPtr("also-explicit")) + return s + }(), + wantBytes: []byte(`{"name":"explicit","code":"also-explicit"}`), + }, + { + desc: "struct with mixed explicit and implicit fields", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{ + Name: stringPtr("implicit"), + Count: intPtr(42), + } + s.SetCode(nil) // explicit nil + return s + }(), + wantBytes: []byte(`{"name":"implicit","code":null,"count":42}`), + }, + { + desc: "struct with multiple explicit nil fields", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{ + Name: stringPtr("test"), + } + s.SetCode(nil) + s.SetCount(nil) + return s + }(), + wantBytes: []byte(`{"name":"test","code":null,"count":null}`), + }, + { + desc: "struct with slice field", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{ + Tags: []string{"tag1", "tag2"}, + } + s.SetTags(nil) // explicit nil slice + return s + }(), + wantBytes: []byte(`{"tags":null}`), + }, + { + desc: "struct with boolean field", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{} + s.SetEnabled(boolPtr(false)) // explicit false + return s + }(), + wantBytes: []byte(`{"enabled":false}`), + }, + { + desc: "struct with all fields explicit", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{} + s.SetName(stringPtr("test")) + s.SetCode(nil) + s.SetCount(intPtr(0)) + s.SetEnabled(boolPtr(false)) + s.SetTags([]string{}) + return s + }(), + wantBytes: []byte(`{"name":"test","code":null,"count":0,"enabled":false,"tags":[]}`), + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + var explicitFields *big.Int + if s, ok := tt.giveInput.(*testExplicitFieldsStruct); ok { + explicitFields = s.explicitFields + } + bytes, err := json.Marshal(HandleExplicitFields(tt.giveInput, explicitFields)) + if tt.wantError != "" { + require.EqualError(t, err, tt.wantError) + assert.Nil(t, tt.wantBytes) + return + } + require.NoError(t, err) + assert.JSONEq(t, string(tt.wantBytes), string(bytes)) + + // Verify it's valid JSON + var value interface{} + require.NoError(t, json.Unmarshal(bytes, &value)) + }) + } +} + +func TestHandleExplicitFieldsCustomMarshaler(t *testing.T) { + t.Run("custom marshaler with explicit fields", func(t *testing.T) { + s := &testExplicitFieldsStruct{} + s.SetName(nil) + s.SetCode(stringPtr("test-code")) + + bytes, err := s.MarshalJSON() + require.NoError(t, err) + assert.JSONEq(t, `{"name":null,"code":"test-code"}`, string(bytes)) + }) + + t.Run("custom marshaler with no explicit fields", func(t *testing.T) { + s := &testExplicitFieldsStruct{ + Name: stringPtr("implicit"), + Code: stringPtr("also-implicit"), + } + + bytes, err := s.MarshalJSON() + require.NoError(t, err) + assert.JSONEq(t, `{"name":"implicit","code":"also-implicit"}`, string(bytes)) + }) +} + +func TestHandleExplicitFieldsPointerHandling(t *testing.T) { + t.Run("nil pointer", func(t *testing.T) { + var s *testExplicitFieldsStruct + bytes, err := json.Marshal(HandleExplicitFields(s, nil)) + require.NoError(t, err) + assert.Equal(t, []byte(`null`), bytes) + }) + + t.Run("pointer to struct", func(t *testing.T) { + s := &testExplicitFieldsStruct{} + s.SetName(nil) + + bytes, err := json.Marshal(HandleExplicitFields(s, s.explicitFields)) + require.NoError(t, err) + assert.JSONEq(t, `{"name":null}`, string(bytes)) + }) +} + +func TestHandleExplicitFieldsEmbeddedStruct(t *testing.T) { + t.Run("embedded struct with explicit fields", func(t *testing.T) { + // Create a struct similar to what MarshalJSON creates + s := &testExplicitFieldsStruct{} + s.SetName(nil) + s.SetCode(stringPtr("test-code")) + + type embed testExplicitFieldsStruct + var marshaler = struct { + embed + }{ + embed: embed(*s), + } + + bytes, err := json.Marshal(HandleExplicitFields(marshaler, s.explicitFields)) + require.NoError(t, err) + // Should include both explicit fields (name as null, code as "test-code") + assert.JSONEq(t, `{"name":null,"code":"test-code"}`, string(bytes)) + }) + + t.Run("embedded struct with no explicit fields", func(t *testing.T) { + s := &testExplicitFieldsStruct{ + Name: stringPtr("implicit"), + Code: stringPtr("also-implicit"), + } + + type embed testExplicitFieldsStruct + var marshaler = struct { + embed + }{ + embed: embed(*s), + } + + bytes, err := json.Marshal(HandleExplicitFields(marshaler, s.explicitFields)) + require.NoError(t, err) + // Should only include non-nil fields (omitempty behavior) + assert.JSONEq(t, `{"name":"implicit","code":"also-implicit"}`, string(bytes)) + }) + + t.Run("embedded struct with mixed fields", func(t *testing.T) { + s := &testExplicitFieldsStruct{ + Count: intPtr(42), // implicit field + } + s.SetName(nil) // explicit nil + s.SetCode(stringPtr("explicit")) // explicit value + + type embed testExplicitFieldsStruct + var marshaler = struct { + embed + }{ + embed: embed(*s), + } + + bytes, err := json.Marshal(HandleExplicitFields(marshaler, s.explicitFields)) + require.NoError(t, err) + // Should include explicit null, explicit value, and implicit value + assert.JSONEq(t, `{"name":null,"code":"explicit","count":42}`, string(bytes)) + }) +} + +func TestHandleExplicitFieldsTagHandling(t *testing.T) { + type testStructWithComplexTags struct { + Field1 *string `json:"field1,omitempty" url:"field1,omitempty"` + Field2 *string `json:"field2,omitempty,string" url:"field2"` + Field3 *string `json:"-"` + Field4 *string `json:"field4"` + explicitFields *big.Int `json:"-"` + } + + s := &testStructWithComplexTags{ + Field1: stringPtr("test1"), + Field4: stringPtr("test4"), + explicitFields: big.NewInt(1), // Only first field is explicit + } + + bytes, err := json.Marshal(HandleExplicitFields(s, s.explicitFields)) + require.NoError(t, err) + + // Field1 should have omitempty removed, Field2 should keep omitempty, Field4 should be included + assert.JSONEq(t, `{"field1":"test1","field4":"test4"}`, string(bytes)) +} + +// Test types for nested struct explicit fields testing +type testNestedStruct struct { + NestedName *string `json:"nested_name,omitempty"` + NestedCode *string `json:"nested_code,omitempty"` + explicitFields *big.Int `json:"-"` +} + +type testParentStruct struct { + ParentName *string `json:"parent_name,omitempty"` + Nested *testNestedStruct `json:"nested,omitempty"` + explicitFields *big.Int `json:"-"` +} + +var ( + nestedFieldName = big.NewInt(1 << 0) + nestedFieldCode = big.NewInt(1 << 1) +) + +var ( + parentFieldName = big.NewInt(1 << 0) + parentFieldNested = big.NewInt(1 << 1) +) + +func (n *testNestedStruct) require(field *big.Int) { + if n.explicitFields == nil { + n.explicitFields = big.NewInt(0) + } + n.explicitFields.Or(n.explicitFields, field) +} + +func (n *testNestedStruct) SetNestedName(name *string) { + n.NestedName = name + n.require(nestedFieldName) +} + +func (n *testNestedStruct) SetNestedCode(code *string) { + n.NestedCode = code + n.require(nestedFieldCode) +} + +func (n *testNestedStruct) MarshalJSON() ([]byte, error) { + type embed testNestedStruct + var marshaler = struct { + embed + }{ + embed: embed(*n), + } + return json.Marshal(HandleExplicitFields(marshaler, n.explicitFields)) +} + +func (p *testParentStruct) require(field *big.Int) { + if p.explicitFields == nil { + p.explicitFields = big.NewInt(0) + } + p.explicitFields.Or(p.explicitFields, field) +} + +func (p *testParentStruct) SetParentName(name *string) { + p.ParentName = name + p.require(parentFieldName) +} + +func (p *testParentStruct) SetNested(nested *testNestedStruct) { + p.Nested = nested + p.require(parentFieldNested) +} + +func (p *testParentStruct) MarshalJSON() ([]byte, error) { + type embed testParentStruct + var marshaler = struct { + embed + }{ + embed: embed(*p), + } + return json.Marshal(HandleExplicitFields(marshaler, p.explicitFields)) +} + +func TestHandleExplicitFieldsNestedStruct(t *testing.T) { + tests := []struct { + desc string + setupFunc func() *testParentStruct + wantBytes []byte + }{ + { + desc: "nested struct with explicit nil in nested object", + setupFunc: func() *testParentStruct { + nested := &testNestedStruct{ + NestedName: stringPtr("implicit-nested"), + } + nested.SetNestedCode(nil) // explicit nil + + return &testParentStruct{ + ParentName: stringPtr("implicit-parent"), + Nested: nested, + } + }, + wantBytes: []byte(`{"parent_name":"implicit-parent","nested":{"nested_name":"implicit-nested","nested_code":null}}`), + }, + { + desc: "parent with explicit nil nested struct", + setupFunc: func() *testParentStruct { + parent := &testParentStruct{ + ParentName: stringPtr("implicit-parent"), + } + parent.SetNested(nil) // explicit nil nested struct + return parent + }, + wantBytes: []byte(`{"parent_name":"implicit-parent","nested":null}`), + }, + { + desc: "all explicit fields in nested structure", + setupFunc: func() *testParentStruct { + nested := &testNestedStruct{} + nested.SetNestedName(stringPtr("explicit-nested")) + nested.SetNestedCode(nil) // explicit nil + + parent := &testParentStruct{} + parent.SetParentName(nil) // explicit nil + parent.SetNested(nested) // explicit nested struct + + return parent + }, + wantBytes: []byte(`{"parent_name":null,"nested":{"nested_name":"explicit-nested","nested_code":null}}`), + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + parent := tt.setupFunc() + bytes, err := parent.MarshalJSON() + require.NoError(t, err) + assert.JSONEq(t, string(tt.wantBytes), string(bytes)) + + // Verify it's valid JSON + var value interface{} + require.NoError(t, json.Unmarshal(bytes, &value)) + }) + } +} + +// Helper functions +func stringPtr(s string) *string { + return &s +} + +func intPtr(i int) *int { + return &i +} + +func boolPtr(b bool) *bool { + return &b +} diff --git a/seed/go-sdk/inferred-auth-implicit-reference/internal/extra_properties.go b/seed/go-sdk/inferred-auth-implicit-reference/internal/extra_properties.go new file mode 100644 index 000000000000..540c3fd89eeb --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/internal/extra_properties.go @@ -0,0 +1,141 @@ +package internal + +import ( + "bytes" + "encoding/json" + "fmt" + "reflect" + "strings" +) + +// MarshalJSONWithExtraProperty marshals the given value to JSON, including the extra property. +func MarshalJSONWithExtraProperty(marshaler interface{}, key string, value interface{}) ([]byte, error) { + return MarshalJSONWithExtraProperties(marshaler, map[string]interface{}{key: value}) +} + +// MarshalJSONWithExtraProperties marshals the given value to JSON, including any extra properties. +func MarshalJSONWithExtraProperties(marshaler interface{}, extraProperties map[string]interface{}) ([]byte, error) { + bytes, err := json.Marshal(marshaler) + if err != nil { + return nil, err + } + if len(extraProperties) == 0 { + return bytes, nil + } + keys, err := getKeys(marshaler) + if err != nil { + return nil, err + } + for _, key := range keys { + if _, ok := extraProperties[key]; ok { + return nil, fmt.Errorf("cannot add extra property %q because it is already defined on the type", key) + } + } + extraBytes, err := json.Marshal(extraProperties) + if err != nil { + return nil, err + } + if isEmptyJSON(bytes) { + if isEmptyJSON(extraBytes) { + return bytes, nil + } + return extraBytes, nil + } + result := bytes[:len(bytes)-1] + result = append(result, ',') + result = append(result, extraBytes[1:len(extraBytes)-1]...) + result = append(result, '}') + return result, nil +} + +// ExtractExtraProperties extracts any extra properties from the given value. +func ExtractExtraProperties(bytes []byte, value interface{}, exclude ...string) (map[string]interface{}, error) { + val := reflect.ValueOf(value) + for val.Kind() == reflect.Ptr { + if val.IsNil() { + return nil, fmt.Errorf("value must be non-nil to extract extra properties") + } + val = val.Elem() + } + if err := json.Unmarshal(bytes, &value); err != nil { + return nil, err + } + var extraProperties map[string]interface{} + if err := json.Unmarshal(bytes, &extraProperties); err != nil { + return nil, err + } + for i := 0; i < val.Type().NumField(); i++ { + key := jsonKey(val.Type().Field(i)) + if key == "" || key == "-" { + continue + } + delete(extraProperties, key) + } + for _, key := range exclude { + delete(extraProperties, key) + } + if len(extraProperties) == 0 { + return nil, nil + } + return extraProperties, nil +} + +// getKeys returns the keys associated with the given value. The value must be a +// a struct or a map with string keys. +func getKeys(value interface{}) ([]string, error) { + val := reflect.ValueOf(value) + if val.Kind() == reflect.Ptr { + val = val.Elem() + } + if !val.IsValid() { + return nil, nil + } + switch val.Kind() { + case reflect.Struct: + return getKeysForStructType(val.Type()), nil + case reflect.Map: + var keys []string + if val.Type().Key().Kind() != reflect.String { + return nil, fmt.Errorf("cannot extract keys from %T; only structs and maps with string keys are supported", value) + } + for _, key := range val.MapKeys() { + keys = append(keys, key.String()) + } + return keys, nil + default: + return nil, fmt.Errorf("cannot extract keys from %T; only structs and maps with string keys are supported", value) + } +} + +// getKeysForStructType returns all the keys associated with the given struct type, +// visiting embedded fields recursively. +func getKeysForStructType(structType reflect.Type) []string { + if structType.Kind() == reflect.Pointer { + structType = structType.Elem() + } + if structType.Kind() != reflect.Struct { + return nil + } + var keys []string + for i := 0; i < structType.NumField(); i++ { + field := structType.Field(i) + if field.Anonymous { + keys = append(keys, getKeysForStructType(field.Type)...) + continue + } + keys = append(keys, jsonKey(field)) + } + return keys +} + +// jsonKey returns the JSON key from the struct tag of the given field, +// excluding the omitempty flag (if any). +func jsonKey(field reflect.StructField) string { + return strings.TrimSuffix(field.Tag.Get("json"), ",omitempty") +} + +// isEmptyJSON returns true if the given data is empty, the empty JSON object, or +// an explicit null. +func isEmptyJSON(data []byte) bool { + return len(data) <= 2 || bytes.Equal(data, []byte("null")) +} diff --git a/seed/go-sdk/inferred-auth-implicit-reference/internal/extra_properties_test.go b/seed/go-sdk/inferred-auth-implicit-reference/internal/extra_properties_test.go new file mode 100644 index 000000000000..aa2510ee5121 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/internal/extra_properties_test.go @@ -0,0 +1,228 @@ +package internal + +import ( + "encoding/json" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type testMarshaler struct { + Name string `json:"name"` + BirthDate time.Time `json:"birthDate"` + CreatedAt time.Time `json:"created_at"` +} + +func (t *testMarshaler) MarshalJSON() ([]byte, error) { + type embed testMarshaler + var marshaler = struct { + embed + BirthDate string `json:"birthDate"` + CreatedAt string `json:"created_at"` + }{ + embed: embed(*t), + BirthDate: t.BirthDate.Format("2006-01-02"), + CreatedAt: t.CreatedAt.Format(time.RFC3339), + } + return MarshalJSONWithExtraProperty(marshaler, "type", "test") +} + +func TestMarshalJSONWithExtraProperties(t *testing.T) { + tests := []struct { + desc string + giveMarshaler interface{} + giveExtraProperties map[string]interface{} + wantBytes []byte + wantError string + }{ + { + desc: "invalid type", + giveMarshaler: []string{"invalid"}, + giveExtraProperties: map[string]interface{}{"key": "overwrite"}, + wantError: `cannot extract keys from []string; only structs and maps with string keys are supported`, + }, + { + desc: "invalid key type", + giveMarshaler: map[int]interface{}{42: "value"}, + giveExtraProperties: map[string]interface{}{"key": "overwrite"}, + wantError: `cannot extract keys from map[int]interface {}; only structs and maps with string keys are supported`, + }, + { + desc: "invalid map overwrite", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{"key": "overwrite"}, + wantError: `cannot add extra property "key" because it is already defined on the type`, + }, + { + desc: "invalid struct overwrite", + giveMarshaler: new(testMarshaler), + giveExtraProperties: map[string]interface{}{"birthDate": "2000-01-01"}, + wantError: `cannot add extra property "birthDate" because it is already defined on the type`, + }, + { + desc: "invalid struct overwrite embedded type", + giveMarshaler: new(testMarshaler), + giveExtraProperties: map[string]interface{}{"name": "bob"}, + wantError: `cannot add extra property "name" because it is already defined on the type`, + }, + { + desc: "nil", + giveMarshaler: nil, + giveExtraProperties: nil, + wantBytes: []byte(`null`), + }, + { + desc: "empty", + giveMarshaler: map[string]interface{}{}, + giveExtraProperties: map[string]interface{}{}, + wantBytes: []byte(`{}`), + }, + { + desc: "no extra properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "only extra properties", + giveMarshaler: map[string]interface{}{}, + giveExtraProperties: map[string]interface{}{"key": "value"}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "single extra property", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{"extra": "property"}, + wantBytes: []byte(`{"key":"value","extra":"property"}`), + }, + { + desc: "multiple extra properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{"one": 1, "two": 2}, + wantBytes: []byte(`{"key":"value","one":1,"two":2}`), + }, + { + desc: "nested properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{ + "user": map[string]interface{}{ + "age": 42, + "name": "alice", + }, + }, + wantBytes: []byte(`{"key":"value","user":{"age":42,"name":"alice"}}`), + }, + { + desc: "multiple nested properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{ + "metadata": map[string]interface{}{ + "ip": "127.0.0.1", + }, + "user": map[string]interface{}{ + "age": 42, + "name": "alice", + }, + }, + wantBytes: []byte(`{"key":"value","metadata":{"ip":"127.0.0.1"},"user":{"age":42,"name":"alice"}}`), + }, + { + desc: "custom marshaler", + giveMarshaler: &testMarshaler{ + Name: "alice", + BirthDate: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), + CreatedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC), + }, + giveExtraProperties: map[string]interface{}{ + "extra": "property", + }, + wantBytes: []byte(`{"name":"alice","birthDate":"2000-01-01","created_at":"2024-01-01T00:00:00Z","type":"test","extra":"property"}`), + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + bytes, err := MarshalJSONWithExtraProperties(tt.giveMarshaler, tt.giveExtraProperties) + if tt.wantError != "" { + require.EqualError(t, err, tt.wantError) + assert.Nil(t, tt.wantBytes) + return + } + require.NoError(t, err) + assert.Equal(t, tt.wantBytes, bytes) + + value := make(map[string]interface{}) + require.NoError(t, json.Unmarshal(bytes, &value)) + }) + } +} + +func TestExtractExtraProperties(t *testing.T) { + t.Run("none", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice"}`), value) + require.NoError(t, err) + assert.Nil(t, extraProperties) + }) + + t.Run("non-nil pointer", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]interface{}{"age": float64(42)}, extraProperties) + }) + + t.Run("nil pointer", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + var value *user + _, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + assert.EqualError(t, err, "value must be non-nil to extract extra properties") + }) + + t.Run("non-zero value", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]interface{}{"age": float64(42)}, extraProperties) + }) + + t.Run("zero value", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + var value user + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]interface{}{"age": float64(42)}, extraProperties) + }) + + t.Run("exclude", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value, "age") + require.NoError(t, err) + assert.Nil(t, extraProperties) + }) +} diff --git a/seed/go-sdk/inferred-auth-implicit-reference/internal/http.go b/seed/go-sdk/inferred-auth-implicit-reference/internal/http.go new file mode 100644 index 000000000000..77863752bb58 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/internal/http.go @@ -0,0 +1,71 @@ +package internal + +import ( + "fmt" + "net/http" + "net/url" + "reflect" +) + +// HTTPClient is an interface for a subset of the *http.Client. +type HTTPClient interface { + Do(*http.Request) (*http.Response, error) +} + +// ResolveBaseURL resolves the base URL from the given arguments, +// preferring the first non-empty value. +func ResolveBaseURL(values ...string) string { + for _, value := range values { + if value != "" { + return value + } + } + return "" +} + +// EncodeURL encodes the given arguments into the URL, escaping +// values as needed. Pointer arguments are dereferenced before processing. +func EncodeURL(urlFormat string, args ...interface{}) string { + escapedArgs := make([]interface{}, 0, len(args)) + for _, arg := range args { + // Dereference the argument if it's a pointer + value := dereferenceArg(arg) + escapedArgs = append(escapedArgs, url.PathEscape(fmt.Sprintf("%v", value))) + } + return fmt.Sprintf(urlFormat, escapedArgs...) +} + +// dereferenceArg dereferences a pointer argument if necessary, returning the underlying value. +// If the argument is not a pointer or is nil, it returns the argument as-is. +func dereferenceArg(arg interface{}) interface{} { + if arg == nil { + return arg + } + + v := reflect.ValueOf(arg) + + // Keep dereferencing until we get to a non-pointer value or hit nil + for v.Kind() == reflect.Ptr { + if v.IsNil() { + return nil + } + v = v.Elem() + } + + return v.Interface() +} + +// MergeHeaders merges the given headers together, where the right +// takes precedence over the left. +func MergeHeaders(left, right http.Header) http.Header { + for key, values := range right { + if len(values) > 1 { + left[key] = values + continue + } + if value := right.Get(key); value != "" { + left.Set(key, value) + } + } + return left +} diff --git a/seed/go-sdk/inferred-auth-implicit-reference/internal/query.go b/seed/go-sdk/inferred-auth-implicit-reference/internal/query.go new file mode 100644 index 000000000000..1cbaf7fe1c02 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/internal/query.go @@ -0,0 +1,353 @@ +package internal + +import ( + "encoding/base64" + "fmt" + "net/url" + "reflect" + "strings" + "time" + + "github.com/google/uuid" +) + +var ( + bytesType = reflect.TypeOf([]byte{}) + queryEncoderType = reflect.TypeOf(new(QueryEncoder)).Elem() + timeType = reflect.TypeOf(time.Time{}) + uuidType = reflect.TypeOf(uuid.UUID{}) +) + +// QueryEncoder is an interface implemented by any type that wishes to encode +// itself into URL values in a non-standard way. +type QueryEncoder interface { + EncodeQueryValues(key string, v *url.Values) error +} + +// prepareValue handles common validation and unwrapping logic for both functions +func prepareValue(v interface{}) (reflect.Value, url.Values, error) { + values := make(url.Values) + val := reflect.ValueOf(v) + for val.Kind() == reflect.Ptr { + if val.IsNil() { + return reflect.Value{}, values, nil + } + val = val.Elem() + } + + if v == nil { + return reflect.Value{}, values, nil + } + + if val.Kind() != reflect.Struct { + return reflect.Value{}, nil, fmt.Errorf("query: Values() expects struct input. Got %v", val.Kind()) + } + + err := reflectValue(values, val, "") + if err != nil { + return reflect.Value{}, nil, err + } + + return val, values, nil +} + +// QueryValues encodes url.Values from request objects. +// +// Note: This type is inspired by Google's query encoding library, but +// supports far less customization and is tailored to fit this SDK's use case. +// +// Ref: https://github.com/google/go-querystring +func QueryValues(v interface{}) (url.Values, error) { + _, values, err := prepareValue(v) + return values, err +} + +// QueryValuesWithDefaults encodes url.Values from request objects +// and default values, merging the defaults into the request. +// It's expected that the values of defaults are wire names. +func QueryValuesWithDefaults(v interface{}, defaults map[string]interface{}) (url.Values, error) { + val, values, err := prepareValue(v) + if err != nil { + return values, err + } + if !val.IsValid() { + return values, nil + } + + // apply defaults to zero-value fields directly on the original struct + valType := val.Type() + for i := 0; i < val.NumField(); i++ { + field := val.Field(i) + fieldType := valType.Field(i) + fieldName := fieldType.Name + + if fieldType.PkgPath != "" && !fieldType.Anonymous { + // Skip unexported fields. + continue + } + + // check if field is zero value and we have a default for it + if field.CanSet() && field.IsZero() { + tag := fieldType.Tag.Get("url") + if tag == "" || tag == "-" { + continue + } + wireName, _ := parseTag(tag) + if wireName == "" { + wireName = fieldName + } + if defaultVal, exists := defaults[wireName]; exists { + values.Set(wireName, valueString(reflect.ValueOf(defaultVal), tagOptions{}, reflect.StructField{})) + } + } + } + + return values, err +} + +// reflectValue populates the values parameter from the struct fields in val. +// Embedded structs are followed recursively (using the rules defined in the +// Values function documentation) breadth-first. +func reflectValue(values url.Values, val reflect.Value, scope string) error { + typ := val.Type() + for i := 0; i < typ.NumField(); i++ { + sf := typ.Field(i) + if sf.PkgPath != "" && !sf.Anonymous { + // Skip unexported fields. + continue + } + + sv := val.Field(i) + tag := sf.Tag.Get("url") + if tag == "" || tag == "-" { + continue + } + + name, opts := parseTag(tag) + if name == "" { + name = sf.Name + } + + if scope != "" { + name = scope + "[" + name + "]" + } + + if opts.Contains("omitempty") && isEmptyValue(sv) { + continue + } + + if sv.Type().Implements(queryEncoderType) { + // If sv is a nil pointer and the custom encoder is defined on a non-pointer + // method receiver, set sv to the zero value of the underlying type + if !reflect.Indirect(sv).IsValid() && sv.Type().Elem().Implements(queryEncoderType) { + sv = reflect.New(sv.Type().Elem()) + } + + m := sv.Interface().(QueryEncoder) + if err := m.EncodeQueryValues(name, &values); err != nil { + return err + } + continue + } + + // Recursively dereference pointers, but stop at nil pointers. + for sv.Kind() == reflect.Ptr { + if sv.IsNil() { + break + } + sv = sv.Elem() + } + + if sv.Type() == uuidType || sv.Type() == bytesType || sv.Type() == timeType { + values.Add(name, valueString(sv, opts, sf)) + continue + } + + if sv.Kind() == reflect.Slice || sv.Kind() == reflect.Array { + if sv.Len() == 0 { + // Skip if slice or array is empty. + continue + } + for i := 0; i < sv.Len(); i++ { + value := sv.Index(i) + if isStructPointer(value) && !value.IsNil() { + if err := reflectValue(values, value.Elem(), name); err != nil { + return err + } + } else { + values.Add(name, valueString(value, opts, sf)) + } + } + continue + } + + if sv.Kind() == reflect.Map { + if err := reflectMap(values, sv, name); err != nil { + return err + } + continue + } + + if sv.Kind() == reflect.Struct { + if err := reflectValue(values, sv, name); err != nil { + return err + } + continue + } + + values.Add(name, valueString(sv, opts, sf)) + } + + return nil +} + +// reflectMap handles map types specifically, generating query parameters in the format key[mapkey]=value +func reflectMap(values url.Values, val reflect.Value, scope string) error { + if val.IsNil() { + return nil + } + + iter := val.MapRange() + for iter.Next() { + k := iter.Key() + v := iter.Value() + + key := fmt.Sprint(k.Interface()) + paramName := scope + "[" + key + "]" + + for v.Kind() == reflect.Ptr { + if v.IsNil() { + break + } + v = v.Elem() + } + + for v.Kind() == reflect.Interface { + v = v.Elem() + } + + if v.Kind() == reflect.Map { + if err := reflectMap(values, v, paramName); err != nil { + return err + } + continue + } + + if v.Kind() == reflect.Struct { + if err := reflectValue(values, v, paramName); err != nil { + return err + } + continue + } + + if v.Kind() == reflect.Slice || v.Kind() == reflect.Array { + if v.Len() == 0 { + continue + } + for i := 0; i < v.Len(); i++ { + value := v.Index(i) + if isStructPointer(value) && !value.IsNil() { + if err := reflectValue(values, value.Elem(), paramName); err != nil { + return err + } + } else { + values.Add(paramName, valueString(value, tagOptions{}, reflect.StructField{})) + } + } + continue + } + + values.Add(paramName, valueString(v, tagOptions{}, reflect.StructField{})) + } + + return nil +} + +// valueString returns the string representation of a value. +func valueString(v reflect.Value, opts tagOptions, sf reflect.StructField) string { + for v.Kind() == reflect.Ptr { + if v.IsNil() { + return "" + } + v = v.Elem() + } + + if v.Type() == timeType { + t := v.Interface().(time.Time) + if format := sf.Tag.Get("format"); format == "date" { + return t.Format("2006-01-02") + } + return t.Format(time.RFC3339) + } + + if v.Type() == uuidType { + u := v.Interface().(uuid.UUID) + return u.String() + } + + if v.Type() == bytesType { + b := v.Interface().([]byte) + return base64.StdEncoding.EncodeToString(b) + } + + return fmt.Sprint(v.Interface()) +} + +// isEmptyValue checks if a value should be considered empty for the purposes +// of omitting fields with the "omitempty" option. +func isEmptyValue(v reflect.Value) bool { + type zeroable interface { + IsZero() bool + } + + if !v.IsZero() { + if z, ok := v.Interface().(zeroable); ok { + return z.IsZero() + } + } + + switch v.Kind() { + case reflect.Array, reflect.Map, reflect.Slice, reflect.String: + return v.Len() == 0 + case reflect.Bool: + return !v.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Interface, reflect.Ptr: + return v.IsNil() + case reflect.Invalid, reflect.Complex64, reflect.Complex128, reflect.Chan, reflect.Func, reflect.Struct, reflect.UnsafePointer: + return false + } + + return false +} + +// isStructPointer returns true if the given reflect.Value is a pointer to a struct. +func isStructPointer(v reflect.Value) bool { + return v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct +} + +// tagOptions is the string following a comma in a struct field's "url" tag, or +// the empty string. It does not include the leading comma. +type tagOptions []string + +// parseTag splits a struct field's url tag into its name and comma-separated +// options. +func parseTag(tag string) (string, tagOptions) { + s := strings.Split(tag, ",") + return s[0], s[1:] +} + +// Contains checks whether the tagOptions contains the specified option. +func (o tagOptions) Contains(option string) bool { + for _, s := range o { + if s == option { + return true + } + } + return false +} diff --git a/seed/go-sdk/inferred-auth-implicit-reference/internal/query_test.go b/seed/go-sdk/inferred-auth-implicit-reference/internal/query_test.go new file mode 100644 index 000000000000..2c28cb8acf68 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/internal/query_test.go @@ -0,0 +1,395 @@ +package internal + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestQueryValues(t *testing.T) { + t.Run("empty optional", func(t *testing.T) { + type nested struct { + Value *string `json:"value,omitempty" url:"value,omitempty"` + } + type example struct { + Nested *nested `json:"nested,omitempty" url:"nested,omitempty"` + } + + values, err := QueryValues(&example{}) + require.NoError(t, err) + assert.Empty(t, values) + }) + + t.Run("empty required", func(t *testing.T) { + type nested struct { + Value *string `json:"value,omitempty" url:"value,omitempty"` + } + type example struct { + Required string `json:"required" url:"required"` + Nested *nested `json:"nested,omitempty" url:"nested,omitempty"` + } + + values, err := QueryValues(&example{}) + require.NoError(t, err) + assert.Equal(t, "required=", values.Encode()) + }) + + t.Run("allow multiple", func(t *testing.T) { + type example struct { + Values []string `json:"values" url:"values"` + } + + values, err := QueryValues( + &example{ + Values: []string{"foo", "bar", "baz"}, + }, + ) + require.NoError(t, err) + assert.Equal(t, "values=foo&values=bar&values=baz", values.Encode()) + }) + + t.Run("nested object", func(t *testing.T) { + type nested struct { + Value *string `json:"value,omitempty" url:"value,omitempty"` + } + type example struct { + Required string `json:"required" url:"required"` + Nested *nested `json:"nested,omitempty" url:"nested,omitempty"` + } + + nestedValue := "nestedValue" + values, err := QueryValues( + &example{ + Required: "requiredValue", + Nested: &nested{ + Value: &nestedValue, + }, + }, + ) + require.NoError(t, err) + assert.Equal(t, "nested%5Bvalue%5D=nestedValue&required=requiredValue", values.Encode()) + }) + + t.Run("url unspecified", func(t *testing.T) { + type example struct { + Required string `json:"required" url:"required"` + NotFound string `json:"notFound"` + } + + values, err := QueryValues( + &example{ + Required: "requiredValue", + NotFound: "notFound", + }, + ) + require.NoError(t, err) + assert.Equal(t, "required=requiredValue", values.Encode()) + }) + + t.Run("url ignored", func(t *testing.T) { + type example struct { + Required string `json:"required" url:"required"` + NotFound string `json:"notFound" url:"-"` + } + + values, err := QueryValues( + &example{ + Required: "requiredValue", + NotFound: "notFound", + }, + ) + require.NoError(t, err) + assert.Equal(t, "required=requiredValue", values.Encode()) + }) + + t.Run("datetime", func(t *testing.T) { + type example struct { + DateTime time.Time `json:"dateTime" url:"dateTime"` + } + + values, err := QueryValues( + &example{ + DateTime: time.Date(1994, 3, 16, 12, 34, 56, 0, time.UTC), + }, + ) + require.NoError(t, err) + assert.Equal(t, "dateTime=1994-03-16T12%3A34%3A56Z", values.Encode()) + }) + + t.Run("date", func(t *testing.T) { + type example struct { + Date time.Time `json:"date" url:"date" format:"date"` + } + + values, err := QueryValues( + &example{ + Date: time.Date(1994, 3, 16, 12, 34, 56, 0, time.UTC), + }, + ) + require.NoError(t, err) + assert.Equal(t, "date=1994-03-16", values.Encode()) + }) + + t.Run("optional time", func(t *testing.T) { + type example struct { + Date *time.Time `json:"date,omitempty" url:"date,omitempty" format:"date"` + } + + values, err := QueryValues( + &example{}, + ) + require.NoError(t, err) + assert.Empty(t, values.Encode()) + }) + + t.Run("omitempty with non-pointer zero value", func(t *testing.T) { + type enum string + + type example struct { + Enum enum `json:"enum,omitempty" url:"enum,omitempty"` + } + + values, err := QueryValues( + &example{}, + ) + require.NoError(t, err) + assert.Empty(t, values.Encode()) + }) + + t.Run("object array", func(t *testing.T) { + type object struct { + Key string `json:"key" url:"key"` + Value string `json:"value" url:"value"` + } + type example struct { + Objects []*object `json:"objects,omitempty" url:"objects,omitempty"` + } + + values, err := QueryValues( + &example{ + Objects: []*object{ + { + Key: "hello", + Value: "world", + }, + { + Key: "foo", + Value: "bar", + }, + }, + }, + ) + require.NoError(t, err) + assert.Equal(t, "objects%5Bkey%5D=hello&objects%5Bkey%5D=foo&objects%5Bvalue%5D=world&objects%5Bvalue%5D=bar", values.Encode()) + }) + + t.Run("map", func(t *testing.T) { + type request struct { + Metadata map[string]interface{} `json:"metadata" url:"metadata"` + } + values, err := QueryValues( + &request{ + Metadata: map[string]interface{}{ + "foo": "bar", + "baz": "qux", + }, + }, + ) + require.NoError(t, err) + assert.Equal(t, "metadata%5Bbaz%5D=qux&metadata%5Bfoo%5D=bar", values.Encode()) + }) + + t.Run("nested map", func(t *testing.T) { + type request struct { + Metadata map[string]interface{} `json:"metadata" url:"metadata"` + } + values, err := QueryValues( + &request{ + Metadata: map[string]interface{}{ + "inner": map[string]interface{}{ + "foo": "bar", + }, + }, + }, + ) + require.NoError(t, err) + assert.Equal(t, "metadata%5Binner%5D%5Bfoo%5D=bar", values.Encode()) + }) + + t.Run("nested map array", func(t *testing.T) { + type request struct { + Metadata map[string]interface{} `json:"metadata" url:"metadata"` + } + values, err := QueryValues( + &request{ + Metadata: map[string]interface{}{ + "inner": []string{ + "one", + "two", + "three", + }, + }, + }, + ) + require.NoError(t, err) + assert.Equal(t, "metadata%5Binner%5D=one&metadata%5Binner%5D=two&metadata%5Binner%5D=three", values.Encode()) + }) +} + +func TestQueryValuesWithDefaults(t *testing.T) { + t.Run("apply defaults to zero values", func(t *testing.T) { + type example struct { + Name string `json:"name" url:"name"` + Age int `json:"age" url:"age"` + Enabled bool `json:"enabled" url:"enabled"` + } + + defaults := map[string]interface{}{ + "name": "default-name", + "age": 25, + "enabled": true, + } + + values, err := QueryValuesWithDefaults(&example{}, defaults) + require.NoError(t, err) + assert.Equal(t, "age=25&enabled=true&name=default-name", values.Encode()) + }) + + t.Run("preserve non-zero values over defaults", func(t *testing.T) { + type example struct { + Name string `json:"name" url:"name"` + Age int `json:"age" url:"age"` + Enabled bool `json:"enabled" url:"enabled"` + } + + defaults := map[string]interface{}{ + "name": "default-name", + "age": 25, + "enabled": true, + } + + values, err := QueryValuesWithDefaults(&example{ + Name: "actual-name", + Age: 30, + // Enabled remains false (zero value), should get default + }, defaults) + require.NoError(t, err) + assert.Equal(t, "age=30&enabled=true&name=actual-name", values.Encode()) + }) + + t.Run("ignore defaults for fields not in struct", func(t *testing.T) { + type example struct { + Name string `json:"name" url:"name"` + Age int `json:"age" url:"age"` + } + + defaults := map[string]interface{}{ + "name": "default-name", + "age": 25, + "nonexistent": "should-be-ignored", + } + + values, err := QueryValuesWithDefaults(&example{}, defaults) + require.NoError(t, err) + assert.Equal(t, "age=25&name=default-name", values.Encode()) + }) + + t.Run("type conversion for compatible defaults", func(t *testing.T) { + type example struct { + Count int64 `json:"count" url:"count"` + Rate float64 `json:"rate" url:"rate"` + Message string `json:"message" url:"message"` + } + + defaults := map[string]interface{}{ + "count": int(100), // int -> int64 conversion + "rate": float32(2.5), // float32 -> float64 conversion + "message": "hello", // string -> string (no conversion needed) + } + + values, err := QueryValuesWithDefaults(&example{}, defaults) + require.NoError(t, err) + assert.Equal(t, "count=100&message=hello&rate=2.5", values.Encode()) + }) + + t.Run("mixed with pointer fields and omitempty", func(t *testing.T) { + type example struct { + Required string `json:"required" url:"required"` + Optional *string `json:"optional,omitempty" url:"optional,omitempty"` + Count int `json:"count,omitempty" url:"count,omitempty"` + } + + defaultOptional := "default-optional" + defaults := map[string]interface{}{ + "required": "default-required", + "optional": &defaultOptional, // pointer type + "count": 42, + } + + values, err := QueryValuesWithDefaults(&example{ + Required: "custom-required", // should override default + // Optional is nil, should get default + // Count is 0, should get default + }, defaults) + require.NoError(t, err) + assert.Equal(t, "count=42&optional=default-optional&required=custom-required", values.Encode()) + }) + + t.Run("override non-zero defaults with explicit zero values", func(t *testing.T) { + type example struct { + Name *string `json:"name" url:"name"` + Age *int `json:"age" url:"age"` + Enabled *bool `json:"enabled" url:"enabled"` + } + + defaults := map[string]interface{}{ + "name": "default-name", + "age": 25, + "enabled": true, + } + + // first, test that a properly empty request is overridden: + { + values, err := QueryValuesWithDefaults(&example{}, defaults) + require.NoError(t, err) + assert.Equal(t, "age=25&enabled=true&name=default-name", values.Encode()) + } + + // second, test that a request that contains zeros is not overridden: + var ( + name = "" + age = 0 + enabled = false + ) + values, err := QueryValuesWithDefaults(&example{ + Name: &name, // explicit empty string should override default + Age: &age, // explicit zero should override default + Enabled: &enabled, // explicit false should override default + }, defaults) + require.NoError(t, err) + assert.Equal(t, "age=0&enabled=false&name=", values.Encode()) + }) + + t.Run("nil input returns empty values", func(t *testing.T) { + defaults := map[string]any{ + "name": "default-name", + "age": 25, + } + + // Test with nil + values, err := QueryValuesWithDefaults(nil, defaults) + require.NoError(t, err) + assert.Empty(t, values) + + // Test with nil pointer + type example struct { + Name string `json:"name" url:"name"` + } + var nilPtr *example + values, err = QueryValuesWithDefaults(nilPtr, defaults) + require.NoError(t, err) + assert.Empty(t, values) + }) +} diff --git a/seed/go-sdk/inferred-auth-implicit-reference/internal/retrier.go b/seed/go-sdk/inferred-auth-implicit-reference/internal/retrier.go new file mode 100644 index 000000000000..4efae1b4c286 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/internal/retrier.go @@ -0,0 +1,230 @@ +package internal + +import ( + "crypto/rand" + "math/big" + "net/http" + "strconv" + "time" +) + +const ( + defaultRetryAttempts = 2 + minRetryDelay = 1000 * time.Millisecond + maxRetryDelay = 60000 * time.Millisecond +) + +// RetryOption adapts the behavior the *Retrier. +type RetryOption func(*retryOptions) + +// RetryFunc is a retryable HTTP function call (i.e. *http.Client.Do). +type RetryFunc func(*http.Request) (*http.Response, error) + +// WithMaxAttempts configures the maximum number of attempts +// of the *Retrier. +func WithMaxAttempts(attempts uint) RetryOption { + return func(opts *retryOptions) { + opts.attempts = attempts + } +} + +// Retrier retries failed requests a configurable number of times with an +// exponential back-off between each retry. +type Retrier struct { + attempts uint +} + +// NewRetrier constructs a new *Retrier with the given options, if any. +func NewRetrier(opts ...RetryOption) *Retrier { + options := new(retryOptions) + for _, opt := range opts { + opt(options) + } + attempts := uint(defaultRetryAttempts) + if options.attempts > 0 { + attempts = options.attempts + } + return &Retrier{ + attempts: attempts, + } +} + +// Run issues the request and, upon failure, retries the request if possible. +// +// The request will be retried as long as the request is deemed retryable and the +// number of retry attempts has not grown larger than the configured retry limit. +func (r *Retrier) Run( + fn RetryFunc, + request *http.Request, + errorDecoder ErrorDecoder, + opts ...RetryOption, +) (*http.Response, error) { + options := new(retryOptions) + for _, opt := range opts { + opt(options) + } + maxRetryAttempts := r.attempts + if options.attempts > 0 { + maxRetryAttempts = options.attempts + } + var ( + retryAttempt uint + previousError error + ) + return r.run( + fn, + request, + errorDecoder, + maxRetryAttempts, + retryAttempt, + previousError, + ) +} + +func (r *Retrier) run( + fn RetryFunc, + request *http.Request, + errorDecoder ErrorDecoder, + maxRetryAttempts uint, + retryAttempt uint, + previousError error, +) (*http.Response, error) { + if retryAttempt >= maxRetryAttempts { + return nil, previousError + } + + // If the call has been cancelled, don't issue the request. + if err := request.Context().Err(); err != nil { + return nil, err + } + + response, err := fn(request) + if err != nil { + return nil, err + } + + if r.shouldRetry(response) { + defer response.Body.Close() + + delay, err := r.retryDelay(response, retryAttempt) + if err != nil { + return nil, err + } + + time.Sleep(delay) + + return r.run( + fn, + request, + errorDecoder, + maxRetryAttempts, + retryAttempt+1, + decodeError(response, errorDecoder), + ) + } + + return response, nil +} + +// shouldRetry returns true if the request should be retried based on the given +// response status code. +func (r *Retrier) shouldRetry(response *http.Response) bool { + return response.StatusCode == http.StatusTooManyRequests || + response.StatusCode == http.StatusRequestTimeout || + response.StatusCode >= http.StatusInternalServerError +} + +// retryDelay calculates the delay time based on response headers, +// falling back to exponential backoff if no headers are present. +func (r *Retrier) retryDelay(response *http.Response, retryAttempt uint) (time.Duration, error) { + // Check for Retry-After header first (RFC 7231), applying no jitter + if retryAfter := response.Header.Get("Retry-After"); retryAfter != "" { + // Parse as number of seconds... + if seconds, err := strconv.Atoi(retryAfter); err == nil { + delay := time.Duration(seconds) * time.Second + if delay > 0 { + if delay > maxRetryDelay { + delay = maxRetryDelay + } + return delay, nil + } + } + + // ...or as an HTTP date; both are valid + if retryTime, err := time.Parse(time.RFC1123, retryAfter); err == nil { + delay := time.Until(retryTime) + if delay > 0 { + if delay > maxRetryDelay { + delay = maxRetryDelay + } + return delay, nil + } + } + } + + // Then check for industry-standard X-RateLimit-Reset header, applying positive jitter + if rateLimitReset := response.Header.Get("X-RateLimit-Reset"); rateLimitReset != "" { + if resetTimestamp, err := strconv.ParseInt(rateLimitReset, 10, 64); err == nil { + // Assume Unix timestamp in seconds + resetTime := time.Unix(resetTimestamp, 0) + delay := time.Until(resetTime) + if delay > 0 { + if delay > maxRetryDelay { + delay = maxRetryDelay + } + return r.addPositiveJitter(delay) + } + } + } + + // Fall back to exponential backoff + return r.exponentialBackoff(retryAttempt) +} + +// exponentialBackoff calculates the delay time based on the retry attempt +// and applies symmetric jitter (±10% around the delay). +func (r *Retrier) exponentialBackoff(retryAttempt uint) (time.Duration, error) { + if retryAttempt > 63 { // 2^63+ would overflow uint64 + retryAttempt = 63 + } + + delay := minRetryDelay << retryAttempt + if delay > maxRetryDelay { + delay = maxRetryDelay + } + + return r.addSymmetricJitter(delay) +} + +// addJitterWithRange applies jitter to the given delay. +// minPercent and maxPercent define the jitter range (e.g., 100, 120 for +0% to +20%). +func (r *Retrier) addJitterWithRange(delay time.Duration, minPercent, maxPercent int) (time.Duration, error) { + jitterRange := big.NewInt(int64(delay * time.Duration(maxPercent-minPercent) / 100)) + jitter, err := rand.Int(rand.Reader, jitterRange) + if err != nil { + return 0, err + } + + jitteredDelay := delay + time.Duration(jitter.Int64()) + delay*time.Duration(minPercent-100)/100 + if jitteredDelay < minRetryDelay { + jitteredDelay = minRetryDelay + } + if jitteredDelay > maxRetryDelay { + jitteredDelay = maxRetryDelay + } + return jitteredDelay, nil +} + +// addPositiveJitter applies positive jitter to the given delay (100%-120% range). +func (r *Retrier) addPositiveJitter(delay time.Duration) (time.Duration, error) { + return r.addJitterWithRange(delay, 100, 120) +} + +// addSymmetricJitter applies symmetric jitter to the given delay (90%-110% range). +func (r *Retrier) addSymmetricJitter(delay time.Duration) (time.Duration, error) { + return r.addJitterWithRange(delay, 90, 110) +} + +type retryOptions struct { + attempts uint +} diff --git a/seed/go-sdk/inferred-auth-implicit-reference/internal/retrier_test.go b/seed/go-sdk/inferred-auth-implicit-reference/internal/retrier_test.go new file mode 100644 index 000000000000..216909772d89 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/internal/retrier_test.go @@ -0,0 +1,300 @@ +package internal + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/inferred-auth-implicit-reference/fern/core" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type RetryTestCase struct { + description string + + giveAttempts uint + giveStatusCodes []int + giveResponse *InternalTestResponse + + wantResponse *InternalTestResponse + wantError *core.APIError +} + +func TestRetrier(t *testing.T) { + tests := []*RetryTestCase{ + { + description: "retry request succeeds after multiple failures", + giveAttempts: 3, + giveStatusCodes: []int{ + http.StatusServiceUnavailable, + http.StatusServiceUnavailable, + http.StatusOK, + }, + giveResponse: &InternalTestResponse{ + Id: "1", + }, + wantResponse: &InternalTestResponse{ + Id: "1", + }, + }, + { + description: "retry request fails if MaxAttempts is exceeded", + giveAttempts: 3, + giveStatusCodes: []int{ + http.StatusRequestTimeout, + http.StatusRequestTimeout, + http.StatusRequestTimeout, + http.StatusOK, + }, + wantError: &core.APIError{ + StatusCode: http.StatusRequestTimeout, + }, + }, + { + description: "retry durations increase exponentially and stay within the min and max delay values", + giveAttempts: 4, + giveStatusCodes: []int{ + http.StatusServiceUnavailable, + http.StatusServiceUnavailable, + http.StatusServiceUnavailable, + http.StatusOK, + }, + }, + { + description: "retry does not occur on status code 404", + giveAttempts: 2, + giveStatusCodes: []int{http.StatusNotFound, http.StatusOK}, + wantError: &core.APIError{ + StatusCode: http.StatusNotFound, + }, + }, + { + description: "retries occur on status code 429", + giveAttempts: 2, + giveStatusCodes: []int{http.StatusTooManyRequests, http.StatusOK}, + }, + { + description: "retries occur on status code 408", + giveAttempts: 2, + giveStatusCodes: []int{http.StatusRequestTimeout, http.StatusOK}, + }, + { + description: "retries occur on status code 500", + giveAttempts: 2, + giveStatusCodes: []int{http.StatusInternalServerError, http.StatusOK}, + }, + } + + for _, tc := range tests { + t.Run(tc.description, func(t *testing.T) { + var ( + test = tc + server = newTestRetryServer(t, test) + client = server.Client() + ) + + t.Parallel() + + caller := NewCaller( + &CallerParams{ + Client: client, + }, + ) + + var response *InternalTestResponse + _, err := caller.Call( + context.Background(), + &CallParams{ + URL: server.URL, + Method: http.MethodGet, + Request: &InternalTestRequest{}, + Response: &response, + MaxAttempts: test.giveAttempts, + ResponseIsOptional: true, + }, + ) + + if test.wantError != nil { + require.IsType(t, err, &core.APIError{}) + expectedErrorCode := test.wantError.StatusCode + actualErrorCode := err.(*core.APIError).StatusCode + assert.Equal(t, expectedErrorCode, actualErrorCode) + return + } + + require.NoError(t, err) + assert.Equal(t, test.wantResponse, response) + }) + } +} + +// newTestRetryServer returns a new *httptest.Server configured with the +// given test parameters, suitable for testing retries. +func newTestRetryServer(t *testing.T, tc *RetryTestCase) *httptest.Server { + var index int + timestamps := make([]time.Time, 0, len(tc.giveStatusCodes)) + + return httptest.NewServer( + http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + timestamps = append(timestamps, time.Now()) + if index > 0 && index < len(expectedRetryDurations) { + // Ensure that the duration between retries increases exponentially, + // and that it is within the minimum and maximum retry delay values. + actualDuration := timestamps[index].Sub(timestamps[index-1]) + expectedDurationMin := expectedRetryDurations[index-1] * 50 / 100 + expectedDurationMax := expectedRetryDurations[index-1] * 150 / 100 + assert.True( + t, + actualDuration >= expectedDurationMin && actualDuration <= expectedDurationMax, + "expected duration to be in range [%v, %v], got %v", + expectedDurationMin, + expectedDurationMax, + actualDuration, + ) + assert.LessOrEqual( + t, + actualDuration, + maxRetryDelay, + "expected duration to be less than the maxRetryDelay (%v), got %v", + maxRetryDelay, + actualDuration, + ) + assert.GreaterOrEqual( + t, + actualDuration, + minRetryDelay, + "expected duration to be greater than the minRetryDelay (%v), got %v", + minRetryDelay, + actualDuration, + ) + } + + request := new(InternalTestRequest) + bytes, err := io.ReadAll(r.Body) + require.NoError(t, err) + require.NoError(t, json.Unmarshal(bytes, request)) + require.LessOrEqual(t, index, len(tc.giveStatusCodes)) + + statusCode := tc.giveStatusCodes[index] + + w.WriteHeader(statusCode) + + if tc.giveResponse != nil && statusCode == http.StatusOK { + bytes, err = json.Marshal(tc.giveResponse) + require.NoError(t, err) + _, err = w.Write(bytes) + require.NoError(t, err) + } + + index++ + }, + ), + ) +} + +// expectedRetryDurations holds an array of calculated retry durations, +// where the index of the array should correspond to the retry attempt. +// +// Values are calculated based off of `minRetryDelay * 2^i`. +var expectedRetryDurations = []time.Duration{ + 1000 * time.Millisecond, // 500ms * 2^1 = 1000ms + 2000 * time.Millisecond, // 500ms * 2^2 = 2000ms + 4000 * time.Millisecond, // 500ms * 2^3 = 4000ms + 8000 * time.Millisecond, // 500ms * 2^4 = 8000ms +} + +func TestRetryDelayTiming(t *testing.T) { + tests := []struct { + name string + headerName string + headerValueFunc func() string + expectedMinMs int64 + expectedMaxMs int64 + }{ + { + name: "retry-after with seconds value", + headerName: "retry-after", + headerValueFunc: func() string { + return "1" + }, + expectedMinMs: 500, + expectedMaxMs: 1500, + }, + { + name: "retry-after with HTTP date", + headerName: "retry-after", + headerValueFunc: func() string { + return time.Now().Add(3 * time.Second).Format(time.RFC1123) + }, + expectedMinMs: 1500, + expectedMaxMs: 4500, + }, + { + name: "x-ratelimit-reset with future timestamp", + headerName: "x-ratelimit-reset", + headerValueFunc: func() string { + return fmt.Sprintf("%d", time.Now().Add(3*time.Second).Unix()) + }, + expectedMinMs: 1500, + expectedMaxMs: 4500, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + var timestamps []time.Time + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + timestamps = append(timestamps, time.Now()) + if len(timestamps) == 1 { + // First request - return retryable error with header + w.Header().Set(tt.headerName, tt.headerValueFunc()) + w.WriteHeader(http.StatusTooManyRequests) + } else { + // Second request - return success + w.WriteHeader(http.StatusOK) + response := &InternalTestResponse{Id: "success"} + bytes, _ := json.Marshal(response) + w.Write(bytes) + } + })) + defer server.Close() + + caller := NewCaller(&CallerParams{ + Client: server.Client(), + }) + + var response *InternalTestResponse + _, err := caller.Call( + context.Background(), + &CallParams{ + URL: server.URL, + Method: http.MethodGet, + Request: &InternalTestRequest{}, + Response: &response, + MaxAttempts: 2, + ResponseIsOptional: true, + }, + ) + + require.NoError(t, err) + require.Len(t, timestamps, 2, "Expected exactly 2 requests") + + actualDelayMs := timestamps[1].Sub(timestamps[0]).Milliseconds() + + assert.GreaterOrEqual(t, actualDelayMs, tt.expectedMinMs, + "Actual delay %dms should be >= expected min %dms", actualDelayMs, tt.expectedMinMs) + assert.LessOrEqual(t, actualDelayMs, tt.expectedMaxMs, + "Actual delay %dms should be <= expected max %dms", actualDelayMs, tt.expectedMaxMs) + }) + } +} diff --git a/seed/go-sdk/inferred-auth-implicit-reference/internal/stringer.go b/seed/go-sdk/inferred-auth-implicit-reference/internal/stringer.go new file mode 100644 index 000000000000..312801851e0e --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/internal/stringer.go @@ -0,0 +1,13 @@ +package internal + +import "encoding/json" + +// StringifyJSON returns a pretty JSON string representation of +// the given value. +func StringifyJSON(value interface{}) (string, error) { + bytes, err := json.MarshalIndent(value, "", " ") + if err != nil { + return "", err + } + return string(bytes), nil +} diff --git a/seed/go-sdk/inferred-auth-implicit-reference/internal/time.go b/seed/go-sdk/inferred-auth-implicit-reference/internal/time.go new file mode 100644 index 000000000000..ab0e269fade3 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/internal/time.go @@ -0,0 +1,137 @@ +package internal + +import ( + "encoding/json" + "time" +) + +const dateFormat = "2006-01-02" + +// DateTime wraps time.Time and adapts its JSON representation +// to conform to a RFC3339 date (e.g. 2006-01-02). +// +// Ref: https://ijmacd.github.io/rfc3339-iso8601 +type Date struct { + t *time.Time +} + +// NewDate returns a new *Date. If the given time.Time +// is nil, nil will be returned. +func NewDate(t time.Time) *Date { + return &Date{t: &t} +} + +// NewOptionalDate returns a new *Date. If the given time.Time +// is nil, nil will be returned. +func NewOptionalDate(t *time.Time) *Date { + if t == nil { + return nil + } + return &Date{t: t} +} + +// Time returns the Date's underlying time, if any. If the +// date is nil, the zero value is returned. +func (d *Date) Time() time.Time { + if d == nil || d.t == nil { + return time.Time{} + } + return *d.t +} + +// TimePtr returns a pointer to the Date's underlying time.Time, if any. +func (d *Date) TimePtr() *time.Time { + if d == nil || d.t == nil { + return nil + } + if d.t.IsZero() { + return nil + } + return d.t +} + +func (d *Date) MarshalJSON() ([]byte, error) { + if d == nil || d.t == nil { + return nil, nil + } + return json.Marshal(d.t.Format(dateFormat)) +} + +func (d *Date) UnmarshalJSON(data []byte) error { + var raw string + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + + parsedTime, err := time.Parse(dateFormat, raw) + if err != nil { + return err + } + + *d = Date{t: &parsedTime} + return nil +} + +// DateTime wraps time.Time and adapts its JSON representation +// to conform to a RFC3339 date-time (e.g. 2017-07-21T17:32:28Z). +// +// Ref: https://ijmacd.github.io/rfc3339-iso8601 +type DateTime struct { + t *time.Time +} + +// NewDateTime returns a new *DateTime. +func NewDateTime(t time.Time) *DateTime { + return &DateTime{t: &t} +} + +// NewOptionalDateTime returns a new *DateTime. If the given time.Time +// is nil, nil will be returned. +func NewOptionalDateTime(t *time.Time) *DateTime { + if t == nil { + return nil + } + return &DateTime{t: t} +} + +// Time returns the DateTime's underlying time, if any. If the +// date-time is nil, the zero value is returned. +func (d *DateTime) Time() time.Time { + if d == nil || d.t == nil { + return time.Time{} + } + return *d.t +} + +// TimePtr returns a pointer to the DateTime's underlying time.Time, if any. +func (d *DateTime) TimePtr() *time.Time { + if d == nil || d.t == nil { + return nil + } + if d.t.IsZero() { + return nil + } + return d.t +} + +func (d *DateTime) MarshalJSON() ([]byte, error) { + if d == nil || d.t == nil { + return nil, nil + } + return json.Marshal(d.t.Format(time.RFC3339)) +} + +func (d *DateTime) UnmarshalJSON(data []byte) error { + var raw string + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + + parsedTime, err := time.Parse(time.RFC3339, raw) + if err != nil { + return err + } + + *d = DateTime{t: &parsedTime} + return nil +} diff --git a/seed/go-sdk/inferred-auth-implicit-reference/nested/api/client.go b/seed/go-sdk/inferred-auth-implicit-reference/nested/api/client.go new file mode 100644 index 000000000000..88c103e656c5 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/nested/api/client.go @@ -0,0 +1,46 @@ +// Code generated by Fern. DO NOT EDIT. + +package api + +import ( + context "context" + core "github.com/inferred-auth-implicit-reference/fern/core" + internal "github.com/inferred-auth-implicit-reference/fern/internal" + option "github.com/inferred-auth-implicit-reference/fern/option" +) + +type Client struct { + WithRawResponse *RawClient + + options *core.RequestOptions + baseURL string + caller *internal.Caller +} + +func NewClient(options *core.RequestOptions) *Client { + return &Client{ + WithRawResponse: NewRawClient(options), + options: options, + baseURL: options.BaseURL, + caller: internal.NewCaller( + &internal.CallerParams{ + Client: options.HTTPClient, + MaxAttempts: options.MaxAttempts, + }, + ), + } +} + +func (c *Client) GetSomething( + ctx context.Context, + opts ...option.RequestOption, +) error { + _, err := c.WithRawResponse.GetSomething( + ctx, + opts..., + ) + if err != nil { + return err + } + return nil +} diff --git a/seed/go-sdk/inferred-auth-implicit-reference/nested/api/raw_client.go b/seed/go-sdk/inferred-auth-implicit-reference/nested/api/raw_client.go new file mode 100644 index 000000000000..7b6374881e79 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/nested/api/raw_client.go @@ -0,0 +1,67 @@ +// Code generated by Fern. DO NOT EDIT. + +package api + +import ( + context "context" + core "github.com/inferred-auth-implicit-reference/fern/core" + internal "github.com/inferred-auth-implicit-reference/fern/internal" + option "github.com/inferred-auth-implicit-reference/fern/option" + http "net/http" +) + +type RawClient struct { + baseURL string + caller *internal.Caller + options *core.RequestOptions +} + +func NewRawClient(options *core.RequestOptions) *RawClient { + return &RawClient{ + options: options, + baseURL: options.BaseURL, + caller: internal.NewCaller( + &internal.CallerParams{ + Client: options.HTTPClient, + MaxAttempts: options.MaxAttempts, + }, + ), + } +} + +func (r *RawClient) GetSomething( + ctx context.Context, + opts ...option.RequestOption, +) (*core.Response[any], error) { + options := core.NewRequestOptions(opts...) + baseURL := internal.ResolveBaseURL( + options.BaseURL, + r.baseURL, + "", + ) + endpointURL := baseURL + "/nested/get-something" + headers := internal.MergeHeaders( + r.options.ToHeader(), + options.ToHeader(), + ) + raw, err := r.caller.Call( + ctx, + &internal.CallParams{ + URL: endpointURL, + Method: http.MethodGet, + Headers: headers, + MaxAttempts: options.MaxAttempts, + BodyProperties: options.BodyProperties, + QueryParameters: options.QueryParameters, + Client: options.HTTPClient, + }, + ) + if err != nil { + return nil, err + } + return &core.Response[any]{ + StatusCode: raw.StatusCode, + Header: raw.Header, + Body: nil, + }, nil +} diff --git a/seed/go-sdk/inferred-auth-implicit-reference/nested/client/client.go b/seed/go-sdk/inferred-auth-implicit-reference/nested/client/client.go new file mode 100644 index 000000000000..08ef81976a97 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/nested/client/client.go @@ -0,0 +1,31 @@ +// Code generated by Fern. DO NOT EDIT. + +package client + +import ( + core "github.com/inferred-auth-implicit-reference/fern/core" + internal "github.com/inferred-auth-implicit-reference/fern/internal" + api "github.com/inferred-auth-implicit-reference/fern/nested/api" +) + +type Client struct { + Api *api.Client + + options *core.RequestOptions + baseURL string + caller *internal.Caller +} + +func NewClient(options *core.RequestOptions) *Client { + return &Client{ + Api: api.NewClient(options), + options: options, + baseURL: options.BaseURL, + caller: internal.NewCaller( + &internal.CallerParams{ + Client: options.HTTPClient, + MaxAttempts: options.MaxAttempts, + }, + ), + } +} diff --git a/seed/go-sdk/inferred-auth-implicit-reference/nestednoauth/api/client.go b/seed/go-sdk/inferred-auth-implicit-reference/nestednoauth/api/client.go new file mode 100644 index 000000000000..88c103e656c5 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/nestednoauth/api/client.go @@ -0,0 +1,46 @@ +// Code generated by Fern. DO NOT EDIT. + +package api + +import ( + context "context" + core "github.com/inferred-auth-implicit-reference/fern/core" + internal "github.com/inferred-auth-implicit-reference/fern/internal" + option "github.com/inferred-auth-implicit-reference/fern/option" +) + +type Client struct { + WithRawResponse *RawClient + + options *core.RequestOptions + baseURL string + caller *internal.Caller +} + +func NewClient(options *core.RequestOptions) *Client { + return &Client{ + WithRawResponse: NewRawClient(options), + options: options, + baseURL: options.BaseURL, + caller: internal.NewCaller( + &internal.CallerParams{ + Client: options.HTTPClient, + MaxAttempts: options.MaxAttempts, + }, + ), + } +} + +func (c *Client) GetSomething( + ctx context.Context, + opts ...option.RequestOption, +) error { + _, err := c.WithRawResponse.GetSomething( + ctx, + opts..., + ) + if err != nil { + return err + } + return nil +} diff --git a/seed/go-sdk/inferred-auth-implicit-reference/nestednoauth/api/raw_client.go b/seed/go-sdk/inferred-auth-implicit-reference/nestednoauth/api/raw_client.go new file mode 100644 index 000000000000..41b2f545cca6 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/nestednoauth/api/raw_client.go @@ -0,0 +1,67 @@ +// Code generated by Fern. DO NOT EDIT. + +package api + +import ( + context "context" + core "github.com/inferred-auth-implicit-reference/fern/core" + internal "github.com/inferred-auth-implicit-reference/fern/internal" + option "github.com/inferred-auth-implicit-reference/fern/option" + http "net/http" +) + +type RawClient struct { + baseURL string + caller *internal.Caller + options *core.RequestOptions +} + +func NewRawClient(options *core.RequestOptions) *RawClient { + return &RawClient{ + options: options, + baseURL: options.BaseURL, + caller: internal.NewCaller( + &internal.CallerParams{ + Client: options.HTTPClient, + MaxAttempts: options.MaxAttempts, + }, + ), + } +} + +func (r *RawClient) GetSomething( + ctx context.Context, + opts ...option.RequestOption, +) (*core.Response[any], error) { + options := core.NewRequestOptions(opts...) + baseURL := internal.ResolveBaseURL( + options.BaseURL, + r.baseURL, + "", + ) + endpointURL := baseURL + "/nested-no-auth/get-something" + headers := internal.MergeHeaders( + r.options.ToHeader(), + options.ToHeader(), + ) + raw, err := r.caller.Call( + ctx, + &internal.CallParams{ + URL: endpointURL, + Method: http.MethodGet, + Headers: headers, + MaxAttempts: options.MaxAttempts, + BodyProperties: options.BodyProperties, + QueryParameters: options.QueryParameters, + Client: options.HTTPClient, + }, + ) + if err != nil { + return nil, err + } + return &core.Response[any]{ + StatusCode: raw.StatusCode, + Header: raw.Header, + Body: nil, + }, nil +} diff --git a/seed/go-sdk/inferred-auth-implicit-reference/nestednoauth/client/client.go b/seed/go-sdk/inferred-auth-implicit-reference/nestednoauth/client/client.go new file mode 100644 index 000000000000..4cb59263d78b --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/nestednoauth/client/client.go @@ -0,0 +1,31 @@ +// Code generated by Fern. DO NOT EDIT. + +package client + +import ( + core "github.com/inferred-auth-implicit-reference/fern/core" + internal "github.com/inferred-auth-implicit-reference/fern/internal" + api "github.com/inferred-auth-implicit-reference/fern/nestednoauth/api" +) + +type Client struct { + Api *api.Client + + options *core.RequestOptions + baseURL string + caller *internal.Caller +} + +func NewClient(options *core.RequestOptions) *Client { + return &Client{ + Api: api.NewClient(options), + options: options, + baseURL: options.BaseURL, + caller: internal.NewCaller( + &internal.CallerParams{ + Client: options.HTTPClient, + MaxAttempts: options.MaxAttempts, + }, + ), + } +} diff --git a/seed/go-sdk/inferred-auth-implicit-reference/option/request_option.go b/seed/go-sdk/inferred-auth-implicit-reference/option/request_option.go new file mode 100644 index 000000000000..9a8a1fa067e2 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/option/request_option.go @@ -0,0 +1,64 @@ +// Code generated by Fern. DO NOT EDIT. + +package option + +import ( + core "github.com/inferred-auth-implicit-reference/fern/core" + http "net/http" + url "net/url" +) + +// RequestOption adapts the behavior of an individual request. +type RequestOption = core.RequestOption + +// WithBaseURL sets the base URL, overriding the default +// environment, if any. +func WithBaseURL(baseURL string) *core.BaseURLOption { + return &core.BaseURLOption{ + BaseURL: baseURL, + } +} + +// WithHTTPClient uses the given HTTPClient to issue the request. +func WithHTTPClient(httpClient core.HTTPClient) *core.HTTPClientOption { + return &core.HTTPClientOption{ + HTTPClient: httpClient, + } +} + +// WithHTTPHeader adds the given http.Header to the request. +func WithHTTPHeader(httpHeader http.Header) *core.HTTPHeaderOption { + return &core.HTTPHeaderOption{ + // Clone the headers so they can't be modified after the option call. + HTTPHeader: httpHeader.Clone(), + } +} + +// WithBodyProperties adds the given body properties to the request. +func WithBodyProperties(bodyProperties map[string]interface{}) *core.BodyPropertiesOption { + copiedBodyProperties := make(map[string]interface{}, len(bodyProperties)) + for key, value := range bodyProperties { + copiedBodyProperties[key] = value + } + return &core.BodyPropertiesOption{ + BodyProperties: copiedBodyProperties, + } +} + +// WithQueryParameters adds the given query parameters to the request. +func WithQueryParameters(queryParameters url.Values) *core.QueryParametersOption { + copiedQueryParameters := make(url.Values, len(queryParameters)) + for key, values := range queryParameters { + copiedQueryParameters[key] = values + } + return &core.QueryParametersOption{ + QueryParameters: copiedQueryParameters, + } +} + +// WithMaxAttempts configures the maximum number of retry attempts. +func WithMaxAttempts(attempts uint) *core.MaxAttemptsOption { + return &core.MaxAttemptsOption{ + MaxAttempts: attempts, + } +} diff --git a/seed/go-sdk/inferred-auth-implicit-reference/pointer.go b/seed/go-sdk/inferred-auth-implicit-reference/pointer.go new file mode 100644 index 000000000000..ace54b751abb --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/pointer.go @@ -0,0 +1,132 @@ +package inferredauthimplicit + +import ( + "time" + + "github.com/google/uuid" +) + +// Bool returns a pointer to the given bool value. +func Bool(b bool) *bool { + return &b +} + +// Byte returns a pointer to the given byte value. +func Byte(b byte) *byte { + return &b +} + +// Complex64 returns a pointer to the given complex64 value. +func Complex64(c complex64) *complex64 { + return &c +} + +// Complex128 returns a pointer to the given complex128 value. +func Complex128(c complex128) *complex128 { + return &c +} + +// Float32 returns a pointer to the given float32 value. +func Float32(f float32) *float32 { + return &f +} + +// Float64 returns a pointer to the given float64 value. +func Float64(f float64) *float64 { + return &f +} + +// Int returns a pointer to the given int value. +func Int(i int) *int { + return &i +} + +// Int8 returns a pointer to the given int8 value. +func Int8(i int8) *int8 { + return &i +} + +// Int16 returns a pointer to the given int16 value. +func Int16(i int16) *int16 { + return &i +} + +// Int32 returns a pointer to the given int32 value. +func Int32(i int32) *int32 { + return &i +} + +// Int64 returns a pointer to the given int64 value. +func Int64(i int64) *int64 { + return &i +} + +// Rune returns a pointer to the given rune value. +func Rune(r rune) *rune { + return &r +} + +// String returns a pointer to the given string value. +func String(s string) *string { + return &s +} + +// Uint returns a pointer to the given uint value. +func Uint(u uint) *uint { + return &u +} + +// Uint8 returns a pointer to the given uint8 value. +func Uint8(u uint8) *uint8 { + return &u +} + +// Uint16 returns a pointer to the given uint16 value. +func Uint16(u uint16) *uint16 { + return &u +} + +// Uint32 returns a pointer to the given uint32 value. +func Uint32(u uint32) *uint32 { + return &u +} + +// Uint64 returns a pointer to the given uint64 value. +func Uint64(u uint64) *uint64 { + return &u +} + +// Uintptr returns a pointer to the given uintptr value. +func Uintptr(u uintptr) *uintptr { + return &u +} + +// UUID returns a pointer to the given uuid.UUID value. +func UUID(u uuid.UUID) *uuid.UUID { + return &u +} + +// Time returns a pointer to the given time.Time value. +func Time(t time.Time) *time.Time { + return &t +} + +// MustParseDate attempts to parse the given string as a +// date time.Time, and panics upon failure. +func MustParseDate(date string) time.Time { + t, err := time.Parse("2006-01-02", date) + if err != nil { + panic(err) + } + return t +} + +// MustParseDateTime attempts to parse the given string as a +// datetime time.Time, and panics upon failure. +func MustParseDateTime(datetime string) time.Time { + t, err := time.Parse(time.RFC3339, datetime) + if err != nil { + panic(err) + } + return t +} diff --git a/seed/go-sdk/inferred-auth-implicit-reference/reference.md b/seed/go-sdk/inferred-auth-implicit-reference/reference.md new file mode 100644 index 000000000000..2d67c50797c1 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/reference.md @@ -0,0 +1,191 @@ +# Reference +## Auth +
client.Auth.GetTokenWithClientCredentials(request) -> *fern.TokenResponse +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```go +request := &fern.GetTokenRequest{ + ClientId: "client_id", + ClientSecret: "client_secret", + Scope: fern.String( + "scope", + ), + } +client.Auth.GetTokenWithClientCredentials( + context.TODO(), + request, + ) +} +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `*fern.GetTokenRequest` + +
+
+
+
+ + +
+
+
+ +
client.Auth.RefreshToken(request) -> *fern.TokenResponse +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```go +request := &fern.RefreshTokenRequest{ + ClientId: "client_id", + ClientSecret: "client_secret", + RefreshToken: "refresh_token", + Scope: fern.String( + "scope", + ), + } +client.Auth.RefreshToken( + context.TODO(), + request, + ) +} +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `*fern.RefreshTokenRequest` + +
+
+
+
+ + +
+
+
+ +## NestedNoAuth Api +
client.NestedNoAuth.Api.GetSomething() -> error +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```go +client.NestedNoAuth.Api.GetSomething( + context.TODO(), + ) +} +``` +
+
+
+
+ + +
+
+
+ +## Nested Api +
client.Nested.Api.GetSomething() -> error +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```go +client.Nested.Api.GetSomething( + context.TODO(), + ) +} +``` +
+
+
+
+ + +
+
+
+ +## Simple +
client.Simple.GetSomething() -> error +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```go +client.Simple.GetSomething( + context.TODO(), + ) +} +``` +
+
+
+
+ + +
+
+
diff --git a/seed/go-sdk/inferred-auth-implicit-reference/simple/client.go b/seed/go-sdk/inferred-auth-implicit-reference/simple/client.go new file mode 100644 index 000000000000..e2c47382bddf --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/simple/client.go @@ -0,0 +1,46 @@ +// Code generated by Fern. DO NOT EDIT. + +package simple + +import ( + context "context" + core "github.com/inferred-auth-implicit-reference/fern/core" + internal "github.com/inferred-auth-implicit-reference/fern/internal" + option "github.com/inferred-auth-implicit-reference/fern/option" +) + +type Client struct { + WithRawResponse *RawClient + + options *core.RequestOptions + baseURL string + caller *internal.Caller +} + +func NewClient(options *core.RequestOptions) *Client { + return &Client{ + WithRawResponse: NewRawClient(options), + options: options, + baseURL: options.BaseURL, + caller: internal.NewCaller( + &internal.CallerParams{ + Client: options.HTTPClient, + MaxAttempts: options.MaxAttempts, + }, + ), + } +} + +func (c *Client) GetSomething( + ctx context.Context, + opts ...option.RequestOption, +) error { + _, err := c.WithRawResponse.GetSomething( + ctx, + opts..., + ) + if err != nil { + return err + } + return nil +} diff --git a/seed/go-sdk/inferred-auth-implicit-reference/simple/raw_client.go b/seed/go-sdk/inferred-auth-implicit-reference/simple/raw_client.go new file mode 100644 index 000000000000..1282e3041f81 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/simple/raw_client.go @@ -0,0 +1,67 @@ +// Code generated by Fern. DO NOT EDIT. + +package simple + +import ( + context "context" + core "github.com/inferred-auth-implicit-reference/fern/core" + internal "github.com/inferred-auth-implicit-reference/fern/internal" + option "github.com/inferred-auth-implicit-reference/fern/option" + http "net/http" +) + +type RawClient struct { + baseURL string + caller *internal.Caller + options *core.RequestOptions +} + +func NewRawClient(options *core.RequestOptions) *RawClient { + return &RawClient{ + options: options, + baseURL: options.BaseURL, + caller: internal.NewCaller( + &internal.CallerParams{ + Client: options.HTTPClient, + MaxAttempts: options.MaxAttempts, + }, + ), + } +} + +func (r *RawClient) GetSomething( + ctx context.Context, + opts ...option.RequestOption, +) (*core.Response[any], error) { + options := core.NewRequestOptions(opts...) + baseURL := internal.ResolveBaseURL( + options.BaseURL, + r.baseURL, + "", + ) + endpointURL := baseURL + "/get-something" + headers := internal.MergeHeaders( + r.options.ToHeader(), + options.ToHeader(), + ) + raw, err := r.caller.Call( + ctx, + &internal.CallParams{ + URL: endpointURL, + Method: http.MethodGet, + Headers: headers, + MaxAttempts: options.MaxAttempts, + BodyProperties: options.BodyProperties, + QueryParameters: options.QueryParameters, + Client: options.HTTPClient, + }, + ) + if err != nil { + return nil, err + } + return &core.Response[any]{ + StatusCode: raw.StatusCode, + Header: raw.Header, + Body: nil, + }, nil +} diff --git a/seed/go-sdk/inferred-auth-implicit-reference/snippet.json b/seed/go-sdk/inferred-auth-implicit-reference/snippet.json new file mode 100644 index 000000000000..52e7563e99a4 --- /dev/null +++ b/seed/go-sdk/inferred-auth-implicit-reference/snippet.json @@ -0,0 +1,59 @@ +{ + "endpoints": [ + { + "id": { + "path": "/get-something", + "method": "GET", + "identifier_override": "endpoint_simple.getSomething" + }, + "snippet": { + "type": "go", + "client": "import (\n\tcontext \"context\"\n\tfernclient \"github.com/inferred-auth-implicit-reference/fern/client\"\n)\n\nclient := fernclient.NewClient()\nerr := client.Simple.GetSomething(\n\tcontext.TODO(),\n)\n" + } + }, + { + "id": { + "path": "/nested-no-auth/get-something", + "method": "GET", + "identifier_override": "endpoint_nested-no-auth/api.getSomething" + }, + "snippet": { + "type": "go", + "client": "import (\n\tcontext \"context\"\n\tfernclient \"github.com/inferred-auth-implicit-reference/fern/client\"\n)\n\nclient := fernclient.NewClient()\nerr := client.NestedNoAuth.Api.GetSomething(\n\tcontext.TODO(),\n)\n" + } + }, + { + "id": { + "path": "/nested/get-something", + "method": "GET", + "identifier_override": "endpoint_nested/api.getSomething" + }, + "snippet": { + "type": "go", + "client": "import (\n\tcontext \"context\"\n\tfernclient \"github.com/inferred-auth-implicit-reference/fern/client\"\n)\n\nclient := fernclient.NewClient()\nerr := client.Nested.Api.GetSomething(\n\tcontext.TODO(),\n)\n" + } + }, + { + "id": { + "path": "/token", + "method": "POST", + "identifier_override": "endpoint_auth.getTokenWithClientCredentials" + }, + "snippet": { + "type": "go", + "client": "import (\n\tcontext \"context\"\n\tfern \"github.com/inferred-auth-implicit-reference/fern\"\n\tfernclient \"github.com/inferred-auth-implicit-reference/fern/client\"\n)\n\nclient := fernclient.NewClient()\nresponse, err := client.Auth.GetTokenWithClientCredentials(\n\tcontext.TODO(),\n\t\u0026fern.GetTokenRequest{\n\t\tClientId: \"client_id\",\n\t\tClientSecret: \"client_secret\",\n\t\tScope: fern.String(\n\t\t\t\"scope\",\n\t\t),\n\t},\n)\n" + } + }, + { + "id": { + "path": "/token/refresh", + "method": "POST", + "identifier_override": "endpoint_auth.refreshToken" + }, + "snippet": { + "type": "go", + "client": "import (\n\tcontext \"context\"\n\tfern \"github.com/inferred-auth-implicit-reference/fern\"\n\tfernclient \"github.com/inferred-auth-implicit-reference/fern/client\"\n)\n\nclient := fernclient.NewClient()\nresponse, err := client.Auth.RefreshToken(\n\tcontext.TODO(),\n\t\u0026fern.RefreshTokenRequest{\n\t\tClientId: \"client_id\",\n\t\tClientSecret: \"client_secret\",\n\t\tRefreshToken: \"refresh_token\",\n\t\tScope: fern.String(\n\t\t\t\"scope\",\n\t\t),\n\t},\n)\n" + } + } + ] +} \ No newline at end of file diff --git a/seed/go-sdk/inferred-auth-implicit/dynamic-snippets/example0/snippet.go b/seed/go-sdk/inferred-auth-implicit/dynamic-snippets/example0/snippet.go index b03fbb7c2409..0fd9eec512cf 100644 --- a/seed/go-sdk/inferred-auth-implicit/dynamic-snippets/example0/snippet.go +++ b/seed/go-sdk/inferred-auth-implicit/dynamic-snippets/example0/snippet.go @@ -12,6 +12,7 @@ func do() { option.WithBaseURL( "https://api.fern.com", ), + nil, ) request := &fern.GetTokenRequest{ XApiKey: "X-Api-Key", diff --git a/seed/go-sdk/inferred-auth-implicit/dynamic-snippets/example1/snippet.go b/seed/go-sdk/inferred-auth-implicit/dynamic-snippets/example1/snippet.go index 8f132d2086a9..46593f414772 100644 --- a/seed/go-sdk/inferred-auth-implicit/dynamic-snippets/example1/snippet.go +++ b/seed/go-sdk/inferred-auth-implicit/dynamic-snippets/example1/snippet.go @@ -12,6 +12,7 @@ func do() { option.WithBaseURL( "https://api.fern.com", ), + nil, ) request := &fern.RefreshTokenRequest{ XApiKey: "X-Api-Key", diff --git a/seed/go-sdk/inferred-auth-implicit/dynamic-snippets/example2/snippet.go b/seed/go-sdk/inferred-auth-implicit/dynamic-snippets/example2/snippet.go index de491792536a..c3588259b04d 100644 --- a/seed/go-sdk/inferred-auth-implicit/dynamic-snippets/example2/snippet.go +++ b/seed/go-sdk/inferred-auth-implicit/dynamic-snippets/example2/snippet.go @@ -11,6 +11,7 @@ func do() { option.WithBaseURL( "https://api.fern.com", ), + nil, ) client.NestedNoAuth.Api.GetSomething( context.TODO(), diff --git a/seed/go-sdk/inferred-auth-implicit/dynamic-snippets/example3/snippet.go b/seed/go-sdk/inferred-auth-implicit/dynamic-snippets/example3/snippet.go index 46ec506d374b..bab4ad88283c 100644 --- a/seed/go-sdk/inferred-auth-implicit/dynamic-snippets/example3/snippet.go +++ b/seed/go-sdk/inferred-auth-implicit/dynamic-snippets/example3/snippet.go @@ -11,6 +11,7 @@ func do() { option.WithBaseURL( "https://api.fern.com", ), + nil, ) client.Nested.Api.GetSomething( context.TODO(), diff --git a/seed/go-sdk/inferred-auth-implicit/dynamic-snippets/example4/snippet.go b/seed/go-sdk/inferred-auth-implicit/dynamic-snippets/example4/snippet.go index feda86342230..30ea661676a0 100644 --- a/seed/go-sdk/inferred-auth-implicit/dynamic-snippets/example4/snippet.go +++ b/seed/go-sdk/inferred-auth-implicit/dynamic-snippets/example4/snippet.go @@ -11,6 +11,7 @@ func do() { option.WithBaseURL( "https://api.fern.com", ), + nil, ) client.Simple.GetSomething( context.TODO(), diff --git a/seed/go-sdk/oauth-client-credentials-custom/client/client.go b/seed/go-sdk/oauth-client-credentials-custom/client/client.go index 1e389c3c462c..5e8c89348000 100644 --- a/seed/go-sdk/oauth-client-credentials-custom/client/client.go +++ b/seed/go-sdk/oauth-client-credentials-custom/client/client.go @@ -4,6 +4,7 @@ package client import ( context "context" + errors "errors" fern "github.com/oauth-client-credentials-custom/fern" auth "github.com/oauth-client-credentials-custom/fern/auth" core "github.com/oauth-client-credentials-custom/fern/core" @@ -36,18 +37,25 @@ func NewClient(opts ...option.RequestOption) *Client { &authOptions, ) options.SetTokenGetter(func() (string, error) { - if token := oauthTokenProvider.GetToken(); token != "" { - return token, nil - } - response, err := authClient.GetTokenWithClientCredentials(context.Background(), &fern.GetTokenRequest{ - Cid: options.ClientID, - Csr: options.ClientSecret, + return oauthTokenProvider.GetOrFetch(func() (string, int, error) { + response, err := authClient.GetTokenWithClientCredentials(context.Background(), &fern.GetTokenRequest{ + Cid: options.ClientID, + Csr: options.ClientSecret, + }) + if err != nil { + return "", 0, err + } + if response.AccessToken == "" { + return "", 0, errors.New( + "oauth response missing access token", + ) + } + expiresIn := core.DefaultExpirySeconds + if response.ExpiresIn > 0 { + expiresIn = response.ExpiresIn + } + return response.AccessToken, expiresIn, nil }) - if err != nil { - return "", err - } - oauthTokenProvider.SetToken(response.AccessToken, response.ExpiresIn) - return response.AccessToken, nil }) return &Client{ Auth: auth.NewClient(options), diff --git a/seed/go-sdk/oauth-client-credentials-custom/core/oauth.go b/seed/go-sdk/oauth-client-credentials-custom/core/oauth.go index 114b1f412ec7..9b6f34cc8e79 100644 --- a/seed/go-sdk/oauth-client-credentials-custom/core/oauth.go +++ b/seed/go-sdk/oauth-client-credentials-custom/core/oauth.go @@ -9,6 +9,9 @@ const ( // expirationBufferMinutes is subtracted from the token expiration time // to ensure we refresh the token before it actually expires. expirationBufferMinutes = 2 + + // DefaultExpirySeconds is used when the OAuth response doesn't include an expires_in value. + DefaultExpirySeconds = 3600 // 1 hour fallback ) // OAuthTokenProvider manages OAuth access tokens, including caching and automatic refresh. @@ -19,6 +22,9 @@ type OAuthTokenProvider struct { mu sync.Mutex accessToken string expiresAt time.Time + + // fetchMu ensures only one goroutine fetches a new token at a time + fetchMu sync.Mutex } // NewOAuthTokenProvider creates a new OAuthTokenProvider with the given credentials. @@ -73,6 +79,34 @@ func (o *OAuthTokenProvider) GetToken() string { return o.accessToken } +// GetOrFetch returns a valid token, fetching a new one if necessary. +// The fetchFunc is called at most once even if multiple goroutines call GetOrFetch +// concurrently when the token is expired. It should return (accessToken, expiresInSeconds, error). +func (o *OAuthTokenProvider) GetOrFetch(fetchFunc func() (string, int, error)) (string, error) { + // Fast path: check if we have a valid token + if token := o.GetToken(); token != "" { + return token, nil + } + + // Slow path: acquire fetch lock to ensure only one goroutine fetches + o.fetchMu.Lock() + defer o.fetchMu.Unlock() + + // Double-check after acquiring lock (another goroutine may have fetched) + if token := o.GetToken(); token != "" { + return token, nil + } + + // Fetch new token + accessToken, expiresIn, err := fetchFunc() + if err != nil { + return "", err + } + + o.SetToken(accessToken, expiresIn) + return accessToken, nil +} + // NeedsRefresh returns true if the token needs to be refreshed. func (o *OAuthTokenProvider) NeedsRefresh() bool { o.mu.Lock() diff --git a/seed/go-sdk/oauth-client-credentials-default/client/client.go b/seed/go-sdk/oauth-client-credentials-default/client/client.go index 717f375accad..cecf1bc4cb73 100644 --- a/seed/go-sdk/oauth-client-credentials-default/client/client.go +++ b/seed/go-sdk/oauth-client-credentials-default/client/client.go @@ -4,6 +4,7 @@ package client import ( context "context" + errors "errors" fern "github.com/oauth-client-credentials-default/fern" auth "github.com/oauth-client-credentials-default/fern/auth" core "github.com/oauth-client-credentials-default/fern/core" @@ -36,18 +37,25 @@ func NewClient(opts ...option.RequestOption) *Client { &authOptions, ) options.SetTokenGetter(func() (string, error) { - if token := oauthTokenProvider.GetToken(); token != "" { - return token, nil - } - response, err := authClient.GetToken(context.Background(), &fern.GetTokenRequest{ - ClientId: options.ClientID, - ClientSecret: options.ClientSecret, + return oauthTokenProvider.GetOrFetch(func() (string, int, error) { + response, err := authClient.GetToken(context.Background(), &fern.GetTokenRequest{ + ClientId: options.ClientID, + ClientSecret: options.ClientSecret, + }) + if err != nil { + return "", 0, err + } + if response.AccessToken == "" { + return "", 0, errors.New( + "oauth response missing access token", + ) + } + expiresIn := core.DefaultExpirySeconds + if response.ExpiresIn > 0 { + expiresIn = response.ExpiresIn + } + return response.AccessToken, expiresIn, nil }) - if err != nil { - return "", err - } - oauthTokenProvider.SetToken(response.AccessToken, response.ExpiresIn) - return response.AccessToken, nil }) return &Client{ Auth: auth.NewClient(options), diff --git a/seed/go-sdk/oauth-client-credentials-default/core/oauth.go b/seed/go-sdk/oauth-client-credentials-default/core/oauth.go index 114b1f412ec7..9b6f34cc8e79 100644 --- a/seed/go-sdk/oauth-client-credentials-default/core/oauth.go +++ b/seed/go-sdk/oauth-client-credentials-default/core/oauth.go @@ -9,6 +9,9 @@ const ( // expirationBufferMinutes is subtracted from the token expiration time // to ensure we refresh the token before it actually expires. expirationBufferMinutes = 2 + + // DefaultExpirySeconds is used when the OAuth response doesn't include an expires_in value. + DefaultExpirySeconds = 3600 // 1 hour fallback ) // OAuthTokenProvider manages OAuth access tokens, including caching and automatic refresh. @@ -19,6 +22,9 @@ type OAuthTokenProvider struct { mu sync.Mutex accessToken string expiresAt time.Time + + // fetchMu ensures only one goroutine fetches a new token at a time + fetchMu sync.Mutex } // NewOAuthTokenProvider creates a new OAuthTokenProvider with the given credentials. @@ -73,6 +79,34 @@ func (o *OAuthTokenProvider) GetToken() string { return o.accessToken } +// GetOrFetch returns a valid token, fetching a new one if necessary. +// The fetchFunc is called at most once even if multiple goroutines call GetOrFetch +// concurrently when the token is expired. It should return (accessToken, expiresInSeconds, error). +func (o *OAuthTokenProvider) GetOrFetch(fetchFunc func() (string, int, error)) (string, error) { + // Fast path: check if we have a valid token + if token := o.GetToken(); token != "" { + return token, nil + } + + // Slow path: acquire fetch lock to ensure only one goroutine fetches + o.fetchMu.Lock() + defer o.fetchMu.Unlock() + + // Double-check after acquiring lock (another goroutine may have fetched) + if token := o.GetToken(); token != "" { + return token, nil + } + + // Fetch new token + accessToken, expiresIn, err := fetchFunc() + if err != nil { + return "", err + } + + o.SetToken(accessToken, expiresIn) + return accessToken, nil +} + // NeedsRefresh returns true if the token needs to be refreshed. func (o *OAuthTokenProvider) NeedsRefresh() bool { o.mu.Lock() diff --git a/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/README.md b/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/README.md index 12d3e7cc1a7a..ab70c720ed94 100644 --- a/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/README.md +++ b/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/README.md @@ -9,6 +9,7 @@ The Seed Go library provides convenient access to the Seed APIs from Go. - [Reference](#reference) - [Usage](#usage) - [Environments](#environments) +- [Oauth](#oauth) - [Errors](#errors) - [Request Options](#request-options) - [Advanced](#advanced) @@ -31,13 +32,17 @@ package example import ( client "github.com/oauth-client-credentials-mandatory-auth/fern/client" + option "github.com/oauth-client-credentials-mandatory-auth/fern/option" fern "github.com/oauth-client-credentials-mandatory-auth/fern" context "context" ) func do() { client := client.NewClient( - nil, + option.WithClientCredentials( + "", + "", + ), ) request := &fern.GetTokenRequest{ ClientId: "my_oauth_app_123", @@ -64,6 +69,31 @@ client := client.NewClient( ) ``` +## Oauth + +This SDK supports OAuth 2.0 authentication. You have two options for providing credentials: + +**Option 1: Client Credentials** - Provide your client ID and secret, and the SDK will automatically handle +token fetching and refresh: + +**Option 2: Direct Token** - If you already have an access token (e.g., obtained through your own OAuth flow), +you can provide it directly: + +```go +// Option 1: Use client credentials (SDK will handle token fetching and refresh) +client := client.NewClient( + option.WithClientCredentials( + "", + "", + ), +) + +// Option 2: Use a pre-fetched token directly +client := client.NewClient( + option.WithToken(""), +) +``` + ## Errors Structured error types are returned from API calls that return non-success status codes. These errors are compatible diff --git a/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/client/client.go b/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/client/client.go index ae7368307cec..1164bbbc2e2e 100644 --- a/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/client/client.go +++ b/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/client/client.go @@ -3,6 +3,9 @@ package client import ( + context "context" + errors "errors" + fern "github.com/oauth-client-credentials-mandatory-auth/fern" auth "github.com/oauth-client-credentials-mandatory-auth/fern/auth" core "github.com/oauth-client-credentials-mandatory-auth/fern/core" internal "github.com/oauth-client-credentials-mandatory-auth/fern/internal" @@ -23,6 +26,35 @@ type Client struct { func NewClient(opts ...option.RequestOption) *Client { options := core.NewRequestOptions(opts...) + oauthTokenProvider := core.NewOAuthTokenProvider( + options.ClientID, + options.ClientSecret, + ) + authOptions := *options + authClient := auth.NewClient( + &authOptions, + ) + options.SetTokenGetter(func() (string, error) { + return oauthTokenProvider.GetOrFetch(func() (string, int, error) { + response, err := authClient.GetTokenWithClientCredentials(context.Background(), &fern.GetTokenRequest{ + ClientId: options.ClientID, + ClientSecret: options.ClientSecret, + }) + if err != nil { + return "", 0, err + } + if response.AccessToken == "" { + return "", 0, errors.New( + "oauth response missing access token", + ) + } + expiresIn := core.DefaultExpirySeconds + if response.ExpiresIn > 0 { + expiresIn = response.ExpiresIn + } + return response.AccessToken, expiresIn, nil + }) + }) return &Client{ Auth: auth.NewClient(options), Nested: client.NewClient(options), diff --git a/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/core/oauth.go b/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/core/oauth.go new file mode 100644 index 000000000000..9b6f34cc8e79 --- /dev/null +++ b/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/core/oauth.go @@ -0,0 +1,129 @@ +package core + +import ( + "sync" + "time" +) + +const ( + // expirationBufferMinutes is subtracted from the token expiration time + // to ensure we refresh the token before it actually expires. + expirationBufferMinutes = 2 + + // DefaultExpirySeconds is used when the OAuth response doesn't include an expires_in value. + DefaultExpirySeconds = 3600 // 1 hour fallback +) + +// OAuthTokenProvider manages OAuth access tokens, including caching and automatic refresh. +type OAuthTokenProvider struct { + clientID string + clientSecret string + + mu sync.Mutex + accessToken string + expiresAt time.Time + + // fetchMu ensures only one goroutine fetches a new token at a time + fetchMu sync.Mutex +} + +// NewOAuthTokenProvider creates a new OAuthTokenProvider with the given credentials. +func NewOAuthTokenProvider(clientID string, clientSecret string) *OAuthTokenProvider { + return &OAuthTokenProvider{ + clientID: clientID, + clientSecret: clientSecret, + } +} + +// ClientID returns the client ID. +func (o *OAuthTokenProvider) ClientID() string { + return o.clientID +} + +// ClientSecret returns the client secret. +func (o *OAuthTokenProvider) ClientSecret() string { + return o.clientSecret +} + +// SetToken sets the cached access token and its expiration time. +// The expiresIn parameter is the number of seconds until the token expires. +func (o *OAuthTokenProvider) SetToken(accessToken string, expiresIn int) { + o.mu.Lock() + defer o.mu.Unlock() + o.accessToken = accessToken + if expiresIn > 0 { + // Apply buffer to refresh before actual expiration + bufferSeconds := expirationBufferMinutes * 60 + effectiveExpiresIn := expiresIn - bufferSeconds + if effectiveExpiresIn < 0 { + effectiveExpiresIn = 0 + } + o.expiresAt = time.Now().Add(time.Duration(effectiveExpiresIn) * time.Second) + } else { + // No expiration info, token won't auto-refresh based on time + o.expiresAt = time.Time{} + } +} + +// GetToken returns the cached access token if it's still valid. +// Returns an empty string if the token is expired or not set. +func (o *OAuthTokenProvider) GetToken() string { + o.mu.Lock() + defer o.mu.Unlock() + if o.accessToken == "" { + return "" + } + if !o.expiresAt.IsZero() && time.Now().After(o.expiresAt) { + return "" + } + return o.accessToken +} + +// GetOrFetch returns a valid token, fetching a new one if necessary. +// The fetchFunc is called at most once even if multiple goroutines call GetOrFetch +// concurrently when the token is expired. It should return (accessToken, expiresInSeconds, error). +func (o *OAuthTokenProvider) GetOrFetch(fetchFunc func() (string, int, error)) (string, error) { + // Fast path: check if we have a valid token + if token := o.GetToken(); token != "" { + return token, nil + } + + // Slow path: acquire fetch lock to ensure only one goroutine fetches + o.fetchMu.Lock() + defer o.fetchMu.Unlock() + + // Double-check after acquiring lock (another goroutine may have fetched) + if token := o.GetToken(); token != "" { + return token, nil + } + + // Fetch new token + accessToken, expiresIn, err := fetchFunc() + if err != nil { + return "", err + } + + o.SetToken(accessToken, expiresIn) + return accessToken, nil +} + +// NeedsRefresh returns true if the token needs to be refreshed. +func (o *OAuthTokenProvider) NeedsRefresh() bool { + o.mu.Lock() + defer o.mu.Unlock() + if o.accessToken == "" { + return true + } + if !o.expiresAt.IsZero() && time.Now().After(o.expiresAt) { + return true + } + return false +} + +// Reset clears the cached token. +func (o *OAuthTokenProvider) Reset() { + o.mu.Lock() + defer o.mu.Unlock() + o.accessToken = "" + o.expiresAt = time.Time{} +} diff --git a/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/core/request_option.go b/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/core/request_option.go index 9597ebe6f026..d3921c3d114b 100644 --- a/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/core/request_option.go +++ b/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/core/request_option.go @@ -12,6 +12,9 @@ type RequestOption interface { applyRequestOptions(*RequestOptions) } +// TokenGetter is a function that returns an access token. +type TokenGetter func() (string, error) + // RequestOptions defines all of the possible request options. // // This type is primarily used by the generated code and is not meant @@ -23,6 +26,10 @@ type RequestOptions struct { BodyProperties map[string]interface{} QueryParameters url.Values MaxAttempts uint + tokenGetter TokenGetter + ClientID string + ClientSecret string + Token string } // NewRequestOptions returns a new *RequestOptions value. @@ -45,6 +52,14 @@ func NewRequestOptions(opts ...RequestOption) *RequestOptions { // for the request(s). func (r *RequestOptions) ToHeader() http.Header { header := r.cloneHeader() + if r.Token != "" { + header.Set("Authorization", "Bearer "+r.Token) + } + if header.Get("Authorization") == "" && r.tokenGetter != nil { + if token, err := r.tokenGetter(); err == nil && token != "" { + header.Set("Authorization", "Bearer "+token) + } + } return header } @@ -110,3 +125,47 @@ type MaxAttemptsOption struct { func (m *MaxAttemptsOption) applyRequestOptions(opts *RequestOptions) { opts.MaxAttempts = m.MaxAttempts } + +// ClientIDOption implements the RequestOption interface. +type ClientIDOption struct { + ClientID string +} + +func (c *ClientIDOption) applyRequestOptions(opts *RequestOptions) { + opts.ClientID = c.ClientID +} + +// ClientSecretOption implements the RequestOption interface. +type ClientSecretOption struct { + ClientSecret string +} + +func (c *ClientSecretOption) applyRequestOptions(opts *RequestOptions) { + opts.ClientSecret = c.ClientSecret +} + +// ClientCredentialsOption implements the RequestOption interface. +type ClientCredentialsOption struct { + ClientID string + ClientSecret string +} + +func (c *ClientCredentialsOption) applyRequestOptions(opts *RequestOptions) { + opts.ClientID = c.ClientID + opts.ClientSecret = c.ClientSecret +} + +// TokenOption implements the RequestOption interface. +type TokenOption struct { + Token string +} + +func (t *TokenOption) applyRequestOptions(opts *RequestOptions) { + opts.Token = t.Token +} + +// SetTokenGetter sets the token getter function for OAuth. +// This is an internal method and should not be called directly. +func (r *RequestOptions) SetTokenGetter(getter TokenGetter) { + r.tokenGetter = getter +} diff --git a/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/dynamic-snippets/example0/snippet.go b/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/dynamic-snippets/example0/snippet.go index 8252c4f7afca..8f69c088fd63 100644 --- a/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/dynamic-snippets/example0/snippet.go +++ b/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/dynamic-snippets/example0/snippet.go @@ -12,7 +12,10 @@ func do() { option.WithBaseURL( "https://api.fern.com", ), - nil, + option.WithClientCredentials( + "", + "", + ), ) request := &fern.GetTokenRequest{ ClientId: "my_oauth_app_123", diff --git a/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/dynamic-snippets/example1/snippet.go b/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/dynamic-snippets/example1/snippet.go index ea26f35693b1..d46045f55304 100644 --- a/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/dynamic-snippets/example1/snippet.go +++ b/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/dynamic-snippets/example1/snippet.go @@ -12,7 +12,10 @@ func do() { option.WithBaseURL( "https://api.fern.com", ), - nil, + option.WithClientCredentials( + "", + "", + ), ) request := &fern.GetTokenRequest{ ClientId: "client_id", diff --git a/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/dynamic-snippets/example2/snippet.go b/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/dynamic-snippets/example2/snippet.go index 6c4cd8728f10..07d2c7c7c37c 100644 --- a/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/dynamic-snippets/example2/snippet.go +++ b/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/dynamic-snippets/example2/snippet.go @@ -12,7 +12,10 @@ func do() { option.WithBaseURL( "https://api.fern.com", ), - nil, + option.WithClientCredentials( + "", + "", + ), ) request := &fern.RefreshTokenRequest{ ClientId: "my_oauth_app_123", diff --git a/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/dynamic-snippets/example3/snippet.go b/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/dynamic-snippets/example3/snippet.go index 497953128d67..9cb0feabc117 100644 --- a/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/dynamic-snippets/example3/snippet.go +++ b/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/dynamic-snippets/example3/snippet.go @@ -12,7 +12,10 @@ func do() { option.WithBaseURL( "https://api.fern.com", ), - nil, + option.WithClientCredentials( + "", + "", + ), ) request := &fern.RefreshTokenRequest{ ClientId: "client_id", diff --git a/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/dynamic-snippets/example4/snippet.go b/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/dynamic-snippets/example4/snippet.go index bca26e33215b..e1fc97026050 100644 --- a/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/dynamic-snippets/example4/snippet.go +++ b/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/dynamic-snippets/example4/snippet.go @@ -11,7 +11,10 @@ func do() { option.WithBaseURL( "https://api.fern.com", ), - nil, + option.WithClientCredentials( + "", + "", + ), ) client.Nested.Api.GetSomething( context.TODO(), diff --git a/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/dynamic-snippets/example5/snippet.go b/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/dynamic-snippets/example5/snippet.go index 2be2360e0902..294dbf131f5e 100644 --- a/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/dynamic-snippets/example5/snippet.go +++ b/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/dynamic-snippets/example5/snippet.go @@ -11,7 +11,10 @@ func do() { option.WithBaseURL( "https://api.fern.com", ), - nil, + option.WithClientCredentials( + "", + "", + ), ) client.Simple.GetSomething( context.TODO(), diff --git a/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/option/request_option.go b/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/option/request_option.go index 72378275954e..294f1bc61de7 100644 --- a/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/option/request_option.go +++ b/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/option/request_option.go @@ -62,3 +62,33 @@ func WithMaxAttempts(attempts uint) *core.MaxAttemptsOption { MaxAttempts: attempts, } } + +// WithClientID sets the clientID auth request parameter. +func WithClientID(clientID string) *core.ClientIDOption { + return &core.ClientIDOption{ + ClientID: clientID, + } +} + +// WithClientSecret sets the clientSecret auth request parameter. +func WithClientSecret(clientSecret string) *core.ClientSecretOption { + return &core.ClientSecretOption{ + ClientSecret: clientSecret, + } +} + +// WithClientCredentials sets both the clientID and clientSecret auth request parameters. +func WithClientCredentials(clientID string, clientSecret string) *core.ClientCredentialsOption { + return &core.ClientCredentialsOption{ + ClientID: clientID, + ClientSecret: clientSecret, + } +} + +// WithToken sets the OAuth token directly, bypassing the client credentials flow. +// Use this when you already have an access token. +func WithToken(token string) *core.TokenOption { + return &core.TokenOption{ + Token: token, + } +} diff --git a/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/snippet.json b/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/snippet.json index 84c832a5e14b..011892656e04 100644 --- a/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/snippet.json +++ b/seed/go-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/snippet.json @@ -8,7 +8,7 @@ }, "snippet": { "type": "go", - "client": "import (\n\tcontext \"context\"\n\tfernclient \"github.com/oauth-client-credentials-mandatory-auth/fern/client\"\n)\n\nclient := fernclient.NewClient()\nerr := client.Simple.GetSomething(\n\tcontext.TODO(),\n)\n" + "client": "import (\n\tcontext \"context\"\n\tfernclient \"github.com/oauth-client-credentials-mandatory-auth/fern/client\"\n\toption \"github.com/oauth-client-credentials-mandatory-auth/fern/option\"\n)\n\nclient := fernclient.NewClient(\n\toption.WithClientCredentials(\n\t\t\"\u003cYOUR_CLIENT_ID\u003e\",\n\t\t\"\u003cYOUR_CLIENT_SECRET\u003e\",\n\t),\n)\nerr := client.Simple.GetSomething(\n\tcontext.TODO(),\n)\n" } }, { @@ -19,7 +19,7 @@ }, "snippet": { "type": "go", - "client": "import (\n\tcontext \"context\"\n\tfernclient \"github.com/oauth-client-credentials-mandatory-auth/fern/client\"\n)\n\nclient := fernclient.NewClient()\nerr := client.Nested.Api.GetSomething(\n\tcontext.TODO(),\n)\n" + "client": "import (\n\tcontext \"context\"\n\tfernclient \"github.com/oauth-client-credentials-mandatory-auth/fern/client\"\n\toption \"github.com/oauth-client-credentials-mandatory-auth/fern/option\"\n)\n\nclient := fernclient.NewClient(\n\toption.WithClientCredentials(\n\t\t\"\u003cYOUR_CLIENT_ID\u003e\",\n\t\t\"\u003cYOUR_CLIENT_SECRET\u003e\",\n\t),\n)\nerr := client.Nested.Api.GetSomething(\n\tcontext.TODO(),\n)\n" } }, { @@ -30,7 +30,7 @@ }, "snippet": { "type": "go", - "client": "import (\n\tcontext \"context\"\n\tfern \"github.com/oauth-client-credentials-mandatory-auth/fern\"\n\tfernclient \"github.com/oauth-client-credentials-mandatory-auth/fern/client\"\n)\n\nclient := fernclient.NewClient()\nresponse, err := client.Auth.GetTokenWithClientCredentials(\n\tcontext.TODO(),\n\t\u0026fern.GetTokenRequest{\n\t\tClientId: \"my_oauth_app_123\",\n\t\tClientSecret: \"sk_live_abcdef123456789\",\n\t},\n)\n" + "client": "import (\n\tcontext \"context\"\n\tfern \"github.com/oauth-client-credentials-mandatory-auth/fern\"\n\tfernclient \"github.com/oauth-client-credentials-mandatory-auth/fern/client\"\n\toption \"github.com/oauth-client-credentials-mandatory-auth/fern/option\"\n)\n\nclient := fernclient.NewClient(\n\toption.WithClientCredentials(\n\t\t\"\u003cYOUR_CLIENT_ID\u003e\",\n\t\t\"\u003cYOUR_CLIENT_SECRET\u003e\",\n\t),\n)\nresponse, err := client.Auth.GetTokenWithClientCredentials(\n\tcontext.TODO(),\n\t\u0026fern.GetTokenRequest{\n\t\tClientId: \"my_oauth_app_123\",\n\t\tClientSecret: \"sk_live_abcdef123456789\",\n\t},\n)\n" } }, { @@ -41,7 +41,7 @@ }, "snippet": { "type": "go", - "client": "import (\n\tcontext \"context\"\n\tfern \"github.com/oauth-client-credentials-mandatory-auth/fern\"\n\tfernclient \"github.com/oauth-client-credentials-mandatory-auth/fern/client\"\n)\n\nclient := fernclient.NewClient()\nresponse, err := client.Auth.RefreshToken(\n\tcontext.TODO(),\n\t\u0026fern.RefreshTokenRequest{\n\t\tClientId: \"my_oauth_app_123\",\n\t\tClientSecret: \"sk_live_abcdef123456789\",\n\t\tRefreshToken: \"refresh_token\",\n\t},\n)\n" + "client": "import (\n\tcontext \"context\"\n\tfern \"github.com/oauth-client-credentials-mandatory-auth/fern\"\n\tfernclient \"github.com/oauth-client-credentials-mandatory-auth/fern/client\"\n\toption \"github.com/oauth-client-credentials-mandatory-auth/fern/option\"\n)\n\nclient := fernclient.NewClient(\n\toption.WithClientCredentials(\n\t\t\"\u003cYOUR_CLIENT_ID\u003e\",\n\t\t\"\u003cYOUR_CLIENT_SECRET\u003e\",\n\t),\n)\nresponse, err := client.Auth.RefreshToken(\n\tcontext.TODO(),\n\t\u0026fern.RefreshTokenRequest{\n\t\tClientId: \"my_oauth_app_123\",\n\t\tClientSecret: \"sk_live_abcdef123456789\",\n\t\tRefreshToken: \"refresh_token\",\n\t},\n)\n" } } ] diff --git a/seed/go-sdk/oauth-client-credentials-nested-root/client/client.go b/seed/go-sdk/oauth-client-credentials-nested-root/client/client.go index 51e4648947cf..2181f7b344a5 100644 --- a/seed/go-sdk/oauth-client-credentials-nested-root/client/client.go +++ b/seed/go-sdk/oauth-client-credentials-nested-root/client/client.go @@ -4,6 +4,7 @@ package client import ( context "context" + errors "errors" auth "github.com/oauth-client-credentials-nested-root/fern/auth" client "github.com/oauth-client-credentials-nested-root/fern/auth/client" core "github.com/oauth-client-credentials-nested-root/fern/core" @@ -36,18 +37,25 @@ func NewClient(opts ...option.RequestOption) *Client { &authOptions, ) options.SetTokenGetter(func() (string, error) { - if token := oauthTokenProvider.GetToken(); token != "" { - return token, nil - } - response, err := authClient.GetToken(context.Background(), &auth.GetTokenRequest{ - ClientId: options.ClientID, - ClientSecret: options.ClientSecret, + return oauthTokenProvider.GetOrFetch(func() (string, int, error) { + response, err := authClient.GetToken(context.Background(), &auth.GetTokenRequest{ + ClientId: options.ClientID, + ClientSecret: options.ClientSecret, + }) + if err != nil { + return "", 0, err + } + if response.AccessToken == "" { + return "", 0, errors.New( + "oauth response missing access token", + ) + } + expiresIn := core.DefaultExpirySeconds + if response.ExpiresIn > 0 { + expiresIn = response.ExpiresIn + } + return response.AccessToken, expiresIn, nil }) - if err != nil { - return "", err - } - oauthTokenProvider.SetToken(response.AccessToken, response.ExpiresIn) - return response.AccessToken, nil }) return &Client{ Auth: client.NewClient(options), diff --git a/seed/go-sdk/oauth-client-credentials-nested-root/core/oauth.go b/seed/go-sdk/oauth-client-credentials-nested-root/core/oauth.go index 114b1f412ec7..9b6f34cc8e79 100644 --- a/seed/go-sdk/oauth-client-credentials-nested-root/core/oauth.go +++ b/seed/go-sdk/oauth-client-credentials-nested-root/core/oauth.go @@ -9,6 +9,9 @@ const ( // expirationBufferMinutes is subtracted from the token expiration time // to ensure we refresh the token before it actually expires. expirationBufferMinutes = 2 + + // DefaultExpirySeconds is used when the OAuth response doesn't include an expires_in value. + DefaultExpirySeconds = 3600 // 1 hour fallback ) // OAuthTokenProvider manages OAuth access tokens, including caching and automatic refresh. @@ -19,6 +22,9 @@ type OAuthTokenProvider struct { mu sync.Mutex accessToken string expiresAt time.Time + + // fetchMu ensures only one goroutine fetches a new token at a time + fetchMu sync.Mutex } // NewOAuthTokenProvider creates a new OAuthTokenProvider with the given credentials. @@ -73,6 +79,34 @@ func (o *OAuthTokenProvider) GetToken() string { return o.accessToken } +// GetOrFetch returns a valid token, fetching a new one if necessary. +// The fetchFunc is called at most once even if multiple goroutines call GetOrFetch +// concurrently when the token is expired. It should return (accessToken, expiresInSeconds, error). +func (o *OAuthTokenProvider) GetOrFetch(fetchFunc func() (string, int, error)) (string, error) { + // Fast path: check if we have a valid token + if token := o.GetToken(); token != "" { + return token, nil + } + + // Slow path: acquire fetch lock to ensure only one goroutine fetches + o.fetchMu.Lock() + defer o.fetchMu.Unlock() + + // Double-check after acquiring lock (another goroutine may have fetched) + if token := o.GetToken(); token != "" { + return token, nil + } + + // Fetch new token + accessToken, expiresIn, err := fetchFunc() + if err != nil { + return "", err + } + + o.SetToken(accessToken, expiresIn) + return accessToken, nil +} + // NeedsRefresh returns true if the token needs to be refreshed. func (o *OAuthTokenProvider) NeedsRefresh() bool { o.mu.Lock() diff --git a/seed/go-sdk/oauth-client-credentials-with-variables/README.md b/seed/go-sdk/oauth-client-credentials-with-variables/README.md index 4ea4f324f78d..6c0da2abfd2a 100644 --- a/seed/go-sdk/oauth-client-credentials-with-variables/README.md +++ b/seed/go-sdk/oauth-client-credentials-with-variables/README.md @@ -9,6 +9,7 @@ The Seed Go library provides convenient access to the Seed APIs from Go. - [Reference](#reference) - [Usage](#usage) - [Environments](#environments) +- [Oauth](#oauth) - [Errors](#errors) - [Request Options](#request-options) - [Advanced](#advanced) @@ -31,13 +32,17 @@ package example import ( client "github.com/oauth-client-credentials-with-variables/fern/client" + option "github.com/oauth-client-credentials-with-variables/fern/option" fern "github.com/oauth-client-credentials-with-variables/fern" context "context" ) func do() { client := client.NewClient( - nil, + option.WithClientCredentials( + "", + "", + ), ) request := &fern.GetTokenRequest{ ClientId: "client_id", @@ -64,6 +69,31 @@ client := client.NewClient( ) ``` +## Oauth + +This SDK supports OAuth 2.0 authentication. You have two options for providing credentials: + +**Option 1: Client Credentials** - Provide your client ID and secret, and the SDK will automatically handle +token fetching and refresh: + +**Option 2: Direct Token** - If you already have an access token (e.g., obtained through your own OAuth flow), +you can provide it directly: + +```go +// Option 1: Use client credentials (SDK will handle token fetching and refresh) +client := client.NewClient( + option.WithClientCredentials( + "", + "", + ), +) + +// Option 2: Use a pre-fetched token directly +client := client.NewClient( + option.WithToken(""), +) +``` + ## Errors Structured error types are returned from API calls that return non-success status codes. These errors are compatible diff --git a/seed/go-sdk/oauth-client-credentials-with-variables/client/client.go b/seed/go-sdk/oauth-client-credentials-with-variables/client/client.go index 1de0eba6e9fa..aac85befcd29 100644 --- a/seed/go-sdk/oauth-client-credentials-with-variables/client/client.go +++ b/seed/go-sdk/oauth-client-credentials-with-variables/client/client.go @@ -3,6 +3,9 @@ package client import ( + context "context" + errors "errors" + fern "github.com/oauth-client-credentials-with-variables/fern" auth "github.com/oauth-client-credentials-with-variables/fern/auth" core "github.com/oauth-client-credentials-with-variables/fern/core" internal "github.com/oauth-client-credentials-with-variables/fern/internal" @@ -27,6 +30,35 @@ type Client struct { func NewClient(opts ...option.RequestOption) *Client { options := core.NewRequestOptions(opts...) + oauthTokenProvider := core.NewOAuthTokenProvider( + options.ClientID, + options.ClientSecret, + ) + authOptions := *options + authClient := auth.NewClient( + &authOptions, + ) + options.SetTokenGetter(func() (string, error) { + return oauthTokenProvider.GetOrFetch(func() (string, int, error) { + response, err := authClient.GetTokenWithClientCredentials(context.Background(), &fern.GetTokenRequest{ + ClientId: options.ClientID, + ClientSecret: options.ClientSecret, + }) + if err != nil { + return "", 0, err + } + if response.AccessToken == "" { + return "", 0, errors.New( + "oauth response missing access token", + ) + } + expiresIn := core.DefaultExpirySeconds + if response.ExpiresIn > 0 { + expiresIn = response.ExpiresIn + } + return response.AccessToken, expiresIn, nil + }) + }) return &Client{ Auth: auth.NewClient(options), NestedNoAuth: client.NewClient(options), diff --git a/seed/go-sdk/oauth-client-credentials-with-variables/core/oauth.go b/seed/go-sdk/oauth-client-credentials-with-variables/core/oauth.go new file mode 100644 index 000000000000..9b6f34cc8e79 --- /dev/null +++ b/seed/go-sdk/oauth-client-credentials-with-variables/core/oauth.go @@ -0,0 +1,129 @@ +package core + +import ( + "sync" + "time" +) + +const ( + // expirationBufferMinutes is subtracted from the token expiration time + // to ensure we refresh the token before it actually expires. + expirationBufferMinutes = 2 + + // DefaultExpirySeconds is used when the OAuth response doesn't include an expires_in value. + DefaultExpirySeconds = 3600 // 1 hour fallback +) + +// OAuthTokenProvider manages OAuth access tokens, including caching and automatic refresh. +type OAuthTokenProvider struct { + clientID string + clientSecret string + + mu sync.Mutex + accessToken string + expiresAt time.Time + + // fetchMu ensures only one goroutine fetches a new token at a time + fetchMu sync.Mutex +} + +// NewOAuthTokenProvider creates a new OAuthTokenProvider with the given credentials. +func NewOAuthTokenProvider(clientID string, clientSecret string) *OAuthTokenProvider { + return &OAuthTokenProvider{ + clientID: clientID, + clientSecret: clientSecret, + } +} + +// ClientID returns the client ID. +func (o *OAuthTokenProvider) ClientID() string { + return o.clientID +} + +// ClientSecret returns the client secret. +func (o *OAuthTokenProvider) ClientSecret() string { + return o.clientSecret +} + +// SetToken sets the cached access token and its expiration time. +// The expiresIn parameter is the number of seconds until the token expires. +func (o *OAuthTokenProvider) SetToken(accessToken string, expiresIn int) { + o.mu.Lock() + defer o.mu.Unlock() + o.accessToken = accessToken + if expiresIn > 0 { + // Apply buffer to refresh before actual expiration + bufferSeconds := expirationBufferMinutes * 60 + effectiveExpiresIn := expiresIn - bufferSeconds + if effectiveExpiresIn < 0 { + effectiveExpiresIn = 0 + } + o.expiresAt = time.Now().Add(time.Duration(effectiveExpiresIn) * time.Second) + } else { + // No expiration info, token won't auto-refresh based on time + o.expiresAt = time.Time{} + } +} + +// GetToken returns the cached access token if it's still valid. +// Returns an empty string if the token is expired or not set. +func (o *OAuthTokenProvider) GetToken() string { + o.mu.Lock() + defer o.mu.Unlock() + if o.accessToken == "" { + return "" + } + if !o.expiresAt.IsZero() && time.Now().After(o.expiresAt) { + return "" + } + return o.accessToken +} + +// GetOrFetch returns a valid token, fetching a new one if necessary. +// The fetchFunc is called at most once even if multiple goroutines call GetOrFetch +// concurrently when the token is expired. It should return (accessToken, expiresInSeconds, error). +func (o *OAuthTokenProvider) GetOrFetch(fetchFunc func() (string, int, error)) (string, error) { + // Fast path: check if we have a valid token + if token := o.GetToken(); token != "" { + return token, nil + } + + // Slow path: acquire fetch lock to ensure only one goroutine fetches + o.fetchMu.Lock() + defer o.fetchMu.Unlock() + + // Double-check after acquiring lock (another goroutine may have fetched) + if token := o.GetToken(); token != "" { + return token, nil + } + + // Fetch new token + accessToken, expiresIn, err := fetchFunc() + if err != nil { + return "", err + } + + o.SetToken(accessToken, expiresIn) + return accessToken, nil +} + +// NeedsRefresh returns true if the token needs to be refreshed. +func (o *OAuthTokenProvider) NeedsRefresh() bool { + o.mu.Lock() + defer o.mu.Unlock() + if o.accessToken == "" { + return true + } + if !o.expiresAt.IsZero() && time.Now().After(o.expiresAt) { + return true + } + return false +} + +// Reset clears the cached token. +func (o *OAuthTokenProvider) Reset() { + o.mu.Lock() + defer o.mu.Unlock() + o.accessToken = "" + o.expiresAt = time.Time{} +} diff --git a/seed/go-sdk/oauth-client-credentials-with-variables/core/request_option.go b/seed/go-sdk/oauth-client-credentials-with-variables/core/request_option.go index 3233711b3044..5307ec9d8dc1 100644 --- a/seed/go-sdk/oauth-client-credentials-with-variables/core/request_option.go +++ b/seed/go-sdk/oauth-client-credentials-with-variables/core/request_option.go @@ -12,6 +12,9 @@ type RequestOption interface { applyRequestOptions(*RequestOptions) } +// TokenGetter is a function that returns an access token. +type TokenGetter func() (string, error) + // RequestOptions defines all of the possible request options. // // This type is primarily used by the generated code and is not meant @@ -23,6 +26,10 @@ type RequestOptions struct { BodyProperties map[string]interface{} QueryParameters url.Values MaxAttempts uint + tokenGetter TokenGetter + ClientID string + ClientSecret string + Token string } // NewRequestOptions returns a new *RequestOptions value. @@ -45,6 +52,14 @@ func NewRequestOptions(opts ...RequestOption) *RequestOptions { // for the request(s). func (r *RequestOptions) ToHeader() http.Header { header := r.cloneHeader() + if r.Token != "" { + header.Set("Authorization", "Bearer "+r.Token) + } + if header.Get("Authorization") == "" && r.tokenGetter != nil { + if token, err := r.tokenGetter(); err == nil && token != "" { + header.Set("Authorization", "Bearer "+token) + } + } return header } @@ -110,3 +125,47 @@ type MaxAttemptsOption struct { func (m *MaxAttemptsOption) applyRequestOptions(opts *RequestOptions) { opts.MaxAttempts = m.MaxAttempts } + +// ClientIDOption implements the RequestOption interface. +type ClientIDOption struct { + ClientID string +} + +func (c *ClientIDOption) applyRequestOptions(opts *RequestOptions) { + opts.ClientID = c.ClientID +} + +// ClientSecretOption implements the RequestOption interface. +type ClientSecretOption struct { + ClientSecret string +} + +func (c *ClientSecretOption) applyRequestOptions(opts *RequestOptions) { + opts.ClientSecret = c.ClientSecret +} + +// ClientCredentialsOption implements the RequestOption interface. +type ClientCredentialsOption struct { + ClientID string + ClientSecret string +} + +func (c *ClientCredentialsOption) applyRequestOptions(opts *RequestOptions) { + opts.ClientID = c.ClientID + opts.ClientSecret = c.ClientSecret +} + +// TokenOption implements the RequestOption interface. +type TokenOption struct { + Token string +} + +func (t *TokenOption) applyRequestOptions(opts *RequestOptions) { + opts.Token = t.Token +} + +// SetTokenGetter sets the token getter function for OAuth. +// This is an internal method and should not be called directly. +func (r *RequestOptions) SetTokenGetter(getter TokenGetter) { + r.tokenGetter = getter +} diff --git a/seed/go-sdk/oauth-client-credentials-with-variables/dynamic-snippets/example0/snippet.go b/seed/go-sdk/oauth-client-credentials-with-variables/dynamic-snippets/example0/snippet.go index aa882e0d9b55..4b0a2ce19590 100644 --- a/seed/go-sdk/oauth-client-credentials-with-variables/dynamic-snippets/example0/snippet.go +++ b/seed/go-sdk/oauth-client-credentials-with-variables/dynamic-snippets/example0/snippet.go @@ -12,7 +12,10 @@ func do() { option.WithBaseURL( "https://api.fern.com", ), - nil, + option.WithClientCredentials( + "", + "", + ), ) request := &fern.GetTokenRequest{ ClientId: "client_id", diff --git a/seed/go-sdk/oauth-client-credentials-with-variables/dynamic-snippets/example1/snippet.go b/seed/go-sdk/oauth-client-credentials-with-variables/dynamic-snippets/example1/snippet.go index 1c3eac577587..5fd138457547 100644 --- a/seed/go-sdk/oauth-client-credentials-with-variables/dynamic-snippets/example1/snippet.go +++ b/seed/go-sdk/oauth-client-credentials-with-variables/dynamic-snippets/example1/snippet.go @@ -12,7 +12,10 @@ func do() { option.WithBaseURL( "https://api.fern.com", ), - nil, + option.WithClientCredentials( + "", + "", + ), ) request := &fern.RefreshTokenRequest{ ClientId: "client_id", diff --git a/seed/go-sdk/oauth-client-credentials-with-variables/dynamic-snippets/example2/snippet.go b/seed/go-sdk/oauth-client-credentials-with-variables/dynamic-snippets/example2/snippet.go index 5bfa72e2699d..58a2bbb13c80 100644 --- a/seed/go-sdk/oauth-client-credentials-with-variables/dynamic-snippets/example2/snippet.go +++ b/seed/go-sdk/oauth-client-credentials-with-variables/dynamic-snippets/example2/snippet.go @@ -11,7 +11,10 @@ func do() { option.WithBaseURL( "https://api.fern.com", ), - nil, + option.WithClientCredentials( + "", + "", + ), ) client.NestedNoAuth.Api.GetSomething( context.TODO(), diff --git a/seed/go-sdk/oauth-client-credentials-with-variables/dynamic-snippets/example3/snippet.go b/seed/go-sdk/oauth-client-credentials-with-variables/dynamic-snippets/example3/snippet.go index ce17e330db75..f0495d9f6ac3 100644 --- a/seed/go-sdk/oauth-client-credentials-with-variables/dynamic-snippets/example3/snippet.go +++ b/seed/go-sdk/oauth-client-credentials-with-variables/dynamic-snippets/example3/snippet.go @@ -11,7 +11,10 @@ func do() { option.WithBaseURL( "https://api.fern.com", ), - nil, + option.WithClientCredentials( + "", + "", + ), ) client.Nested.Api.GetSomething( context.TODO(), diff --git a/seed/go-sdk/oauth-client-credentials-with-variables/dynamic-snippets/example4/snippet.go b/seed/go-sdk/oauth-client-credentials-with-variables/dynamic-snippets/example4/snippet.go index 481ca02b0183..a7c04f737f9c 100644 --- a/seed/go-sdk/oauth-client-credentials-with-variables/dynamic-snippets/example4/snippet.go +++ b/seed/go-sdk/oauth-client-credentials-with-variables/dynamic-snippets/example4/snippet.go @@ -11,7 +11,10 @@ func do() { option.WithBaseURL( "https://api.fern.com", ), - nil, + option.WithClientCredentials( + "", + "", + ), ) client.Service.Post( context.TODO(), diff --git a/seed/go-sdk/oauth-client-credentials-with-variables/dynamic-snippets/example5/snippet.go b/seed/go-sdk/oauth-client-credentials-with-variables/dynamic-snippets/example5/snippet.go index 21aef78c8f17..d325605274d4 100644 --- a/seed/go-sdk/oauth-client-credentials-with-variables/dynamic-snippets/example5/snippet.go +++ b/seed/go-sdk/oauth-client-credentials-with-variables/dynamic-snippets/example5/snippet.go @@ -11,7 +11,10 @@ func do() { option.WithBaseURL( "https://api.fern.com", ), - nil, + option.WithClientCredentials( + "", + "", + ), ) client.Simple.GetSomething( context.TODO(), diff --git a/seed/go-sdk/oauth-client-credentials-with-variables/option/request_option.go b/seed/go-sdk/oauth-client-credentials-with-variables/option/request_option.go index b64b0383b853..8668fa58a2b9 100644 --- a/seed/go-sdk/oauth-client-credentials-with-variables/option/request_option.go +++ b/seed/go-sdk/oauth-client-credentials-with-variables/option/request_option.go @@ -62,3 +62,33 @@ func WithMaxAttempts(attempts uint) *core.MaxAttemptsOption { MaxAttempts: attempts, } } + +// WithClientID sets the clientID auth request parameter. +func WithClientID(clientID string) *core.ClientIDOption { + return &core.ClientIDOption{ + ClientID: clientID, + } +} + +// WithClientSecret sets the clientSecret auth request parameter. +func WithClientSecret(clientSecret string) *core.ClientSecretOption { + return &core.ClientSecretOption{ + ClientSecret: clientSecret, + } +} + +// WithClientCredentials sets both the clientID and clientSecret auth request parameters. +func WithClientCredentials(clientID string, clientSecret string) *core.ClientCredentialsOption { + return &core.ClientCredentialsOption{ + ClientID: clientID, + ClientSecret: clientSecret, + } +} + +// WithToken sets the OAuth token directly, bypassing the client credentials flow. +// Use this when you already have an access token. +func WithToken(token string) *core.TokenOption { + return &core.TokenOption{ + Token: token, + } +} diff --git a/seed/go-sdk/oauth-client-credentials-with-variables/snippet.json b/seed/go-sdk/oauth-client-credentials-with-variables/snippet.json index a3daef125e90..b501fd5b861d 100644 --- a/seed/go-sdk/oauth-client-credentials-with-variables/snippet.json +++ b/seed/go-sdk/oauth-client-credentials-with-variables/snippet.json @@ -8,7 +8,7 @@ }, "snippet": { "type": "go", - "client": "import (\n\tcontext \"context\"\n\tfernclient \"github.com/oauth-client-credentials-with-variables/fern/client\"\n)\n\nclient := fernclient.NewClient()\nerr := client.Simple.GetSomething(\n\tcontext.TODO(),\n)\n" + "client": "import (\n\tcontext \"context\"\n\tfernclient \"github.com/oauth-client-credentials-with-variables/fern/client\"\n\toption \"github.com/oauth-client-credentials-with-variables/fern/option\"\n)\n\nclient := fernclient.NewClient(\n\toption.WithClientCredentials(\n\t\t\"\u003cYOUR_CLIENT_ID\u003e\",\n\t\t\"\u003cYOUR_CLIENT_SECRET\u003e\",\n\t),\n)\nerr := client.Simple.GetSomething(\n\tcontext.TODO(),\n)\n" } }, { @@ -19,7 +19,7 @@ }, "snippet": { "type": "go", - "client": "import (\n\tcontext \"context\"\n\tfernclient \"github.com/oauth-client-credentials-with-variables/fern/client\"\n)\n\nclient := fernclient.NewClient()\nerr := client.NestedNoAuth.Api.GetSomething(\n\tcontext.TODO(),\n)\n" + "client": "import (\n\tcontext \"context\"\n\tfernclient \"github.com/oauth-client-credentials-with-variables/fern/client\"\n\toption \"github.com/oauth-client-credentials-with-variables/fern/option\"\n)\n\nclient := fernclient.NewClient(\n\toption.WithClientCredentials(\n\t\t\"\u003cYOUR_CLIENT_ID\u003e\",\n\t\t\"\u003cYOUR_CLIENT_SECRET\u003e\",\n\t),\n)\nerr := client.NestedNoAuth.Api.GetSomething(\n\tcontext.TODO(),\n)\n" } }, { @@ -30,7 +30,7 @@ }, "snippet": { "type": "go", - "client": "import (\n\tcontext \"context\"\n\tfernclient \"github.com/oauth-client-credentials-with-variables/fern/client\"\n)\n\nclient := fernclient.NewClient()\nerr := client.Nested.Api.GetSomething(\n\tcontext.TODO(),\n)\n" + "client": "import (\n\tcontext \"context\"\n\tfernclient \"github.com/oauth-client-credentials-with-variables/fern/client\"\n\toption \"github.com/oauth-client-credentials-with-variables/fern/option\"\n)\n\nclient := fernclient.NewClient(\n\toption.WithClientCredentials(\n\t\t\"\u003cYOUR_CLIENT_ID\u003e\",\n\t\t\"\u003cYOUR_CLIENT_SECRET\u003e\",\n\t),\n)\nerr := client.Nested.Api.GetSomething(\n\tcontext.TODO(),\n)\n" } }, { @@ -41,7 +41,7 @@ }, "snippet": { "type": "go", - "client": "import (\n\tcontext \"context\"\n\tfernclient \"github.com/oauth-client-credentials-with-variables/fern/client\"\n)\n\nclient := fernclient.NewClient()\nerr := client.Service.Post(\n\tcontext.TODO(),\n\t\"endpointParam\",\n)\n" + "client": "import (\n\tcontext \"context\"\n\tfernclient \"github.com/oauth-client-credentials-with-variables/fern/client\"\n\toption \"github.com/oauth-client-credentials-with-variables/fern/option\"\n)\n\nclient := fernclient.NewClient(\n\toption.WithClientCredentials(\n\t\t\"\u003cYOUR_CLIENT_ID\u003e\",\n\t\t\"\u003cYOUR_CLIENT_SECRET\u003e\",\n\t),\n)\nerr := client.Service.Post(\n\tcontext.TODO(),\n\t\"endpointParam\",\n)\n" } }, { @@ -52,7 +52,7 @@ }, "snippet": { "type": "go", - "client": "import (\n\tcontext \"context\"\n\tfern \"github.com/oauth-client-credentials-with-variables/fern\"\n\tfernclient \"github.com/oauth-client-credentials-with-variables/fern/client\"\n)\n\nclient := fernclient.NewClient()\nresponse, err := client.Auth.GetTokenWithClientCredentials(\n\tcontext.TODO(),\n\t\u0026fern.GetTokenRequest{\n\t\tClientId: \"client_id\",\n\t\tClientSecret: \"client_secret\",\n\t},\n)\n" + "client": "import (\n\tcontext \"context\"\n\tfern \"github.com/oauth-client-credentials-with-variables/fern\"\n\tfernclient \"github.com/oauth-client-credentials-with-variables/fern/client\"\n\toption \"github.com/oauth-client-credentials-with-variables/fern/option\"\n)\n\nclient := fernclient.NewClient(\n\toption.WithClientCredentials(\n\t\t\"\u003cYOUR_CLIENT_ID\u003e\",\n\t\t\"\u003cYOUR_CLIENT_SECRET\u003e\",\n\t),\n)\nresponse, err := client.Auth.GetTokenWithClientCredentials(\n\tcontext.TODO(),\n\t\u0026fern.GetTokenRequest{\n\t\tClientId: \"client_id\",\n\t\tClientSecret: \"client_secret\",\n\t},\n)\n" } }, { @@ -63,7 +63,7 @@ }, "snippet": { "type": "go", - "client": "import (\n\tcontext \"context\"\n\tfern \"github.com/oauth-client-credentials-with-variables/fern\"\n\tfernclient \"github.com/oauth-client-credentials-with-variables/fern/client\"\n)\n\nclient := fernclient.NewClient()\nresponse, err := client.Auth.RefreshToken(\n\tcontext.TODO(),\n\t\u0026fern.RefreshTokenRequest{\n\t\tClientId: \"client_id\",\n\t\tClientSecret: \"client_secret\",\n\t\tRefreshToken: \"refresh_token\",\n\t},\n)\n" + "client": "import (\n\tcontext \"context\"\n\tfern \"github.com/oauth-client-credentials-with-variables/fern\"\n\tfernclient \"github.com/oauth-client-credentials-with-variables/fern/client\"\n\toption \"github.com/oauth-client-credentials-with-variables/fern/option\"\n)\n\nclient := fernclient.NewClient(\n\toption.WithClientCredentials(\n\t\t\"\u003cYOUR_CLIENT_ID\u003e\",\n\t\t\"\u003cYOUR_CLIENT_SECRET\u003e\",\n\t),\n)\nresponse, err := client.Auth.RefreshToken(\n\tcontext.TODO(),\n\t\u0026fern.RefreshTokenRequest{\n\t\tClientId: \"client_id\",\n\t\tClientSecret: \"client_secret\",\n\t\tRefreshToken: \"refresh_token\",\n\t},\n)\n" } } ] diff --git a/seed/go-sdk/oauth-client-credentials/dynamic-snippets/example0/snippet.go b/seed/go-sdk/oauth-client-credentials/dynamic-snippets/example0/snippet.go new file mode 100644 index 000000000000..4caeed5de9f4 --- /dev/null +++ b/seed/go-sdk/oauth-client-credentials/dynamic-snippets/example0/snippet.go @@ -0,0 +1,31 @@ +package example + +import ( + client "github.com/oauth-client-credentials/fern/client" + option "github.com/oauth-client-credentials/fern/option" + fern "github.com/oauth-client-credentials/fern" + context "context" +) + +func do() { + client := client.NewClient( + option.WithBaseURL( + "https://api.fern.com", + ), + option.WithClientCredentials( + "", + "", + ), + ) + request := &fern.GetTokenRequest{ + ClientId: "my_oauth_app_123", + ClientSecret: "sk_live_abcdef123456789", + Scope: fern.String( + "read:users", + ), + } + client.Auth.GetTokenWithClientCredentials( + context.TODO(), + request, + ) +} diff --git a/seed/go-sdk/oauth-client-credentials/dynamic-snippets/example1/snippet.go b/seed/go-sdk/oauth-client-credentials/dynamic-snippets/example1/snippet.go new file mode 100644 index 000000000000..d7c1bd4a3fc0 --- /dev/null +++ b/seed/go-sdk/oauth-client-credentials/dynamic-snippets/example1/snippet.go @@ -0,0 +1,31 @@ +package example + +import ( + client "github.com/oauth-client-credentials/fern/client" + option "github.com/oauth-client-credentials/fern/option" + fern "github.com/oauth-client-credentials/fern" + context "context" +) + +func do() { + client := client.NewClient( + option.WithBaseURL( + "https://api.fern.com", + ), + option.WithClientCredentials( + "", + "", + ), + ) + request := &fern.GetTokenRequest{ + ClientId: "client_id", + ClientSecret: "client_secret", + Scope: fern.String( + "scope", + ), + } + client.Auth.GetTokenWithClientCredentials( + context.TODO(), + request, + ) +} diff --git a/seed/go-sdk/oauth-client-credentials/dynamic-snippets/example2/snippet.go b/seed/go-sdk/oauth-client-credentials/dynamic-snippets/example2/snippet.go new file mode 100644 index 000000000000..413636cc4b2a --- /dev/null +++ b/seed/go-sdk/oauth-client-credentials/dynamic-snippets/example2/snippet.go @@ -0,0 +1,32 @@ +package example + +import ( + client "github.com/oauth-client-credentials/fern/client" + option "github.com/oauth-client-credentials/fern/option" + fern "github.com/oauth-client-credentials/fern" + context "context" +) + +func do() { + client := client.NewClient( + option.WithBaseURL( + "https://api.fern.com", + ), + option.WithClientCredentials( + "", + "", + ), + ) + request := &fern.RefreshTokenRequest{ + ClientId: "my_oauth_app_123", + ClientSecret: "sk_live_abcdef123456789", + RefreshToken: "refresh_token", + Scope: fern.String( + "read:users", + ), + } + client.Auth.RefreshToken( + context.TODO(), + request, + ) +} diff --git a/seed/go-sdk/oauth-client-credentials/dynamic-snippets/example3/snippet.go b/seed/go-sdk/oauth-client-credentials/dynamic-snippets/example3/snippet.go new file mode 100644 index 000000000000..470eb72617a2 --- /dev/null +++ b/seed/go-sdk/oauth-client-credentials/dynamic-snippets/example3/snippet.go @@ -0,0 +1,32 @@ +package example + +import ( + client "github.com/oauth-client-credentials/fern/client" + option "github.com/oauth-client-credentials/fern/option" + fern "github.com/oauth-client-credentials/fern" + context "context" +) + +func do() { + client := client.NewClient( + option.WithBaseURL( + "https://api.fern.com", + ), + option.WithClientCredentials( + "", + "", + ), + ) + request := &fern.RefreshTokenRequest{ + ClientId: "client_id", + ClientSecret: "client_secret", + RefreshToken: "refresh_token", + Scope: fern.String( + "scope", + ), + } + client.Auth.RefreshToken( + context.TODO(), + request, + ) +} diff --git a/seed/go-sdk/oauth-client-credentials/dynamic-snippets/example4/snippet.go b/seed/go-sdk/oauth-client-credentials/dynamic-snippets/example4/snippet.go new file mode 100644 index 000000000000..3b8be659aaf3 --- /dev/null +++ b/seed/go-sdk/oauth-client-credentials/dynamic-snippets/example4/snippet.go @@ -0,0 +1,22 @@ +package example + +import ( + client "github.com/oauth-client-credentials/fern/client" + option "github.com/oauth-client-credentials/fern/option" + context "context" +) + +func do() { + client := client.NewClient( + option.WithBaseURL( + "https://api.fern.com", + ), + option.WithClientCredentials( + "", + "", + ), + ) + client.NestedNoAuth.Api.GetSomething( + context.TODO(), + ) +} diff --git a/seed/go-sdk/oauth-client-credentials/dynamic-snippets/example5/snippet.go b/seed/go-sdk/oauth-client-credentials/dynamic-snippets/example5/snippet.go new file mode 100644 index 000000000000..4b064f36c0e5 --- /dev/null +++ b/seed/go-sdk/oauth-client-credentials/dynamic-snippets/example5/snippet.go @@ -0,0 +1,22 @@ +package example + +import ( + client "github.com/oauth-client-credentials/fern/client" + option "github.com/oauth-client-credentials/fern/option" + context "context" +) + +func do() { + client := client.NewClient( + option.WithBaseURL( + "https://api.fern.com", + ), + option.WithClientCredentials( + "", + "", + ), + ) + client.Nested.Api.GetSomething( + context.TODO(), + ) +} diff --git a/seed/go-sdk/oauth-client-credentials/dynamic-snippets/example6/snippet.go b/seed/go-sdk/oauth-client-credentials/dynamic-snippets/example6/snippet.go new file mode 100644 index 000000000000..fb306a8900af --- /dev/null +++ b/seed/go-sdk/oauth-client-credentials/dynamic-snippets/example6/snippet.go @@ -0,0 +1,22 @@ +package example + +import ( + client "github.com/oauth-client-credentials/fern/client" + option "github.com/oauth-client-credentials/fern/option" + context "context" +) + +func do() { + client := client.NewClient( + option.WithBaseURL( + "https://api.fern.com", + ), + option.WithClientCredentials( + "", + "", + ), + ) + client.Simple.GetSomething( + context.TODO(), + ) +} diff --git a/seed/go-sdk/pagination/dynamic-snippets/example26/snippet.go b/seed/go-sdk/pagination/dynamic-snippets/example26/snippet.go new file mode 100644 index 000000000000..22ca8b41c10f --- /dev/null +++ b/seed/go-sdk/pagination/dynamic-snippets/example26/snippet.go @@ -0,0 +1,28 @@ +package example + +import ( + client "github.com/pagination/fern/client" + option "github.com/pagination/fern/option" + fern "github.com/pagination/fern" + context "context" +) + +func do() { + client := client.NewClient( + option.WithBaseURL( + "https://api.fern.com", + ), + option.WithToken( + "", + ), + ) + request := &fern.ListUsersOptionalDataRequest{ + Page: fern.Int( + 1, + ), + } + client.Users.ListWithOptionalData( + context.TODO(), + request, + ) +} diff --git a/seed/go-sdk/pagination/dynamic-snippets/example27/snippet.go b/seed/go-sdk/pagination/dynamic-snippets/example27/snippet.go new file mode 100644 index 000000000000..22ca8b41c10f --- /dev/null +++ b/seed/go-sdk/pagination/dynamic-snippets/example27/snippet.go @@ -0,0 +1,28 @@ +package example + +import ( + client "github.com/pagination/fern/client" + option "github.com/pagination/fern/option" + fern "github.com/pagination/fern" + context "context" +) + +func do() { + client := client.NewClient( + option.WithBaseURL( + "https://api.fern.com", + ), + option.WithToken( + "", + ), + ) + request := &fern.ListUsersOptionalDataRequest{ + Page: fern.Int( + 1, + ), + } + client.Users.ListWithOptionalData( + context.TODO(), + request, + ) +} diff --git a/seed/go-sdk/pagination/dynamic-snippets/example28/snippet.go b/seed/go-sdk/pagination/dynamic-snippets/example28/snippet.go new file mode 100644 index 000000000000..22ca8b41c10f --- /dev/null +++ b/seed/go-sdk/pagination/dynamic-snippets/example28/snippet.go @@ -0,0 +1,28 @@ +package example + +import ( + client "github.com/pagination/fern/client" + option "github.com/pagination/fern/option" + fern "github.com/pagination/fern" + context "context" +) + +func do() { + client := client.NewClient( + option.WithBaseURL( + "https://api.fern.com", + ), + option.WithToken( + "", + ), + ) + request := &fern.ListUsersOptionalDataRequest{ + Page: fern.Int( + 1, + ), + } + client.Users.ListWithOptionalData( + context.TODO(), + request, + ) +} diff --git a/seed/go-sdk/pagination/reference.md b/seed/go-sdk/pagination/reference.md index b96156375eeb..0ca5c8471be6 100644 --- a/seed/go-sdk/pagination/reference.md +++ b/seed/go-sdk/pagination/reference.md @@ -1608,6 +1608,55 @@ client.Users.ListWithGlobalConfig( + + + + +
client.Users.ListWithOptionalData() -> *fern.ListUsersOptionalDataPaginationResponse +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```go +request := &fern.ListUsersOptionalDataRequest{ + Page: fern.Int( + 1, + ), + } +client.Users.ListWithOptionalData( + context.TODO(), + request, + ) +} +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**page:** `*int` — Defaults to first page + +
+
+
+
+ +
diff --git a/seed/go-sdk/pagination/snippet.json b/seed/go-sdk/pagination/snippet.json index 4f0be36fecd3..cedba6791e3d 100644 --- a/seed/go-sdk/pagination/snippet.json +++ b/seed/go-sdk/pagination/snippet.json @@ -275,6 +275,17 @@ "client": "import (\n\tcontext \"context\"\n\tfern \"github.com/pagination/fern\"\n\tfernclient \"github.com/pagination/fern/client\"\n\toption \"github.com/pagination/fern/option\"\n)\n\nclient := fernclient.NewClient(\n\toption.WithToken(\n\t\t\"\u003cYOUR_AUTH_TOKEN\u003e\",\n\t),\n)\nresponse, err := client.Users.ListWithMixedTypeCursorPagination(\n\tcontext.TODO(),\n\t\u0026fern.ListUsersMixedTypeCursorPaginationRequest{\n\t\tCursor: fern.String(\n\t\t\t\"cursor\",\n\t\t),\n\t},\n)\n" } }, + { + "id": { + "path": "/users/optional-data", + "method": "GET", + "identifier_override": "endpoint_users.listWithOptionalData" + }, + "snippet": { + "type": "go", + "client": "import (\n\tcontext \"context\"\n\tfern \"github.com/pagination/fern\"\n\tfernclient \"github.com/pagination/fern/client\"\n\toption \"github.com/pagination/fern/option\"\n)\n\nclient := fernclient.NewClient(\n\toption.WithToken(\n\t\t\"\u003cYOUR_AUTH_TOKEN\u003e\",\n\t),\n)\nresponse, err := client.Users.ListWithOptionalData(\n\tcontext.TODO(),\n\t\u0026fern.ListUsersOptionalDataRequest{\n\t\tPage: fern.Int(\n\t\t\t1,\n\t\t),\n\t},\n)\n" + } + }, { "id": { "path": "/{index}/conversations/search", diff --git a/seed/go-sdk/pagination/users.go b/seed/go-sdk/pagination/users.go index 0161063c652e..1b869509770b 100644 --- a/seed/go-sdk/pagination/users.go +++ b/seed/go-sdk/pagination/users.go @@ -480,6 +480,32 @@ func (l *ListUsersOffsetStepPaginationRequest) SetOrder(order *Order) { l.require(listUsersOffsetStepPaginationRequestFieldOrder) } +var ( + listUsersOptionalDataRequestFieldPage = big.NewInt(1 << 0) +) + +type ListUsersOptionalDataRequest struct { + // Defaults to first page + Page *int `json:"-" url:"page,omitempty"` + + // Private bitmask of fields set to an explicit value and therefore not to be omitted + explicitFields *big.Int `json:"-" url:"-"` +} + +func (l *ListUsersOptionalDataRequest) require(field *big.Int) { + if l.explicitFields == nil { + l.explicitFields = big.NewInt(0) + } + l.explicitFields.Or(l.explicitFields, field) +} + +// SetPage sets the Page field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (l *ListUsersOptionalDataRequest) SetPage(page *int) { + l.Page = page + l.require(listUsersOptionalDataRequestFieldPage) +} + var ( listUsersExtendedOptionalListResponseFieldData = big.NewInt(1 << 0) listUsersExtendedOptionalListResponseFieldNext = big.NewInt(1 << 1) @@ -796,6 +822,133 @@ func (l *ListUsersMixedTypePaginationResponse) String() string { return fmt.Sprintf("%#v", l) } +var ( + listUsersOptionalDataPaginationResponseFieldHasNextPage = big.NewInt(1 << 0) + listUsersOptionalDataPaginationResponseFieldPage = big.NewInt(1 << 1) + listUsersOptionalDataPaginationResponseFieldTotalCount = big.NewInt(1 << 2) + listUsersOptionalDataPaginationResponseFieldData = big.NewInt(1 << 3) +) + +type ListUsersOptionalDataPaginationResponse struct { + HasNextPage *bool `json:"hasNextPage,omitempty" url:"hasNextPage,omitempty"` + Page *Page `json:"page,omitempty" url:"page,omitempty"` + // The totall number of /users + TotalCount int `json:"total_count" url:"total_count"` + Data []*User `json:"data,omitempty" url:"data,omitempty"` + + // Private bitmask of fields set to an explicit value and therefore not to be omitted + explicitFields *big.Int `json:"-" url:"-"` + + extraProperties map[string]interface{} + rawJSON json.RawMessage +} + +func (l *ListUsersOptionalDataPaginationResponse) GetHasNextPage() *bool { + if l == nil { + return nil + } + return l.HasNextPage +} + +func (l *ListUsersOptionalDataPaginationResponse) GetPage() *Page { + if l == nil { + return nil + } + return l.Page +} + +func (l *ListUsersOptionalDataPaginationResponse) GetTotalCount() int { + if l == nil { + return 0 + } + return l.TotalCount +} + +func (l *ListUsersOptionalDataPaginationResponse) GetData() []*User { + if l == nil { + return nil + } + return l.Data +} + +func (l *ListUsersOptionalDataPaginationResponse) GetExtraProperties() map[string]interface{} { + return l.extraProperties +} + +func (l *ListUsersOptionalDataPaginationResponse) require(field *big.Int) { + if l.explicitFields == nil { + l.explicitFields = big.NewInt(0) + } + l.explicitFields.Or(l.explicitFields, field) +} + +// SetHasNextPage sets the HasNextPage field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (l *ListUsersOptionalDataPaginationResponse) SetHasNextPage(hasNextPage *bool) { + l.HasNextPage = hasNextPage + l.require(listUsersOptionalDataPaginationResponseFieldHasNextPage) +} + +// SetPage sets the Page field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (l *ListUsersOptionalDataPaginationResponse) SetPage(page *Page) { + l.Page = page + l.require(listUsersOptionalDataPaginationResponseFieldPage) +} + +// SetTotalCount sets the TotalCount field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (l *ListUsersOptionalDataPaginationResponse) SetTotalCount(totalCount int) { + l.TotalCount = totalCount + l.require(listUsersOptionalDataPaginationResponseFieldTotalCount) +} + +// SetData sets the Data field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (l *ListUsersOptionalDataPaginationResponse) SetData(data []*User) { + l.Data = data + l.require(listUsersOptionalDataPaginationResponseFieldData) +} + +func (l *ListUsersOptionalDataPaginationResponse) UnmarshalJSON(data []byte) error { + type unmarshaler ListUsersOptionalDataPaginationResponse + var value unmarshaler + if err := json.Unmarshal(data, &value); err != nil { + return err + } + *l = ListUsersOptionalDataPaginationResponse(value) + extraProperties, err := internal.ExtractExtraProperties(data, *l) + if err != nil { + return err + } + l.extraProperties = extraProperties + l.rawJSON = json.RawMessage(data) + return nil +} + +func (l *ListUsersOptionalDataPaginationResponse) MarshalJSON() ([]byte, error) { + type embed ListUsersOptionalDataPaginationResponse + var marshaler = struct { + embed + }{ + embed: embed(*l), + } + explicitMarshaler := internal.HandleExplicitFields(marshaler, l.explicitFields) + return json.Marshal(explicitMarshaler) +} + +func (l *ListUsersOptionalDataPaginationResponse) String() string { + if len(l.rawJSON) > 0 { + if value, err := internal.StringifyJSON(l.rawJSON); err == nil { + return value + } + } + if value, err := internal.StringifyJSON(l); err == nil { + return value + } + return fmt.Sprintf("%#v", l) +} + var ( listUsersPaginationResponseFieldHasNextPage = big.NewInt(1 << 0) listUsersPaginationResponseFieldPage = big.NewInt(1 << 1) diff --git a/seed/go-sdk/pagination/users/client.go b/seed/go-sdk/pagination/users/client.go index 3607c49c55ed..d1c514e52645 100644 --- a/seed/go-sdk/pagination/users/client.go +++ b/seed/go-sdk/pagination/users/client.go @@ -741,3 +741,67 @@ func (c *Client) ListWithGlobalConfig( ) return pager.GetPage(ctx, &next) } + +func (c *Client) ListWithOptionalData( + ctx context.Context, + request *fern.ListUsersOptionalDataRequest, + opts ...option.RequestOption, +) (*core.Page[*int, *fern.User, *fern.ListUsersOptionalDataPaginationResponse], error) { + options := core.NewRequestOptions(opts...) + baseURL := internal.ResolveBaseURL( + options.BaseURL, + c.baseURL, + "", + ) + endpointURL := baseURL + "/users/optional-data" + queryParams, err := internal.QueryValues(request) + if err != nil { + return nil, err + } + headers := internal.MergeHeaders( + c.options.ToHeader(), + options.ToHeader(), + ) + prepareCall := func(pageRequest *core.PageRequest[*int]) *internal.CallParams { + if pageRequest.Cursor != nil { + queryParams.Set("page", fmt.Sprintf("%v", *pageRequest.Cursor)) + } + nextURL := endpointURL + if len(queryParams) > 0 { + nextURL += "?" + queryParams.Encode() + } + return &internal.CallParams{ + URL: nextURL, + Method: http.MethodGet, + Headers: headers, + MaxAttempts: options.MaxAttempts, + BodyProperties: options.BodyProperties, + QueryParameters: options.QueryParameters, + Client: options.HTTPClient, + Response: pageRequest.Response, + } + } + next := 1 + if queryParams.Has("page") { + var err error + if next, err = strconv.Atoi(queryParams.Get("page")); err != nil { + return nil, err + } + } + + readPageResponse := func(response *fern.ListUsersOptionalDataPaginationResponse) *core.PageResponse[*int, *fern.User, *fern.ListUsersOptionalDataPaginationResponse] { + next += 1 + results := response.GetData() + return &core.PageResponse[*int, *fern.User, *fern.ListUsersOptionalDataPaginationResponse]{ + Results: results, + Response: response, + Next: &next, + } + } + pager := internal.NewOffsetPager( + c.caller, + prepareCall, + readPageResponse, + ) + return pager.GetPage(ctx, &next) +} diff --git a/seed/go-sdk/streaming/.fern/metadata.json b/seed/go-sdk/streaming/.fern/metadata.json new file mode 100644 index 000000000000..abf3778e9da9 --- /dev/null +++ b/seed/go-sdk/streaming/.fern/metadata.json @@ -0,0 +1,12 @@ +{ + "cliVersion": "DUMMY", + "generatorName": "fernapi/fern-go-sdk", + "generatorVersion": "latest", + "generatorConfig": { + "enableWireTests": false, + "packageName": "stream", + "module": { + "path": "github.com/fern-api/stream-go" + } + } +} \ No newline at end of file diff --git a/seed/go-sdk/streaming/.github/workflows/ci.yml b/seed/go-sdk/streaming/.github/workflows/ci.yml new file mode 100644 index 000000000000..56310d69624b --- /dev/null +++ b/seed/go-sdk/streaming/.github/workflows/ci.yml @@ -0,0 +1,35 @@ +name: ci + +on: [push] + +jobs: + compile: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up go + uses: actions/setup-go@v4 + + - name: Compile + run: go build ./... + test: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up go + uses: actions/setup-go@v4 + + - name: Setup wiremock server + run: | + if [ -f wiremock/docker-compose.test.yml ]; then docker compose -f wiremock/docker-compose.test.yml down && docker compose -f wiremock/docker-compose.test.yml up -d; fi + + - name: Test + run: go test ./... + + - name: Teardown wiremock server + run: | + if [ -f wiremock/docker-compose.test.yml ]; then docker compose -f wiremock/docker-compose.test.yml down; fi diff --git a/seed/go-sdk/streaming/client/client.go b/seed/go-sdk/streaming/client/client.go new file mode 100644 index 000000000000..902525e73215 --- /dev/null +++ b/seed/go-sdk/streaming/client/client.go @@ -0,0 +1,33 @@ +// Code generated by Fern. DO NOT EDIT. + +package client + +import ( + core "github.com/fern-api/stream-go/v2/core" + dummy "github.com/fern-api/stream-go/v2/dummy" + internal "github.com/fern-api/stream-go/v2/internal" + option "github.com/fern-api/stream-go/v2/option" +) + +type Client struct { + Dummy *dummy.Client + + options *core.RequestOptions + baseURL string + caller *internal.Caller +} + +func NewClient(opts ...option.RequestOption) *Client { + options := core.NewRequestOptions(opts...) + return &Client{ + Dummy: dummy.NewClient(options), + options: options, + baseURL: options.BaseURL, + caller: internal.NewCaller( + &internal.CallerParams{ + Client: options.HTTPClient, + MaxAttempts: options.MaxAttempts, + }, + ), + } +} diff --git a/seed/go-sdk/streaming/client/client_test.go b/seed/go-sdk/streaming/client/client_test.go new file mode 100644 index 000000000000..6b0ee5caf60e --- /dev/null +++ b/seed/go-sdk/streaming/client/client_test.go @@ -0,0 +1,45 @@ +// Code generated by Fern. DO NOT EDIT. + +package client + +import ( + option "github.com/fern-api/stream-go/v2/option" + assert "github.com/stretchr/testify/assert" + http "net/http" + testing "testing" + time "time" +) + +func TestNewClient(t *testing.T) { + t.Run("default", func(t *testing.T) { + c := NewClient() + assert.Empty(t, c.baseURL) + }) + + t.Run("base url", func(t *testing.T) { + c := NewClient( + option.WithBaseURL("test.co"), + ) + assert.Equal(t, "test.co", c.baseURL) + }) + + t.Run("http client", func(t *testing.T) { + httpClient := &http.Client{ + Timeout: 5 * time.Second, + } + c := NewClient( + option.WithHTTPClient(httpClient), + ) + assert.Empty(t, c.baseURL) + }) + + t.Run("http header", func(t *testing.T) { + header := make(http.Header) + header.Set("X-API-Tenancy", "test") + c := NewClient( + option.WithHTTPHeader(header), + ) + assert.Empty(t, c.baseURL) + assert.Equal(t, "test", c.options.HTTPHeader.Get("X-API-Tenancy")) + }) +} diff --git a/seed/go-sdk/streaming/core/api_error.go b/seed/go-sdk/streaming/core/api_error.go new file mode 100644 index 000000000000..6168388541b4 --- /dev/null +++ b/seed/go-sdk/streaming/core/api_error.go @@ -0,0 +1,47 @@ +package core + +import ( + "fmt" + "net/http" +) + +// APIError is a lightweight wrapper around the standard error +// interface that preserves the status code from the RPC, if any. +type APIError struct { + err error + + StatusCode int `json:"-"` + Header http.Header `json:"-"` +} + +// NewAPIError constructs a new API error. +func NewAPIError(statusCode int, header http.Header, err error) *APIError { + return &APIError{ + err: err, + Header: header, + StatusCode: statusCode, + } +} + +// Unwrap returns the underlying error. This also makes the error compatible +// with errors.As and errors.Is. +func (a *APIError) Unwrap() error { + if a == nil { + return nil + } + return a.err +} + +// Error returns the API error's message. +func (a *APIError) Error() string { + if a == nil || (a.err == nil && a.StatusCode == 0) { + return "" + } + if a.err == nil { + return fmt.Sprintf("%d", a.StatusCode) + } + if a.StatusCode == 0 { + return a.err.Error() + } + return fmt.Sprintf("%d: %s", a.StatusCode, a.err.Error()) +} diff --git a/seed/go-sdk/streaming/core/http.go b/seed/go-sdk/streaming/core/http.go new file mode 100644 index 000000000000..92c435692940 --- /dev/null +++ b/seed/go-sdk/streaming/core/http.go @@ -0,0 +1,15 @@ +package core + +import "net/http" + +// HTTPClient is an interface for a subset of the *http.Client. +type HTTPClient interface { + Do(*http.Request) (*http.Response, error) +} + +// Response is an HTTP response from an HTTP client. +type Response[T any] struct { + StatusCode int + Header http.Header + Body T +} diff --git a/seed/go-sdk/streaming/core/request_option.go b/seed/go-sdk/streaming/core/request_option.go new file mode 100644 index 000000000000..119c02bfe449 --- /dev/null +++ b/seed/go-sdk/streaming/core/request_option.go @@ -0,0 +1,109 @@ +// Code generated by Fern. DO NOT EDIT. + +package core + +import ( + http "net/http" + url "net/url" +) + +// RequestOption adapts the behavior of the client or an individual request. +type RequestOption interface { + applyRequestOptions(*RequestOptions) +} + +// RequestOptions defines all of the possible request options. +// +// This type is primarily used by the generated code and is not meant +// to be used directly; use the option package instead. +type RequestOptions struct { + BaseURL string + HTTPClient HTTPClient + HTTPHeader http.Header + BodyProperties map[string]interface{} + QueryParameters url.Values + MaxAttempts uint +} + +// NewRequestOptions returns a new *RequestOptions value. +// +// This function is primarily used by the generated code and is not meant +// to be used directly; use RequestOption instead. +func NewRequestOptions(opts ...RequestOption) *RequestOptions { + options := &RequestOptions{ + HTTPHeader: make(http.Header), + BodyProperties: make(map[string]interface{}), + QueryParameters: make(url.Values), + } + for _, opt := range opts { + opt.applyRequestOptions(options) + } + return options +} + +// ToHeader maps the configured request options into a http.Header used +// for the request(s). +func (r *RequestOptions) ToHeader() http.Header { return r.cloneHeader() } + +func (r *RequestOptions) cloneHeader() http.Header { + headers := r.HTTPHeader.Clone() + headers.Set("X-Fern-Language", "Go") + headers.Set("X-Fern-SDK-Name", "github.com/fern-api/stream-go/v2") + headers.Set("X-Fern-SDK-Version", "v2.0.0") + headers.Set("User-Agent", "github.com/streaming/fern/v2.0.0") + return headers +} + +// BaseURLOption implements the RequestOption interface. +type BaseURLOption struct { + BaseURL string +} + +func (b *BaseURLOption) applyRequestOptions(opts *RequestOptions) { + opts.BaseURL = b.BaseURL +} + +// HTTPClientOption implements the RequestOption interface. +type HTTPClientOption struct { + HTTPClient HTTPClient +} + +func (h *HTTPClientOption) applyRequestOptions(opts *RequestOptions) { + opts.HTTPClient = h.HTTPClient +} + +// HTTPHeaderOption implements the RequestOption interface. +type HTTPHeaderOption struct { + HTTPHeader http.Header +} + +func (h *HTTPHeaderOption) applyRequestOptions(opts *RequestOptions) { + opts.HTTPHeader = h.HTTPHeader +} + +// BodyPropertiesOption implements the RequestOption interface. +type BodyPropertiesOption struct { + BodyProperties map[string]interface{} +} + +func (b *BodyPropertiesOption) applyRequestOptions(opts *RequestOptions) { + opts.BodyProperties = b.BodyProperties +} + +// QueryParametersOption implements the RequestOption interface. +type QueryParametersOption struct { + QueryParameters url.Values +} + +func (q *QueryParametersOption) applyRequestOptions(opts *RequestOptions) { + opts.QueryParameters = q.QueryParameters +} + +// MaxAttemptsOption implements the RequestOption interface. +type MaxAttemptsOption struct { + MaxAttempts uint +} + +func (m *MaxAttemptsOption) applyRequestOptions(opts *RequestOptions) { + opts.MaxAttempts = m.MaxAttempts +} diff --git a/seed/go-sdk/streaming/core/stream.go b/seed/go-sdk/streaming/core/stream.go new file mode 100644 index 000000000000..25c528e89516 --- /dev/null +++ b/seed/go-sdk/streaming/core/stream.go @@ -0,0 +1,368 @@ +package core + +import ( + "bufio" + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "slices" + "strings" +) + +type StreamFormat string + +const ( + StreamFormatSSE StreamFormat = "sse" + StreamFormatEmpty StreamFormat = "" +) + +const ( + sseEventSeparator = "\n\n" + sseLineSeparator = "\n" +) + +const ( + defaultMaxBufSize = 64 * 1024 // 64KB +) + +// Stream represents a stream of messages sent from a server. +type Stream[T any] struct { + reader streamReader + closer io.Closer +} + +// StreamOption adapts the behavior of the Stream. +type StreamOption func(*streamOptions) + +// WithDelimiter overrides the delimiter for the Stream. +// +// By default, the Stream is newline-delimited. +func WithDelimiter(delimiter string) StreamOption { + return func(opts *streamOptions) { + opts.delimiter = delimiter + } +} + +// WithPrefix overrides the prefix for the Stream. +// +// By default, the Stream doesn't have a prefix. +func WithPrefix(prefix string) StreamOption { + return func(opts *streamOptions) { + opts.prefix = prefix + } +} + +// WithTerminator overrides the terminator for the Stream. +// +// By default, the Stream terminates on EOF. +func WithTerminator(terminator string) StreamOption { + return func(opts *streamOptions) { + opts.terminator = terminator + } +} + +// WithFormat overrides the isSSE flag for the Stream. +// +// By default, the Stream is not SSE. +func WithFormat(format StreamFormat) StreamOption { + return func(opts *streamOptions) { + opts.format = format + } +} + +// NewStream constructs a new Stream from the given *http.Response. +func NewStream[T any](response *http.Response, opts ...StreamOption) *Stream[T] { + options := new(streamOptions) + for _, opt := range opts { + opt(options) + } + return &Stream[T]{ + reader: newStreamReader(response.Body, options), + closer: response.Body, + } +} + +// Recv reads a message from the stream, returning io.EOF when +// all the messages have been read. +func (s Stream[T]) Recv() (T, error) { + var value T + bytes, err := s.reader.ReadFromStream() + if err != nil { + return value, err + } + if err := json.Unmarshal(bytes, &value); err != nil { + return value, err + } + return value, nil +} + +// Close closes the Stream. +func (s Stream[T]) Close() error { + return s.closer.Close() +} + +// streamReader reads data from a stream. +type streamReader interface { + ReadFromStream() ([]byte, error) +} + +// newStreamReader returns a new streamReader based on the given +// delimiter. +// +// By default, the streamReader uses a simple a *bufio.Reader +// which splits on newlines, and otherwise use a *bufio.Scanner to +// split on custom delimiters. +func newStreamReader(reader io.Reader, options *streamOptions) streamReader { + if !options.isEmpty() { + if options.maxBufSize == 0 { + options.maxBufSize = defaultMaxBufSize + } + if options.format == StreamFormatSSE { + return newSseStreamReader(reader, options) + } + return newScannerStreamReader(reader, options) + } + return newBufferStreamReader(reader) +} + +// BufferStreamReader reads data from a *bufio.Reader, which splits +// on newlines. +type BufferStreamReader struct { + reader *bufio.Reader +} + +func newBufferStreamReader(reader io.Reader) *BufferStreamReader { + return &BufferStreamReader{ + reader: bufio.NewReader(reader), + } +} + +func (b *BufferStreamReader) ReadFromStream() ([]byte, error) { + line, err := b.reader.ReadBytes('\n') + if err != nil { + return nil, err + } + // Strip the trailing newline + return bytes.TrimSuffix(line, []byte("\n")), nil +} + +// ScannerStreamReader reads data from a *bufio.Scanner, which allows for +// configurable delimiters. +type ScannerStreamReader struct { + scanner *bufio.Scanner + options *streamOptions +} + +func newScannerStreamReader( + reader io.Reader, + options *streamOptions, +) *ScannerStreamReader { + scanner := bufio.NewScanner(reader) + stream := &ScannerStreamReader{ + scanner: scanner, + options: options, + } + scanner.Split(func(bytes []byte, atEOF bool) (int, []byte, error) { + if atEOF && len(bytes) == 0 { + return 0, nil, nil + } + n, data, err := stream.parse(bytes) + if stream.isTerminated(data) { + return 0, nil, io.EOF + } + return n, data, err + }) + return stream +} + +func (s *ScannerStreamReader) ReadFromStream() ([]byte, error) { + if s.scanner.Scan() { + return s.scanner.Bytes(), nil + } + if err := s.scanner.Err(); err != nil { + return nil, err + } + return nil, io.EOF +} + +func (s *ScannerStreamReader) parse(bytes []byte) (int, []byte, error) { + var startIndex int + if s.options != nil && s.options.prefix != "" { + if i := strings.Index(string(bytes), s.options.prefix); i >= 0 { + startIndex = i + len(s.options.prefix) + } + } + data := bytes[startIndex:] + lineDelimiter := s.options.getLineDelimiter() + delimIndex := strings.Index(string(data), lineDelimiter) + if delimIndex < 0 { + return startIndex + len(data), data, nil + } + endIndex := delimIndex + len(lineDelimiter) + parsedData := data[:endIndex] + n := startIndex + endIndex + return n, parsedData, nil +} + +func (s *ScannerStreamReader) isTerminated(bytes []byte) bool { + if s.options == nil || s.options.terminator == "" { + return false + } + return strings.Contains(string(bytes), s.options.terminator) +} + +type streamOptions struct { + delimiter string + prefix string + terminator string + format StreamFormat + maxBufSize int +} + +func (s *streamOptions) isEmpty() bool { + return s.delimiter == "" && s.prefix == "" && s.terminator == "" && s.format == StreamFormatEmpty +} + +func (s *streamOptions) getLineDelimiter() string { + if s.delimiter != "" { + return s.delimiter + } + return sseLineSeparator +} + +type SseStreamReader struct { + scanner *bufio.Scanner + options *streamOptions +} + +func newSseStreamReader( + reader io.Reader, + options *streamOptions, +) *SseStreamReader { + scanner := bufio.NewScanner(reader) + stream := &SseStreamReader{ + scanner: scanner, + options: options, + } + scanner.Buffer(make([]byte, slices.Min([]int{4096, options.maxBufSize})), options.maxBufSize) + + // Configure scanner to split on SSE event separator (\n\n) + // This is fixed by the SSE specification and cannot be changed + scanner.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error) { + if atEOF && len(data) == 0 { + return 0, nil, nil + } + // SSE messages are always separated by blank lines (\n\n) + if i := strings.Index(string(data), sseEventSeparator); i >= 0 { + return i + len(sseEventSeparator), data[0:i], nil + } + + if atEOF || stream.isTerminated(data) { + return len(data), data, nil + } + return 0, nil, nil + }) + return stream +} + +func (s *SseStreamReader) isTerminated(bytes []byte) bool { + if s.options == nil || s.options.terminator == "" { + return false + } + return strings.Contains(string(bytes), s.options.terminator) +} + +func (s *SseStreamReader) ReadFromStream() ([]byte, error) { + + event, err := s.nextEvent() + if err != nil { + return nil, err + } + return event.data, nil +} + +func (s *SseStreamReader) nextEvent() (*SseEvent, error) { + + event := SseEvent{} + if s.scanner.Scan() { + rawEvent := s.scanner.Bytes() + + // Parse individual lines within the SSE message + // Lines are always separated by \n within a message (SSE specification) + lines := strings.Split(string(rawEvent), sseLineSeparator) + for _, line := range lines { + s.parseSseLine([]byte(line), &event) + } + + if event.size() > s.options.maxBufSize { + return nil, errors.New("SseStreamReader.ReadFromStream: buffer limit exceeded") + } + return &event, nil + } + return &event, io.EOF +} + +func (s *SseStreamReader) parseSseLine(_bytes []byte, event *SseEvent) { + // Try to parse with space first (standard format), then without space (lenient format) + if value, ok := s.tryParseField(_bytes, sseDataPrefix, sseDataPrefixNoSpace); ok { + if len(event.data) > 0 { + // Join multiple data: lines using the configured delimiter + // This allows customization of how multi-line data is concatenated: + // - "\n" (default): preserves line breaks for multi-line JSON + // - "": concatenates without separator + // - Any other string: custom separator + lineDelimiter := s.options.getLineDelimiter() + event.data = append(event.data, lineDelimiter...) + } + event.data = append(event.data, value...) + } else if value, ok := s.tryParseField(_bytes, sseIdPrefix, sseIdPrefixNoSpace); ok { + event.id = append(event.id, value...) + } else if value, ok := s.tryParseField(_bytes, sseEventPrefix, sseEventPrefixNoSpace); ok { + event.event = append(event.event, value...) + } else if value, ok := s.tryParseField(_bytes, sseRetryPrefix, sseRetryPrefixNoSpace); ok { + event.retry = append(event.retry, value...) + } +} + +// tryParseField attempts to parse an SSE field by trying multiple prefix patterns in order. +// This handles APIs that don't strictly follow the SSE specification by omitting the space after the colon. +// It tries each prefix in the order provided and returns the value after the first matching prefix. +func (s *SseStreamReader) tryParseField(line []byte, prefixes ...[]byte) ([]byte, bool) { + for _, prefix := range prefixes { + if bytes.HasPrefix(line, prefix) { + return line[len(prefix):], true + } + } + return nil, false +} + +func (event *SseEvent) size() int { + return len(event.id) + len(event.data) + len(event.event) + len(event.retry) +} + +func (event *SseEvent) String() string { + return fmt.Sprintf("SseEvent{id: %q, event: %q, data: %q, retry: %q}", event.id, event.event, event.data, event.retry) +} + +type SseEvent struct { + id []byte + data []byte + event []byte + retry []byte +} + +var ( + sseIdPrefix = []byte("id: ") + sseDataPrefix = []byte("data: ") + sseEventPrefix = []byte("event: ") + sseRetryPrefix = []byte("retry: ") + + // Lenient prefixes without space for APIs that don't strictly follow SSE specification + sseIdPrefixNoSpace = []byte("id:") + sseDataPrefixNoSpace = []byte("data:") + sseEventPrefixNoSpace = []byte("event:") + sseRetryPrefixNoSpace = []byte("retry:") +) diff --git a/seed/go-sdk/streaming/dummy/client.go b/seed/go-sdk/streaming/dummy/client.go new file mode 100644 index 000000000000..47c2a23f6cce --- /dev/null +++ b/seed/go-sdk/streaming/dummy/client.go @@ -0,0 +1,83 @@ +// Code generated by Fern. DO NOT EDIT. + +package dummy + +import ( + context "context" + stream "github.com/fern-api/stream-go/v2" + core "github.com/fern-api/stream-go/v2/core" + internal "github.com/fern-api/stream-go/v2/internal" + option "github.com/fern-api/stream-go/v2/option" + http "net/http" +) + +type Client struct { + WithRawResponse *RawClient + + options *core.RequestOptions + baseURL string + caller *internal.Caller +} + +func NewClient(options *core.RequestOptions) *Client { + return &Client{ + WithRawResponse: NewRawClient(options), + options: options, + baseURL: options.BaseURL, + caller: internal.NewCaller( + &internal.CallerParams{ + Client: options.HTTPClient, + MaxAttempts: options.MaxAttempts, + }, + ), + } +} + +func (c *Client) GenerateStream( + ctx context.Context, + request *stream.GenerateStreamRequest, + opts ...option.RequestOption, +) (*core.Stream[stream.StreamResponse], error) { + options := core.NewRequestOptions(opts...) + baseURL := internal.ResolveBaseURL( + options.BaseURL, + c.baseURL, + "", + ) + endpointURL := baseURL + "/generate-stream" + headers := internal.MergeHeaders( + c.options.ToHeader(), + options.ToHeader(), + ) + streamer := internal.NewStreamer[stream.StreamResponse](c.caller) + return streamer.Stream( + ctx, + &internal.StreamParams{ + URL: endpointURL, + Method: http.MethodPost, + Headers: headers, + MaxAttempts: options.MaxAttempts, + BodyProperties: options.BodyProperties, + QueryParameters: options.QueryParameters, + Client: options.HTTPClient, + Request: request, + ErrorDecoder: internal.NewErrorDecoder(stream.ErrorCodes), + }, + ) +} + +func (c *Client) Generate( + ctx context.Context, + request *stream.Generateequest, + opts ...option.RequestOption, +) (*stream.StreamResponse, error) { + response, err := c.WithRawResponse.Generate( + ctx, + request, + opts..., + ) + if err != nil { + return nil, err + } + return response.Body, nil +} diff --git a/seed/go-sdk/streaming/dummy/raw_client.go b/seed/go-sdk/streaming/dummy/raw_client.go new file mode 100644 index 000000000000..361c67f5907f --- /dev/null +++ b/seed/go-sdk/streaming/dummy/raw_client.go @@ -0,0 +1,72 @@ +// Code generated by Fern. DO NOT EDIT. + +package dummy + +import ( + context "context" + stream "github.com/fern-api/stream-go/v2" + core "github.com/fern-api/stream-go/v2/core" + internal "github.com/fern-api/stream-go/v2/internal" + option "github.com/fern-api/stream-go/v2/option" + http "net/http" +) + +type RawClient struct { + baseURL string + caller *internal.Caller + options *core.RequestOptions +} + +func NewRawClient(options *core.RequestOptions) *RawClient { + return &RawClient{ + options: options, + baseURL: options.BaseURL, + caller: internal.NewCaller( + &internal.CallerParams{ + Client: options.HTTPClient, + MaxAttempts: options.MaxAttempts, + }, + ), + } +} + +func (r *RawClient) Generate( + ctx context.Context, + request *stream.Generateequest, + opts ...option.RequestOption, +) (*core.Response[*stream.StreamResponse], error) { + options := core.NewRequestOptions(opts...) + baseURL := internal.ResolveBaseURL( + options.BaseURL, + r.baseURL, + "", + ) + endpointURL := baseURL + "/generate" + headers := internal.MergeHeaders( + r.options.ToHeader(), + options.ToHeader(), + ) + var response *stream.StreamResponse + raw, err := r.caller.Call( + ctx, + &internal.CallParams{ + URL: endpointURL, + Method: http.MethodPost, + Headers: headers, + MaxAttempts: options.MaxAttempts, + BodyProperties: options.BodyProperties, + QueryParameters: options.QueryParameters, + Client: options.HTTPClient, + Request: request, + Response: &response, + }, + ) + if err != nil { + return nil, err + } + return &core.Response[*stream.StreamResponse]{ + StatusCode: raw.StatusCode, + Header: raw.Header, + Body: response, + }, nil +} diff --git a/seed/go-sdk/streaming/dynamic-snippets/example0/snippet.go b/seed/go-sdk/streaming/dynamic-snippets/example0/snippet.go new file mode 100644 index 000000000000..cdc45a765996 --- /dev/null +++ b/seed/go-sdk/streaming/dynamic-snippets/example0/snippet.go @@ -0,0 +1,23 @@ +package example + +import ( + client "github.com/fern-api/stream-go/v2/client" + option "github.com/fern-api/stream-go/v2/option" + stream "github.com/fern-api/stream-go/v2" + context "context" +) + +func do() { + client := client.NewClient( + option.WithBaseURL( + "https://api.fern.com", + ), + ) + request := &stream.GenerateStreamRequest{ + NumEvents: 1, + } + client.Dummy.GenerateStream( + context.TODO(), + request, + ) +} diff --git a/seed/go-sdk/streaming/dynamic-snippets/example1/snippet.go b/seed/go-sdk/streaming/dynamic-snippets/example1/snippet.go new file mode 100644 index 000000000000..5a3f2af9383d --- /dev/null +++ b/seed/go-sdk/streaming/dynamic-snippets/example1/snippet.go @@ -0,0 +1,23 @@ +package example + +import ( + client "github.com/fern-api/stream-go/v2/client" + option "github.com/fern-api/stream-go/v2/option" + stream "github.com/fern-api/stream-go/v2" + context "context" +) + +func do() { + client := client.NewClient( + option.WithBaseURL( + "https://api.fern.com", + ), + ) + request := &stream.Generateequest{ + NumEvents: 5, + } + client.Dummy.Generate( + context.TODO(), + request, + ) +} diff --git a/seed/go-sdk/streaming/dynamic-snippets/example2/snippet.go b/seed/go-sdk/streaming/dynamic-snippets/example2/snippet.go new file mode 100644 index 000000000000..4e83013bdd35 --- /dev/null +++ b/seed/go-sdk/streaming/dynamic-snippets/example2/snippet.go @@ -0,0 +1,23 @@ +package example + +import ( + client "github.com/fern-api/stream-go/v2/client" + option "github.com/fern-api/stream-go/v2/option" + stream "github.com/fern-api/stream-go/v2" + context "context" +) + +func do() { + client := client.NewClient( + option.WithBaseURL( + "https://api.fern.com", + ), + ) + request := &stream.Generateequest{ + NumEvents: 1, + } + client.Dummy.Generate( + context.TODO(), + request, + ) +} diff --git a/seed/go-sdk/streaming/internal/caller.go b/seed/go-sdk/streaming/internal/caller.go new file mode 100644 index 000000000000..53d1c2d54b22 --- /dev/null +++ b/seed/go-sdk/streaming/internal/caller.go @@ -0,0 +1,250 @@ +package internal + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "reflect" + "strings" + + "github.com/fern-api/stream-go/v2/core" +) + +const ( + // contentType specifies the JSON Content-Type header value. + contentType = "application/json" + contentTypeHeader = "Content-Type" +) + +// Caller calls APIs and deserializes their response, if any. +type Caller struct { + client core.HTTPClient + retrier *Retrier +} + +// CallerParams represents the parameters used to constrcut a new *Caller. +type CallerParams struct { + Client core.HTTPClient + MaxAttempts uint +} + +// NewCaller returns a new *Caller backed by the given parameters. +func NewCaller(params *CallerParams) *Caller { + var httpClient core.HTTPClient = http.DefaultClient + if params.Client != nil { + httpClient = params.Client + } + var retryOptions []RetryOption + if params.MaxAttempts > 0 { + retryOptions = append(retryOptions, WithMaxAttempts(params.MaxAttempts)) + } + return &Caller{ + client: httpClient, + retrier: NewRetrier(retryOptions...), + } +} + +// CallParams represents the parameters used to issue an API call. +type CallParams struct { + URL string + Method string + MaxAttempts uint + Headers http.Header + BodyProperties map[string]interface{} + QueryParameters url.Values + Client core.HTTPClient + Request interface{} + Response interface{} + ResponseIsOptional bool + ErrorDecoder ErrorDecoder +} + +// CallResponse is a parsed HTTP response from an API call. +type CallResponse struct { + StatusCode int + Header http.Header +} + +// Call issues an API call according to the given call parameters. +func (c *Caller) Call(ctx context.Context, params *CallParams) (*CallResponse, error) { + url := buildURL(params.URL, params.QueryParameters) + req, err := newRequest( + ctx, + url, + params.Method, + params.Headers, + params.Request, + params.BodyProperties, + ) + if err != nil { + return nil, err + } + + // If the call has been cancelled, don't issue the request. + if err := ctx.Err(); err != nil { + return nil, err + } + + client := c.client + if params.Client != nil { + // Use the HTTP client scoped to the request. + client = params.Client + } + + var retryOptions []RetryOption + if params.MaxAttempts > 0 { + retryOptions = append(retryOptions, WithMaxAttempts(params.MaxAttempts)) + } + + resp, err := c.retrier.Run( + client.Do, + req, + params.ErrorDecoder, + retryOptions..., + ) + if err != nil { + return nil, err + } + + // Close the response body after we're done. + defer resp.Body.Close() + + // Check if the call was cancelled before we return the error + // associated with the call and/or unmarshal the response data. + if err := ctx.Err(); err != nil { + return nil, err + } + + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + return nil, decodeError(resp, params.ErrorDecoder) + } + + // Mutate the response parameter in-place. + if params.Response != nil { + if writer, ok := params.Response.(io.Writer); ok { + _, err = io.Copy(writer, resp.Body) + } else { + err = json.NewDecoder(resp.Body).Decode(params.Response) + } + if err != nil { + if err == io.EOF { + if params.ResponseIsOptional { + // The response is optional, so we should ignore the + // io.EOF error + return &CallResponse{ + StatusCode: resp.StatusCode, + Header: resp.Header, + }, nil + } + return nil, fmt.Errorf("expected a %T response, but the server responded with nothing", params.Response) + } + return nil, err + } + } + + return &CallResponse{ + StatusCode: resp.StatusCode, + Header: resp.Header, + }, nil +} + +// buildURL constructs the final URL by appending the given query parameters (if any). +func buildURL( + url string, + queryParameters url.Values, +) string { + if len(queryParameters) == 0 { + return url + } + if strings.ContainsRune(url, '?') { + url += "&" + } else { + url += "?" + } + url += queryParameters.Encode() + return url +} + +// newRequest returns a new *http.Request with all of the fields +// required to issue the call. +func newRequest( + ctx context.Context, + url string, + method string, + endpointHeaders http.Header, + request interface{}, + bodyProperties map[string]interface{}, +) (*http.Request, error) { + requestBody, err := newRequestBody(request, bodyProperties) + if err != nil { + return nil, err + } + req, err := http.NewRequestWithContext(ctx, method, url, requestBody) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + req.Header.Set(contentTypeHeader, contentType) + for name, values := range endpointHeaders { + req.Header[name] = values + } + return req, nil +} + +// newRequestBody returns a new io.Reader that represents the HTTP request body. +func newRequestBody(request interface{}, bodyProperties map[string]interface{}) (io.Reader, error) { + if isNil(request) { + if len(bodyProperties) == 0 { + return nil, nil + } + requestBytes, err := json.Marshal(bodyProperties) + if err != nil { + return nil, err + } + return bytes.NewReader(requestBytes), nil + } + if body, ok := request.(io.Reader); ok { + return body, nil + } + requestBytes, err := MarshalJSONWithExtraProperties(request, bodyProperties) + if err != nil { + return nil, err + } + return bytes.NewReader(requestBytes), nil +} + +// decodeError decodes the error from the given HTTP response. Note that +// it's the caller's responsibility to close the response body. +func decodeError(response *http.Response, errorDecoder ErrorDecoder) error { + if errorDecoder != nil { + // This endpoint has custom errors, so we'll + // attempt to unmarshal the error into a structured + // type based on the status code. + return errorDecoder(response.StatusCode, response.Header, response.Body) + } + // This endpoint doesn't have any custom error + // types, so we just read the body as-is, and + // put it into a normal error. + bytes, err := io.ReadAll(response.Body) + if err != nil && err != io.EOF { + return err + } + if err == io.EOF { + // The error didn't have a response body, + // so all we can do is return an error + // with the status code. + return core.NewAPIError(response.StatusCode, response.Header, nil) + } + return core.NewAPIError(response.StatusCode, response.Header, errors.New(string(bytes))) +} + +// isNil is used to determine if the request value is equal to nil (i.e. an interface +// value that holds a nil concrete value is itself non-nil). +func isNil(value interface{}) bool { + return value == nil || reflect.ValueOf(value).IsNil() +} diff --git a/seed/go-sdk/streaming/internal/caller_test.go b/seed/go-sdk/streaming/internal/caller_test.go new file mode 100644 index 000000000000..bab98eb18836 --- /dev/null +++ b/seed/go-sdk/streaming/internal/caller_test.go @@ -0,0 +1,395 @@ +package internal + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "net/http/httptest" + "net/url" + "strconv" + "testing" + + "github.com/fern-api/stream-go/v2/core" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// InternalTestCase represents a single test case. +type InternalTestCase struct { + description string + + // Server-side assertions. + givePathSuffix string + giveMethod string + giveResponseIsOptional bool + giveHeader http.Header + giveErrorDecoder ErrorDecoder + giveRequest *InternalTestRequest + giveQueryParams url.Values + giveBodyProperties map[string]interface{} + + // Client-side assertions. + wantResponse *InternalTestResponse + wantHeaders http.Header + wantError error +} + +// InternalTestRequest a simple request body. +type InternalTestRequest struct { + Id string `json:"id"` +} + +// InternalTestResponse a simple response body. +type InternalTestResponse struct { + Id string `json:"id"` + ExtraBodyProperties map[string]interface{} `json:"extraBodyProperties,omitempty"` + QueryParameters url.Values `json:"queryParameters,omitempty"` +} + +// InternalTestNotFoundError represents a 404. +type InternalTestNotFoundError struct { + *core.APIError + + Message string `json:"message"` +} + +func TestCall(t *testing.T) { + tests := []*InternalTestCase{ + { + description: "GET success", + giveMethod: http.MethodGet, + giveHeader: http.Header{ + "X-API-Status": []string{"success"}, + }, + giveRequest: &InternalTestRequest{ + Id: "123", + }, + wantResponse: &InternalTestResponse{ + Id: "123", + }, + }, + { + description: "GET success with query", + givePathSuffix: "?limit=1", + giveMethod: http.MethodGet, + giveHeader: http.Header{ + "X-API-Status": []string{"success"}, + }, + giveRequest: &InternalTestRequest{ + Id: "123", + }, + wantResponse: &InternalTestResponse{ + Id: "123", + QueryParameters: url.Values{ + "limit": []string{"1"}, + }, + }, + }, + { + description: "GET not found", + giveMethod: http.MethodGet, + giveHeader: http.Header{ + "X-API-Status": []string{"fail"}, + }, + giveRequest: &InternalTestRequest{ + Id: strconv.Itoa(http.StatusNotFound), + }, + giveErrorDecoder: newTestErrorDecoder(t), + wantError: &InternalTestNotFoundError{ + APIError: core.NewAPIError( + http.StatusNotFound, + http.Header{}, + errors.New(`{"message":"ID \"404\" not found"}`), + ), + }, + }, + { + description: "POST empty body", + giveMethod: http.MethodPost, + giveHeader: http.Header{ + "X-API-Status": []string{"fail"}, + }, + giveRequest: nil, + wantError: core.NewAPIError( + http.StatusBadRequest, + http.Header{}, + errors.New("invalid request"), + ), + }, + { + description: "POST optional response", + giveMethod: http.MethodPost, + giveHeader: http.Header{ + "X-API-Status": []string{"success"}, + }, + giveRequest: &InternalTestRequest{ + Id: "123", + }, + giveResponseIsOptional: true, + }, + { + description: "POST API error", + giveMethod: http.MethodPost, + giveHeader: http.Header{ + "X-API-Status": []string{"fail"}, + }, + giveRequest: &InternalTestRequest{ + Id: strconv.Itoa(http.StatusInternalServerError), + }, + wantError: core.NewAPIError( + http.StatusInternalServerError, + http.Header{}, + errors.New("failed to process request"), + ), + }, + { + description: "POST extra properties", + giveMethod: http.MethodPost, + giveHeader: http.Header{ + "X-API-Status": []string{"success"}, + }, + giveRequest: new(InternalTestRequest), + giveBodyProperties: map[string]interface{}{ + "key": "value", + }, + wantResponse: &InternalTestResponse{ + ExtraBodyProperties: map[string]interface{}{ + "key": "value", + }, + }, + }, + { + description: "GET extra query parameters", + giveMethod: http.MethodGet, + giveHeader: http.Header{ + "X-API-Status": []string{"success"}, + }, + giveQueryParams: url.Values{ + "extra": []string{"true"}, + }, + giveRequest: &InternalTestRequest{ + Id: "123", + }, + wantResponse: &InternalTestResponse{ + Id: "123", + QueryParameters: url.Values{ + "extra": []string{"true"}, + }, + }, + }, + { + description: "GET merge extra query parameters", + givePathSuffix: "?limit=1", + giveMethod: http.MethodGet, + giveHeader: http.Header{ + "X-API-Status": []string{"success"}, + }, + giveRequest: &InternalTestRequest{ + Id: "123", + }, + giveQueryParams: url.Values{ + "extra": []string{"true"}, + }, + wantResponse: &InternalTestResponse{ + Id: "123", + QueryParameters: url.Values{ + "limit": []string{"1"}, + "extra": []string{"true"}, + }, + }, + }, + } + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + var ( + server = newTestServer(t, test) + client = server.Client() + ) + caller := NewCaller( + &CallerParams{ + Client: client, + }, + ) + var response *InternalTestResponse + _, err := caller.Call( + context.Background(), + &CallParams{ + URL: server.URL + test.givePathSuffix, + Method: test.giveMethod, + Headers: test.giveHeader, + BodyProperties: test.giveBodyProperties, + QueryParameters: test.giveQueryParams, + Request: test.giveRequest, + Response: &response, + ResponseIsOptional: test.giveResponseIsOptional, + ErrorDecoder: test.giveErrorDecoder, + }, + ) + if test.wantError != nil { + assert.EqualError(t, err, test.wantError.Error()) + return + } + require.NoError(t, err) + assert.Equal(t, test.wantResponse, response) + }) + } +} + +func TestMergeHeaders(t *testing.T) { + t.Run("both empty", func(t *testing.T) { + merged := MergeHeaders(make(http.Header), make(http.Header)) + assert.Empty(t, merged) + }) + + t.Run("empty left", func(t *testing.T) { + left := make(http.Header) + + right := make(http.Header) + right.Set("X-API-Version", "0.0.1") + + merged := MergeHeaders(left, right) + assert.Equal(t, "0.0.1", merged.Get("X-API-Version")) + }) + + t.Run("empty right", func(t *testing.T) { + left := make(http.Header) + left.Set("X-API-Version", "0.0.1") + + right := make(http.Header) + + merged := MergeHeaders(left, right) + assert.Equal(t, "0.0.1", merged.Get("X-API-Version")) + }) + + t.Run("single value override", func(t *testing.T) { + left := make(http.Header) + left.Set("X-API-Version", "0.0.0") + + right := make(http.Header) + right.Set("X-API-Version", "0.0.1") + + merged := MergeHeaders(left, right) + assert.Equal(t, []string{"0.0.1"}, merged.Values("X-API-Version")) + }) + + t.Run("multiple value override", func(t *testing.T) { + left := make(http.Header) + left.Set("X-API-Versions", "0.0.0") + + right := make(http.Header) + right.Add("X-API-Versions", "0.0.1") + right.Add("X-API-Versions", "0.0.2") + + merged := MergeHeaders(left, right) + assert.Equal(t, []string{"0.0.1", "0.0.2"}, merged.Values("X-API-Versions")) + }) + + t.Run("disjoint merge", func(t *testing.T) { + left := make(http.Header) + left.Set("X-API-Tenancy", "test") + + right := make(http.Header) + right.Set("X-API-Version", "0.0.1") + + merged := MergeHeaders(left, right) + assert.Equal(t, []string{"test"}, merged.Values("X-API-Tenancy")) + assert.Equal(t, []string{"0.0.1"}, merged.Values("X-API-Version")) + }) +} + +// newTestServer returns a new *httptest.Server configured with the +// given test parameters. +func newTestServer(t *testing.T, tc *InternalTestCase) *httptest.Server { + return httptest.NewServer( + http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, tc.giveMethod, r.Method) + assert.Equal(t, contentType, r.Header.Get(contentTypeHeader)) + for header, value := range tc.giveHeader { + assert.Equal(t, value, r.Header.Values(header)) + } + + request := new(InternalTestRequest) + + bytes, err := io.ReadAll(r.Body) + if tc.giveRequest == nil { + require.Empty(t, bytes) + w.WriteHeader(http.StatusBadRequest) + _, err = w.Write([]byte("invalid request")) + require.NoError(t, err) + return + } + require.NoError(t, err) + require.NoError(t, json.Unmarshal(bytes, request)) + + switch request.Id { + case strconv.Itoa(http.StatusNotFound): + notFoundError := &InternalTestNotFoundError{ + APIError: &core.APIError{ + StatusCode: http.StatusNotFound, + }, + Message: fmt.Sprintf("ID %q not found", request.Id), + } + bytes, err = json.Marshal(notFoundError) + require.NoError(t, err) + + w.WriteHeader(http.StatusNotFound) + _, err = w.Write(bytes) + require.NoError(t, err) + return + + case strconv.Itoa(http.StatusInternalServerError): + w.WriteHeader(http.StatusInternalServerError) + _, err = w.Write([]byte("failed to process request")) + require.NoError(t, err) + return + } + + if tc.giveResponseIsOptional { + w.WriteHeader(http.StatusOK) + return + } + + extraBodyProperties := make(map[string]interface{}) + require.NoError(t, json.Unmarshal(bytes, &extraBodyProperties)) + delete(extraBodyProperties, "id") + + response := &InternalTestResponse{ + Id: request.Id, + ExtraBodyProperties: extraBodyProperties, + QueryParameters: r.URL.Query(), + } + bytes, err = json.Marshal(response) + require.NoError(t, err) + + _, err = w.Write(bytes) + require.NoError(t, err) + }, + ), + ) +} + +// newTestErrorDecoder returns an error decoder suitable for tests. +func newTestErrorDecoder(t *testing.T) func(int, http.Header, io.Reader) error { + return func(statusCode int, header http.Header, body io.Reader) error { + raw, err := io.ReadAll(body) + require.NoError(t, err) + + var ( + apiError = core.NewAPIError(statusCode, header, errors.New(string(raw))) + decoder = json.NewDecoder(bytes.NewReader(raw)) + ) + if statusCode == http.StatusNotFound { + value := new(InternalTestNotFoundError) + value.APIError = apiError + require.NoError(t, decoder.Decode(value)) + + return value + } + return apiError + } +} diff --git a/seed/go-sdk/streaming/internal/error_decoder.go b/seed/go-sdk/streaming/internal/error_decoder.go new file mode 100644 index 000000000000..e04ba8093068 --- /dev/null +++ b/seed/go-sdk/streaming/internal/error_decoder.go @@ -0,0 +1,64 @@ +package internal + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + + "github.com/fern-api/stream-go/v2/core" +) + +// ErrorCodes maps HTTP status codes to error constructors. +type ErrorCodes map[int]func(*core.APIError) error + +// ErrorDecoder decodes *http.Response errors and returns a +// typed API error (e.g. *core.APIError). +type ErrorDecoder func(statusCode int, header http.Header, body io.Reader) error + +// NewErrorDecoder returns a new ErrorDecoder backed by the given error codes. +// errorCodesOverrides is optional and will be merged with the default error codes, +// with overrides taking precedence. +func NewErrorDecoder(errorCodes ErrorCodes, errorCodesOverrides ...ErrorCodes) ErrorDecoder { + // Merge default error codes with overrides + mergedErrorCodes := make(ErrorCodes) + + // Start with default error codes + for statusCode, errorFunc := range errorCodes { + mergedErrorCodes[statusCode] = errorFunc + } + + // Apply overrides if provided + if len(errorCodesOverrides) > 0 && errorCodesOverrides[0] != nil { + for statusCode, errorFunc := range errorCodesOverrides[0] { + mergedErrorCodes[statusCode] = errorFunc + } + } + + return func(statusCode int, header http.Header, body io.Reader) error { + raw, err := io.ReadAll(body) + if err != nil { + return fmt.Errorf("failed to read error from response body: %w", err) + } + apiError := core.NewAPIError( + statusCode, + header, + errors.New(string(raw)), + ) + newErrorFunc, ok := mergedErrorCodes[statusCode] + if !ok { + // This status code isn't recognized, so we return + // the API error as-is. + return apiError + } + customError := newErrorFunc(apiError) + if err := json.NewDecoder(bytes.NewReader(raw)).Decode(customError); err != nil { + // If we fail to decode the error, we return the + // API error as-is. + return apiError + } + return customError + } +} diff --git a/seed/go-sdk/streaming/internal/error_decoder_test.go b/seed/go-sdk/streaming/internal/error_decoder_test.go new file mode 100644 index 000000000000..604c09120ac5 --- /dev/null +++ b/seed/go-sdk/streaming/internal/error_decoder_test.go @@ -0,0 +1,59 @@ +package internal + +import ( + "bytes" + "errors" + "net/http" + "testing" + + "github.com/fern-api/stream-go/v2/core" + "github.com/stretchr/testify/assert" +) + +func TestErrorDecoder(t *testing.T) { + decoder := NewErrorDecoder( + ErrorCodes{ + http.StatusNotFound: func(apiError *core.APIError) error { + return &InternalTestNotFoundError{APIError: apiError} + }, + }) + + tests := []struct { + description string + giveStatusCode int + giveHeader http.Header + giveBody string + wantError error + }{ + { + description: "unrecognized status code", + giveStatusCode: http.StatusInternalServerError, + giveHeader: http.Header{}, + giveBody: "Internal Server Error", + wantError: core.NewAPIError(http.StatusInternalServerError, http.Header{}, errors.New("Internal Server Error")), + }, + { + description: "not found with valid JSON", + giveStatusCode: http.StatusNotFound, + giveHeader: http.Header{}, + giveBody: `{"message": "Resource not found"}`, + wantError: &InternalTestNotFoundError{ + APIError: core.NewAPIError(http.StatusNotFound, http.Header{}, errors.New(`{"message": "Resource not found"}`)), + Message: "Resource not found", + }, + }, + { + description: "not found with invalid JSON", + giveStatusCode: http.StatusNotFound, + giveHeader: http.Header{}, + giveBody: `Resource not found`, + wantError: core.NewAPIError(http.StatusNotFound, http.Header{}, errors.New("Resource not found")), + }, + } + + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + assert.Equal(t, tt.wantError, decoder(tt.giveStatusCode, tt.giveHeader, bytes.NewReader([]byte(tt.giveBody)))) + }) + } +} diff --git a/seed/go-sdk/streaming/internal/explicit_fields.go b/seed/go-sdk/streaming/internal/explicit_fields.go new file mode 100644 index 000000000000..4bdf34fc2b7c --- /dev/null +++ b/seed/go-sdk/streaming/internal/explicit_fields.go @@ -0,0 +1,116 @@ +package internal + +import ( + "math/big" + "reflect" + "strings" +) + +// HandleExplicitFields processes a struct to remove `omitempty` from +// fields that have been explicitly set (as indicated by their corresponding bit in explicitFields). +// Note that `marshaler` should be an embedded struct to avoid infinite recursion. +// Returns an interface{} that can be passed to json.Marshal. +func HandleExplicitFields(marshaler interface{}, explicitFields *big.Int) interface{} { + val := reflect.ValueOf(marshaler) + typ := reflect.TypeOf(marshaler) + + // Handle pointer types + if val.Kind() == reflect.Ptr { + if val.IsNil() { + return nil + } + val = val.Elem() + typ = typ.Elem() + } + + // Only handle struct types + if val.Kind() != reflect.Struct { + return marshaler + } + + // Handle embedded struct pattern + var sourceVal reflect.Value + var sourceType reflect.Type + + // Check if this is an embedded struct pattern + if typ.NumField() == 1 && typ.Field(0).Anonymous { + // This is likely an embedded struct, get the embedded value + embeddedField := val.Field(0) + sourceVal = embeddedField + sourceType = embeddedField.Type() + } else { + // Regular struct + sourceVal = val + sourceType = typ + } + + // If no explicit fields set, use standard marshaling + if explicitFields == nil || explicitFields.Sign() == 0 { + return marshaler + } + + // Create a new struct type with modified tags + fields := make([]reflect.StructField, 0, sourceType.NumField()) + + for i := 0; i < sourceType.NumField(); i++ { + field := sourceType.Field(i) + + // Skip unexported fields and the explicitFields field itself + if !field.IsExported() || field.Name == "explicitFields" { + continue + } + + // Check if this field has been explicitly set + fieldBit := big.NewInt(1) + fieldBit.Lsh(fieldBit, uint(i)) + if big.NewInt(0).And(explicitFields, fieldBit).Sign() != 0 { + // Remove omitempty from the json tag + tag := field.Tag.Get("json") + if tag != "" && tag != "-" { + // Parse the json tag, remove omitempty from options + parts := strings.Split(tag, ",") + if len(parts) > 1 { + var newParts []string + newParts = append(newParts, parts[0]) // Keep the field name + for _, part := range parts[1:] { + if strings.TrimSpace(part) != "omitempty" { + newParts = append(newParts, part) + } + } + tag = strings.Join(newParts, ",") + } + + // Reconstruct the struct tag + newTag := `json:"` + tag + `"` + if urlTag := field.Tag.Get("url"); urlTag != "" { + newTag += ` url:"` + urlTag + `"` + } + + field.Tag = reflect.StructTag(newTag) + } + } + + fields = append(fields, field) + } + + // Create new struct type with modified tags + newType := reflect.StructOf(fields) + newVal := reflect.New(newType).Elem() + + // Copy field values from original struct to new struct + fieldIndex := 0 + for i := 0; i < sourceType.NumField(); i++ { + originalField := sourceType.Field(i) + + // Skip unexported fields and the explicitFields field itself + if !originalField.IsExported() || originalField.Name == "explicitFields" { + continue + } + + originalValue := sourceVal.Field(i) + newVal.Field(fieldIndex).Set(originalValue) + fieldIndex++ + } + + return newVal.Interface() +} diff --git a/seed/go-sdk/streaming/internal/explicit_fields_test.go b/seed/go-sdk/streaming/internal/explicit_fields_test.go new file mode 100644 index 000000000000..3d05e88a2ce9 --- /dev/null +++ b/seed/go-sdk/streaming/internal/explicit_fields_test.go @@ -0,0 +1,497 @@ +package internal + +import ( + "encoding/json" + "math/big" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type testExplicitFieldsStruct struct { + Name *string `json:"name,omitempty"` + Code *string `json:"code,omitempty"` + Count *int `json:"count,omitempty"` + Enabled *bool `json:"enabled,omitempty"` + Tags []string `json:"tags,omitempty"` + //lint:ignore unused this field is intentionally unused for testing + unexported string `json:"-"` + explicitFields *big.Int `json:"-"` +} + +var ( + testFieldName = big.NewInt(1 << 0) + testFieldCode = big.NewInt(1 << 1) + testFieldCount = big.NewInt(1 << 2) + testFieldEnabled = big.NewInt(1 << 3) + testFieldTags = big.NewInt(1 << 4) +) + +func (t *testExplicitFieldsStruct) require(field *big.Int) { + if t.explicitFields == nil { + t.explicitFields = big.NewInt(0) + } + t.explicitFields.Or(t.explicitFields, field) +} + +func (t *testExplicitFieldsStruct) SetName(name *string) { + t.Name = name + t.require(testFieldName) +} + +func (t *testExplicitFieldsStruct) SetCode(code *string) { + t.Code = code + t.require(testFieldCode) +} + +func (t *testExplicitFieldsStruct) SetCount(count *int) { + t.Count = count + t.require(testFieldCount) +} + +func (t *testExplicitFieldsStruct) SetEnabled(enabled *bool) { + t.Enabled = enabled + t.require(testFieldEnabled) +} + +func (t *testExplicitFieldsStruct) SetTags(tags []string) { + t.Tags = tags + t.require(testFieldTags) +} + +func (t *testExplicitFieldsStruct) MarshalJSON() ([]byte, error) { + type embed testExplicitFieldsStruct + var marshaler = struct { + embed + }{ + embed: embed(*t), + } + return json.Marshal(HandleExplicitFields(marshaler, t.explicitFields)) +} + +type testStructWithoutExplicitFields struct { + Name *string `json:"name,omitempty"` + Code *string `json:"code,omitempty"` +} + +func TestHandleExplicitFields(t *testing.T) { + tests := []struct { + desc string + giveInput interface{} + wantBytes []byte + wantError string + }{ + { + desc: "nil input", + giveInput: nil, + wantBytes: []byte(`null`), + }, + { + desc: "non-struct input", + giveInput: "string", + wantBytes: []byte(`"string"`), + }, + { + desc: "slice input", + giveInput: []string{"a", "b"}, + wantBytes: []byte(`["a","b"]`), + }, + { + desc: "map input", + giveInput: map[string]interface{}{"key": "value"}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "struct without explicitFields field", + giveInput: &testStructWithoutExplicitFields{ + Name: stringPtr("test"), + Code: nil, + }, + wantBytes: []byte(`{"name":"test"}`), + }, + { + desc: "struct with no explicit fields set", + giveInput: &testExplicitFieldsStruct{ + Name: stringPtr("test"), + Code: nil, + }, + wantBytes: []byte(`{"name":"test"}`), + }, + { + desc: "struct with explicit nil field", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{ + Name: stringPtr("test"), + } + s.SetCode(nil) + return s + }(), + wantBytes: []byte(`{"name":"test","code":null}`), + }, + { + desc: "struct with explicit non-nil field", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{} + s.SetName(stringPtr("explicit")) + s.SetCode(stringPtr("also-explicit")) + return s + }(), + wantBytes: []byte(`{"name":"explicit","code":"also-explicit"}`), + }, + { + desc: "struct with mixed explicit and implicit fields", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{ + Name: stringPtr("implicit"), + Count: intPtr(42), + } + s.SetCode(nil) // explicit nil + return s + }(), + wantBytes: []byte(`{"name":"implicit","code":null,"count":42}`), + }, + { + desc: "struct with multiple explicit nil fields", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{ + Name: stringPtr("test"), + } + s.SetCode(nil) + s.SetCount(nil) + return s + }(), + wantBytes: []byte(`{"name":"test","code":null,"count":null}`), + }, + { + desc: "struct with slice field", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{ + Tags: []string{"tag1", "tag2"}, + } + s.SetTags(nil) // explicit nil slice + return s + }(), + wantBytes: []byte(`{"tags":null}`), + }, + { + desc: "struct with boolean field", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{} + s.SetEnabled(boolPtr(false)) // explicit false + return s + }(), + wantBytes: []byte(`{"enabled":false}`), + }, + { + desc: "struct with all fields explicit", + giveInput: func() *testExplicitFieldsStruct { + s := &testExplicitFieldsStruct{} + s.SetName(stringPtr("test")) + s.SetCode(nil) + s.SetCount(intPtr(0)) + s.SetEnabled(boolPtr(false)) + s.SetTags([]string{}) + return s + }(), + wantBytes: []byte(`{"name":"test","code":null,"count":0,"enabled":false,"tags":[]}`), + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + var explicitFields *big.Int + if s, ok := tt.giveInput.(*testExplicitFieldsStruct); ok { + explicitFields = s.explicitFields + } + bytes, err := json.Marshal(HandleExplicitFields(tt.giveInput, explicitFields)) + if tt.wantError != "" { + require.EqualError(t, err, tt.wantError) + assert.Nil(t, tt.wantBytes) + return + } + require.NoError(t, err) + assert.JSONEq(t, string(tt.wantBytes), string(bytes)) + + // Verify it's valid JSON + var value interface{} + require.NoError(t, json.Unmarshal(bytes, &value)) + }) + } +} + +func TestHandleExplicitFieldsCustomMarshaler(t *testing.T) { + t.Run("custom marshaler with explicit fields", func(t *testing.T) { + s := &testExplicitFieldsStruct{} + s.SetName(nil) + s.SetCode(stringPtr("test-code")) + + bytes, err := s.MarshalJSON() + require.NoError(t, err) + assert.JSONEq(t, `{"name":null,"code":"test-code"}`, string(bytes)) + }) + + t.Run("custom marshaler with no explicit fields", func(t *testing.T) { + s := &testExplicitFieldsStruct{ + Name: stringPtr("implicit"), + Code: stringPtr("also-implicit"), + } + + bytes, err := s.MarshalJSON() + require.NoError(t, err) + assert.JSONEq(t, `{"name":"implicit","code":"also-implicit"}`, string(bytes)) + }) +} + +func TestHandleExplicitFieldsPointerHandling(t *testing.T) { + t.Run("nil pointer", func(t *testing.T) { + var s *testExplicitFieldsStruct + bytes, err := json.Marshal(HandleExplicitFields(s, nil)) + require.NoError(t, err) + assert.Equal(t, []byte(`null`), bytes) + }) + + t.Run("pointer to struct", func(t *testing.T) { + s := &testExplicitFieldsStruct{} + s.SetName(nil) + + bytes, err := json.Marshal(HandleExplicitFields(s, s.explicitFields)) + require.NoError(t, err) + assert.JSONEq(t, `{"name":null}`, string(bytes)) + }) +} + +func TestHandleExplicitFieldsEmbeddedStruct(t *testing.T) { + t.Run("embedded struct with explicit fields", func(t *testing.T) { + // Create a struct similar to what MarshalJSON creates + s := &testExplicitFieldsStruct{} + s.SetName(nil) + s.SetCode(stringPtr("test-code")) + + type embed testExplicitFieldsStruct + var marshaler = struct { + embed + }{ + embed: embed(*s), + } + + bytes, err := json.Marshal(HandleExplicitFields(marshaler, s.explicitFields)) + require.NoError(t, err) + // Should include both explicit fields (name as null, code as "test-code") + assert.JSONEq(t, `{"name":null,"code":"test-code"}`, string(bytes)) + }) + + t.Run("embedded struct with no explicit fields", func(t *testing.T) { + s := &testExplicitFieldsStruct{ + Name: stringPtr("implicit"), + Code: stringPtr("also-implicit"), + } + + type embed testExplicitFieldsStruct + var marshaler = struct { + embed + }{ + embed: embed(*s), + } + + bytes, err := json.Marshal(HandleExplicitFields(marshaler, s.explicitFields)) + require.NoError(t, err) + // Should only include non-nil fields (omitempty behavior) + assert.JSONEq(t, `{"name":"implicit","code":"also-implicit"}`, string(bytes)) + }) + + t.Run("embedded struct with mixed fields", func(t *testing.T) { + s := &testExplicitFieldsStruct{ + Count: intPtr(42), // implicit field + } + s.SetName(nil) // explicit nil + s.SetCode(stringPtr("explicit")) // explicit value + + type embed testExplicitFieldsStruct + var marshaler = struct { + embed + }{ + embed: embed(*s), + } + + bytes, err := json.Marshal(HandleExplicitFields(marshaler, s.explicitFields)) + require.NoError(t, err) + // Should include explicit null, explicit value, and implicit value + assert.JSONEq(t, `{"name":null,"code":"explicit","count":42}`, string(bytes)) + }) +} + +func TestHandleExplicitFieldsTagHandling(t *testing.T) { + type testStructWithComplexTags struct { + Field1 *string `json:"field1,omitempty" url:"field1,omitempty"` + Field2 *string `json:"field2,omitempty,string" url:"field2"` + Field3 *string `json:"-"` + Field4 *string `json:"field4"` + explicitFields *big.Int `json:"-"` + } + + s := &testStructWithComplexTags{ + Field1: stringPtr("test1"), + Field4: stringPtr("test4"), + explicitFields: big.NewInt(1), // Only first field is explicit + } + + bytes, err := json.Marshal(HandleExplicitFields(s, s.explicitFields)) + require.NoError(t, err) + + // Field1 should have omitempty removed, Field2 should keep omitempty, Field4 should be included + assert.JSONEq(t, `{"field1":"test1","field4":"test4"}`, string(bytes)) +} + +// Test types for nested struct explicit fields testing +type testNestedStruct struct { + NestedName *string `json:"nested_name,omitempty"` + NestedCode *string `json:"nested_code,omitempty"` + explicitFields *big.Int `json:"-"` +} + +type testParentStruct struct { + ParentName *string `json:"parent_name,omitempty"` + Nested *testNestedStruct `json:"nested,omitempty"` + explicitFields *big.Int `json:"-"` +} + +var ( + nestedFieldName = big.NewInt(1 << 0) + nestedFieldCode = big.NewInt(1 << 1) +) + +var ( + parentFieldName = big.NewInt(1 << 0) + parentFieldNested = big.NewInt(1 << 1) +) + +func (n *testNestedStruct) require(field *big.Int) { + if n.explicitFields == nil { + n.explicitFields = big.NewInt(0) + } + n.explicitFields.Or(n.explicitFields, field) +} + +func (n *testNestedStruct) SetNestedName(name *string) { + n.NestedName = name + n.require(nestedFieldName) +} + +func (n *testNestedStruct) SetNestedCode(code *string) { + n.NestedCode = code + n.require(nestedFieldCode) +} + +func (n *testNestedStruct) MarshalJSON() ([]byte, error) { + type embed testNestedStruct + var marshaler = struct { + embed + }{ + embed: embed(*n), + } + return json.Marshal(HandleExplicitFields(marshaler, n.explicitFields)) +} + +func (p *testParentStruct) require(field *big.Int) { + if p.explicitFields == nil { + p.explicitFields = big.NewInt(0) + } + p.explicitFields.Or(p.explicitFields, field) +} + +func (p *testParentStruct) SetParentName(name *string) { + p.ParentName = name + p.require(parentFieldName) +} + +func (p *testParentStruct) SetNested(nested *testNestedStruct) { + p.Nested = nested + p.require(parentFieldNested) +} + +func (p *testParentStruct) MarshalJSON() ([]byte, error) { + type embed testParentStruct + var marshaler = struct { + embed + }{ + embed: embed(*p), + } + return json.Marshal(HandleExplicitFields(marshaler, p.explicitFields)) +} + +func TestHandleExplicitFieldsNestedStruct(t *testing.T) { + tests := []struct { + desc string + setupFunc func() *testParentStruct + wantBytes []byte + }{ + { + desc: "nested struct with explicit nil in nested object", + setupFunc: func() *testParentStruct { + nested := &testNestedStruct{ + NestedName: stringPtr("implicit-nested"), + } + nested.SetNestedCode(nil) // explicit nil + + return &testParentStruct{ + ParentName: stringPtr("implicit-parent"), + Nested: nested, + } + }, + wantBytes: []byte(`{"parent_name":"implicit-parent","nested":{"nested_name":"implicit-nested","nested_code":null}}`), + }, + { + desc: "parent with explicit nil nested struct", + setupFunc: func() *testParentStruct { + parent := &testParentStruct{ + ParentName: stringPtr("implicit-parent"), + } + parent.SetNested(nil) // explicit nil nested struct + return parent + }, + wantBytes: []byte(`{"parent_name":"implicit-parent","nested":null}`), + }, + { + desc: "all explicit fields in nested structure", + setupFunc: func() *testParentStruct { + nested := &testNestedStruct{} + nested.SetNestedName(stringPtr("explicit-nested")) + nested.SetNestedCode(nil) // explicit nil + + parent := &testParentStruct{} + parent.SetParentName(nil) // explicit nil + parent.SetNested(nested) // explicit nested struct + + return parent + }, + wantBytes: []byte(`{"parent_name":null,"nested":{"nested_name":"explicit-nested","nested_code":null}}`), + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + parent := tt.setupFunc() + bytes, err := parent.MarshalJSON() + require.NoError(t, err) + assert.JSONEq(t, string(tt.wantBytes), string(bytes)) + + // Verify it's valid JSON + var value interface{} + require.NoError(t, json.Unmarshal(bytes, &value)) + }) + } +} + +// Helper functions +func stringPtr(s string) *string { + return &s +} + +func intPtr(i int) *int { + return &i +} + +func boolPtr(b bool) *bool { + return &b +} diff --git a/seed/go-sdk/streaming/internal/extra_properties.go b/seed/go-sdk/streaming/internal/extra_properties.go new file mode 100644 index 000000000000..540c3fd89eeb --- /dev/null +++ b/seed/go-sdk/streaming/internal/extra_properties.go @@ -0,0 +1,141 @@ +package internal + +import ( + "bytes" + "encoding/json" + "fmt" + "reflect" + "strings" +) + +// MarshalJSONWithExtraProperty marshals the given value to JSON, including the extra property. +func MarshalJSONWithExtraProperty(marshaler interface{}, key string, value interface{}) ([]byte, error) { + return MarshalJSONWithExtraProperties(marshaler, map[string]interface{}{key: value}) +} + +// MarshalJSONWithExtraProperties marshals the given value to JSON, including any extra properties. +func MarshalJSONWithExtraProperties(marshaler interface{}, extraProperties map[string]interface{}) ([]byte, error) { + bytes, err := json.Marshal(marshaler) + if err != nil { + return nil, err + } + if len(extraProperties) == 0 { + return bytes, nil + } + keys, err := getKeys(marshaler) + if err != nil { + return nil, err + } + for _, key := range keys { + if _, ok := extraProperties[key]; ok { + return nil, fmt.Errorf("cannot add extra property %q because it is already defined on the type", key) + } + } + extraBytes, err := json.Marshal(extraProperties) + if err != nil { + return nil, err + } + if isEmptyJSON(bytes) { + if isEmptyJSON(extraBytes) { + return bytes, nil + } + return extraBytes, nil + } + result := bytes[:len(bytes)-1] + result = append(result, ',') + result = append(result, extraBytes[1:len(extraBytes)-1]...) + result = append(result, '}') + return result, nil +} + +// ExtractExtraProperties extracts any extra properties from the given value. +func ExtractExtraProperties(bytes []byte, value interface{}, exclude ...string) (map[string]interface{}, error) { + val := reflect.ValueOf(value) + for val.Kind() == reflect.Ptr { + if val.IsNil() { + return nil, fmt.Errorf("value must be non-nil to extract extra properties") + } + val = val.Elem() + } + if err := json.Unmarshal(bytes, &value); err != nil { + return nil, err + } + var extraProperties map[string]interface{} + if err := json.Unmarshal(bytes, &extraProperties); err != nil { + return nil, err + } + for i := 0; i < val.Type().NumField(); i++ { + key := jsonKey(val.Type().Field(i)) + if key == "" || key == "-" { + continue + } + delete(extraProperties, key) + } + for _, key := range exclude { + delete(extraProperties, key) + } + if len(extraProperties) == 0 { + return nil, nil + } + return extraProperties, nil +} + +// getKeys returns the keys associated with the given value. The value must be a +// a struct or a map with string keys. +func getKeys(value interface{}) ([]string, error) { + val := reflect.ValueOf(value) + if val.Kind() == reflect.Ptr { + val = val.Elem() + } + if !val.IsValid() { + return nil, nil + } + switch val.Kind() { + case reflect.Struct: + return getKeysForStructType(val.Type()), nil + case reflect.Map: + var keys []string + if val.Type().Key().Kind() != reflect.String { + return nil, fmt.Errorf("cannot extract keys from %T; only structs and maps with string keys are supported", value) + } + for _, key := range val.MapKeys() { + keys = append(keys, key.String()) + } + return keys, nil + default: + return nil, fmt.Errorf("cannot extract keys from %T; only structs and maps with string keys are supported", value) + } +} + +// getKeysForStructType returns all the keys associated with the given struct type, +// visiting embedded fields recursively. +func getKeysForStructType(structType reflect.Type) []string { + if structType.Kind() == reflect.Pointer { + structType = structType.Elem() + } + if structType.Kind() != reflect.Struct { + return nil + } + var keys []string + for i := 0; i < structType.NumField(); i++ { + field := structType.Field(i) + if field.Anonymous { + keys = append(keys, getKeysForStructType(field.Type)...) + continue + } + keys = append(keys, jsonKey(field)) + } + return keys +} + +// jsonKey returns the JSON key from the struct tag of the given field, +// excluding the omitempty flag (if any). +func jsonKey(field reflect.StructField) string { + return strings.TrimSuffix(field.Tag.Get("json"), ",omitempty") +} + +// isEmptyJSON returns true if the given data is empty, the empty JSON object, or +// an explicit null. +func isEmptyJSON(data []byte) bool { + return len(data) <= 2 || bytes.Equal(data, []byte("null")) +} diff --git a/seed/go-sdk/streaming/internal/extra_properties_test.go b/seed/go-sdk/streaming/internal/extra_properties_test.go new file mode 100644 index 000000000000..aa2510ee5121 --- /dev/null +++ b/seed/go-sdk/streaming/internal/extra_properties_test.go @@ -0,0 +1,228 @@ +package internal + +import ( + "encoding/json" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type testMarshaler struct { + Name string `json:"name"` + BirthDate time.Time `json:"birthDate"` + CreatedAt time.Time `json:"created_at"` +} + +func (t *testMarshaler) MarshalJSON() ([]byte, error) { + type embed testMarshaler + var marshaler = struct { + embed + BirthDate string `json:"birthDate"` + CreatedAt string `json:"created_at"` + }{ + embed: embed(*t), + BirthDate: t.BirthDate.Format("2006-01-02"), + CreatedAt: t.CreatedAt.Format(time.RFC3339), + } + return MarshalJSONWithExtraProperty(marshaler, "type", "test") +} + +func TestMarshalJSONWithExtraProperties(t *testing.T) { + tests := []struct { + desc string + giveMarshaler interface{} + giveExtraProperties map[string]interface{} + wantBytes []byte + wantError string + }{ + { + desc: "invalid type", + giveMarshaler: []string{"invalid"}, + giveExtraProperties: map[string]interface{}{"key": "overwrite"}, + wantError: `cannot extract keys from []string; only structs and maps with string keys are supported`, + }, + { + desc: "invalid key type", + giveMarshaler: map[int]interface{}{42: "value"}, + giveExtraProperties: map[string]interface{}{"key": "overwrite"}, + wantError: `cannot extract keys from map[int]interface {}; only structs and maps with string keys are supported`, + }, + { + desc: "invalid map overwrite", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{"key": "overwrite"}, + wantError: `cannot add extra property "key" because it is already defined on the type`, + }, + { + desc: "invalid struct overwrite", + giveMarshaler: new(testMarshaler), + giveExtraProperties: map[string]interface{}{"birthDate": "2000-01-01"}, + wantError: `cannot add extra property "birthDate" because it is already defined on the type`, + }, + { + desc: "invalid struct overwrite embedded type", + giveMarshaler: new(testMarshaler), + giveExtraProperties: map[string]interface{}{"name": "bob"}, + wantError: `cannot add extra property "name" because it is already defined on the type`, + }, + { + desc: "nil", + giveMarshaler: nil, + giveExtraProperties: nil, + wantBytes: []byte(`null`), + }, + { + desc: "empty", + giveMarshaler: map[string]interface{}{}, + giveExtraProperties: map[string]interface{}{}, + wantBytes: []byte(`{}`), + }, + { + desc: "no extra properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "only extra properties", + giveMarshaler: map[string]interface{}{}, + giveExtraProperties: map[string]interface{}{"key": "value"}, + wantBytes: []byte(`{"key":"value"}`), + }, + { + desc: "single extra property", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{"extra": "property"}, + wantBytes: []byte(`{"key":"value","extra":"property"}`), + }, + { + desc: "multiple extra properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{"one": 1, "two": 2}, + wantBytes: []byte(`{"key":"value","one":1,"two":2}`), + }, + { + desc: "nested properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{ + "user": map[string]interface{}{ + "age": 42, + "name": "alice", + }, + }, + wantBytes: []byte(`{"key":"value","user":{"age":42,"name":"alice"}}`), + }, + { + desc: "multiple nested properties", + giveMarshaler: map[string]interface{}{"key": "value"}, + giveExtraProperties: map[string]interface{}{ + "metadata": map[string]interface{}{ + "ip": "127.0.0.1", + }, + "user": map[string]interface{}{ + "age": 42, + "name": "alice", + }, + }, + wantBytes: []byte(`{"key":"value","metadata":{"ip":"127.0.0.1"},"user":{"age":42,"name":"alice"}}`), + }, + { + desc: "custom marshaler", + giveMarshaler: &testMarshaler{ + Name: "alice", + BirthDate: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), + CreatedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC), + }, + giveExtraProperties: map[string]interface{}{ + "extra": "property", + }, + wantBytes: []byte(`{"name":"alice","birthDate":"2000-01-01","created_at":"2024-01-01T00:00:00Z","type":"test","extra":"property"}`), + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + bytes, err := MarshalJSONWithExtraProperties(tt.giveMarshaler, tt.giveExtraProperties) + if tt.wantError != "" { + require.EqualError(t, err, tt.wantError) + assert.Nil(t, tt.wantBytes) + return + } + require.NoError(t, err) + assert.Equal(t, tt.wantBytes, bytes) + + value := make(map[string]interface{}) + require.NoError(t, json.Unmarshal(bytes, &value)) + }) + } +} + +func TestExtractExtraProperties(t *testing.T) { + t.Run("none", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice"}`), value) + require.NoError(t, err) + assert.Nil(t, extraProperties) + }) + + t.Run("non-nil pointer", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]interface{}{"age": float64(42)}, extraProperties) + }) + + t.Run("nil pointer", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + var value *user + _, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + assert.EqualError(t, err, "value must be non-nil to extract extra properties") + }) + + t.Run("non-zero value", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]interface{}{"age": float64(42)}, extraProperties) + }) + + t.Run("zero value", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + var value user + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) + require.NoError(t, err) + assert.Equal(t, map[string]interface{}{"age": float64(42)}, extraProperties) + }) + + t.Run("exclude", func(t *testing.T) { + type user struct { + Name string `json:"name"` + } + value := &user{ + Name: "alice", + } + extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value, "age") + require.NoError(t, err) + assert.Nil(t, extraProperties) + }) +} diff --git a/seed/go-sdk/streaming/internal/http.go b/seed/go-sdk/streaming/internal/http.go new file mode 100644 index 000000000000..77863752bb58 --- /dev/null +++ b/seed/go-sdk/streaming/internal/http.go @@ -0,0 +1,71 @@ +package internal + +import ( + "fmt" + "net/http" + "net/url" + "reflect" +) + +// HTTPClient is an interface for a subset of the *http.Client. +type HTTPClient interface { + Do(*http.Request) (*http.Response, error) +} + +// ResolveBaseURL resolves the base URL from the given arguments, +// preferring the first non-empty value. +func ResolveBaseURL(values ...string) string { + for _, value := range values { + if value != "" { + return value + } + } + return "" +} + +// EncodeURL encodes the given arguments into the URL, escaping +// values as needed. Pointer arguments are dereferenced before processing. +func EncodeURL(urlFormat string, args ...interface{}) string { + escapedArgs := make([]interface{}, 0, len(args)) + for _, arg := range args { + // Dereference the argument if it's a pointer + value := dereferenceArg(arg) + escapedArgs = append(escapedArgs, url.PathEscape(fmt.Sprintf("%v", value))) + } + return fmt.Sprintf(urlFormat, escapedArgs...) +} + +// dereferenceArg dereferences a pointer argument if necessary, returning the underlying value. +// If the argument is not a pointer or is nil, it returns the argument as-is. +func dereferenceArg(arg interface{}) interface{} { + if arg == nil { + return arg + } + + v := reflect.ValueOf(arg) + + // Keep dereferencing until we get to a non-pointer value or hit nil + for v.Kind() == reflect.Ptr { + if v.IsNil() { + return nil + } + v = v.Elem() + } + + return v.Interface() +} + +// MergeHeaders merges the given headers together, where the right +// takes precedence over the left. +func MergeHeaders(left, right http.Header) http.Header { + for key, values := range right { + if len(values) > 1 { + left[key] = values + continue + } + if value := right.Get(key); value != "" { + left.Set(key, value) + } + } + return left +} diff --git a/seed/go-sdk/streaming/internal/query.go b/seed/go-sdk/streaming/internal/query.go new file mode 100644 index 000000000000..1cbaf7fe1c02 --- /dev/null +++ b/seed/go-sdk/streaming/internal/query.go @@ -0,0 +1,353 @@ +package internal + +import ( + "encoding/base64" + "fmt" + "net/url" + "reflect" + "strings" + "time" + + "github.com/google/uuid" +) + +var ( + bytesType = reflect.TypeOf([]byte{}) + queryEncoderType = reflect.TypeOf(new(QueryEncoder)).Elem() + timeType = reflect.TypeOf(time.Time{}) + uuidType = reflect.TypeOf(uuid.UUID{}) +) + +// QueryEncoder is an interface implemented by any type that wishes to encode +// itself into URL values in a non-standard way. +type QueryEncoder interface { + EncodeQueryValues(key string, v *url.Values) error +} + +// prepareValue handles common validation and unwrapping logic for both functions +func prepareValue(v interface{}) (reflect.Value, url.Values, error) { + values := make(url.Values) + val := reflect.ValueOf(v) + for val.Kind() == reflect.Ptr { + if val.IsNil() { + return reflect.Value{}, values, nil + } + val = val.Elem() + } + + if v == nil { + return reflect.Value{}, values, nil + } + + if val.Kind() != reflect.Struct { + return reflect.Value{}, nil, fmt.Errorf("query: Values() expects struct input. Got %v", val.Kind()) + } + + err := reflectValue(values, val, "") + if err != nil { + return reflect.Value{}, nil, err + } + + return val, values, nil +} + +// QueryValues encodes url.Values from request objects. +// +// Note: This type is inspired by Google's query encoding library, but +// supports far less customization and is tailored to fit this SDK's use case. +// +// Ref: https://github.com/google/go-querystring +func QueryValues(v interface{}) (url.Values, error) { + _, values, err := prepareValue(v) + return values, err +} + +// QueryValuesWithDefaults encodes url.Values from request objects +// and default values, merging the defaults into the request. +// It's expected that the values of defaults are wire names. +func QueryValuesWithDefaults(v interface{}, defaults map[string]interface{}) (url.Values, error) { + val, values, err := prepareValue(v) + if err != nil { + return values, err + } + if !val.IsValid() { + return values, nil + } + + // apply defaults to zero-value fields directly on the original struct + valType := val.Type() + for i := 0; i < val.NumField(); i++ { + field := val.Field(i) + fieldType := valType.Field(i) + fieldName := fieldType.Name + + if fieldType.PkgPath != "" && !fieldType.Anonymous { + // Skip unexported fields. + continue + } + + // check if field is zero value and we have a default for it + if field.CanSet() && field.IsZero() { + tag := fieldType.Tag.Get("url") + if tag == "" || tag == "-" { + continue + } + wireName, _ := parseTag(tag) + if wireName == "" { + wireName = fieldName + } + if defaultVal, exists := defaults[wireName]; exists { + values.Set(wireName, valueString(reflect.ValueOf(defaultVal), tagOptions{}, reflect.StructField{})) + } + } + } + + return values, err +} + +// reflectValue populates the values parameter from the struct fields in val. +// Embedded structs are followed recursively (using the rules defined in the +// Values function documentation) breadth-first. +func reflectValue(values url.Values, val reflect.Value, scope string) error { + typ := val.Type() + for i := 0; i < typ.NumField(); i++ { + sf := typ.Field(i) + if sf.PkgPath != "" && !sf.Anonymous { + // Skip unexported fields. + continue + } + + sv := val.Field(i) + tag := sf.Tag.Get("url") + if tag == "" || tag == "-" { + continue + } + + name, opts := parseTag(tag) + if name == "" { + name = sf.Name + } + + if scope != "" { + name = scope + "[" + name + "]" + } + + if opts.Contains("omitempty") && isEmptyValue(sv) { + continue + } + + if sv.Type().Implements(queryEncoderType) { + // If sv is a nil pointer and the custom encoder is defined on a non-pointer + // method receiver, set sv to the zero value of the underlying type + if !reflect.Indirect(sv).IsValid() && sv.Type().Elem().Implements(queryEncoderType) { + sv = reflect.New(sv.Type().Elem()) + } + + m := sv.Interface().(QueryEncoder) + if err := m.EncodeQueryValues(name, &values); err != nil { + return err + } + continue + } + + // Recursively dereference pointers, but stop at nil pointers. + for sv.Kind() == reflect.Ptr { + if sv.IsNil() { + break + } + sv = sv.Elem() + } + + if sv.Type() == uuidType || sv.Type() == bytesType || sv.Type() == timeType { + values.Add(name, valueString(sv, opts, sf)) + continue + } + + if sv.Kind() == reflect.Slice || sv.Kind() == reflect.Array { + if sv.Len() == 0 { + // Skip if slice or array is empty. + continue + } + for i := 0; i < sv.Len(); i++ { + value := sv.Index(i) + if isStructPointer(value) && !value.IsNil() { + if err := reflectValue(values, value.Elem(), name); err != nil { + return err + } + } else { + values.Add(name, valueString(value, opts, sf)) + } + } + continue + } + + if sv.Kind() == reflect.Map { + if err := reflectMap(values, sv, name); err != nil { + return err + } + continue + } + + if sv.Kind() == reflect.Struct { + if err := reflectValue(values, sv, name); err != nil { + return err + } + continue + } + + values.Add(name, valueString(sv, opts, sf)) + } + + return nil +} + +// reflectMap handles map types specifically, generating query parameters in the format key[mapkey]=value +func reflectMap(values url.Values, val reflect.Value, scope string) error { + if val.IsNil() { + return nil + } + + iter := val.MapRange() + for iter.Next() { + k := iter.Key() + v := iter.Value() + + key := fmt.Sprint(k.Interface()) + paramName := scope + "[" + key + "]" + + for v.Kind() == reflect.Ptr { + if v.IsNil() { + break + } + v = v.Elem() + } + + for v.Kind() == reflect.Interface { + v = v.Elem() + } + + if v.Kind() == reflect.Map { + if err := reflectMap(values, v, paramName); err != nil { + return err + } + continue + } + + if v.Kind() == reflect.Struct { + if err := reflectValue(values, v, paramName); err != nil { + return err + } + continue + } + + if v.Kind() == reflect.Slice || v.Kind() == reflect.Array { + if v.Len() == 0 { + continue + } + for i := 0; i < v.Len(); i++ { + value := v.Index(i) + if isStructPointer(value) && !value.IsNil() { + if err := reflectValue(values, value.Elem(), paramName); err != nil { + return err + } + } else { + values.Add(paramName, valueString(value, tagOptions{}, reflect.StructField{})) + } + } + continue + } + + values.Add(paramName, valueString(v, tagOptions{}, reflect.StructField{})) + } + + return nil +} + +// valueString returns the string representation of a value. +func valueString(v reflect.Value, opts tagOptions, sf reflect.StructField) string { + for v.Kind() == reflect.Ptr { + if v.IsNil() { + return "" + } + v = v.Elem() + } + + if v.Type() == timeType { + t := v.Interface().(time.Time) + if format := sf.Tag.Get("format"); format == "date" { + return t.Format("2006-01-02") + } + return t.Format(time.RFC3339) + } + + if v.Type() == uuidType { + u := v.Interface().(uuid.UUID) + return u.String() + } + + if v.Type() == bytesType { + b := v.Interface().([]byte) + return base64.StdEncoding.EncodeToString(b) + } + + return fmt.Sprint(v.Interface()) +} + +// isEmptyValue checks if a value should be considered empty for the purposes +// of omitting fields with the "omitempty" option. +func isEmptyValue(v reflect.Value) bool { + type zeroable interface { + IsZero() bool + } + + if !v.IsZero() { + if z, ok := v.Interface().(zeroable); ok { + return z.IsZero() + } + } + + switch v.Kind() { + case reflect.Array, reflect.Map, reflect.Slice, reflect.String: + return v.Len() == 0 + case reflect.Bool: + return !v.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Interface, reflect.Ptr: + return v.IsNil() + case reflect.Invalid, reflect.Complex64, reflect.Complex128, reflect.Chan, reflect.Func, reflect.Struct, reflect.UnsafePointer: + return false + } + + return false +} + +// isStructPointer returns true if the given reflect.Value is a pointer to a struct. +func isStructPointer(v reflect.Value) bool { + return v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct +} + +// tagOptions is the string following a comma in a struct field's "url" tag, or +// the empty string. It does not include the leading comma. +type tagOptions []string + +// parseTag splits a struct field's url tag into its name and comma-separated +// options. +func parseTag(tag string) (string, tagOptions) { + s := strings.Split(tag, ",") + return s[0], s[1:] +} + +// Contains checks whether the tagOptions contains the specified option. +func (o tagOptions) Contains(option string) bool { + for _, s := range o { + if s == option { + return true + } + } + return false +} diff --git a/seed/go-sdk/streaming/internal/query_test.go b/seed/go-sdk/streaming/internal/query_test.go new file mode 100644 index 000000000000..2c28cb8acf68 --- /dev/null +++ b/seed/go-sdk/streaming/internal/query_test.go @@ -0,0 +1,395 @@ +package internal + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestQueryValues(t *testing.T) { + t.Run("empty optional", func(t *testing.T) { + type nested struct { + Value *string `json:"value,omitempty" url:"value,omitempty"` + } + type example struct { + Nested *nested `json:"nested,omitempty" url:"nested,omitempty"` + } + + values, err := QueryValues(&example{}) + require.NoError(t, err) + assert.Empty(t, values) + }) + + t.Run("empty required", func(t *testing.T) { + type nested struct { + Value *string `json:"value,omitempty" url:"value,omitempty"` + } + type example struct { + Required string `json:"required" url:"required"` + Nested *nested `json:"nested,omitempty" url:"nested,omitempty"` + } + + values, err := QueryValues(&example{}) + require.NoError(t, err) + assert.Equal(t, "required=", values.Encode()) + }) + + t.Run("allow multiple", func(t *testing.T) { + type example struct { + Values []string `json:"values" url:"values"` + } + + values, err := QueryValues( + &example{ + Values: []string{"foo", "bar", "baz"}, + }, + ) + require.NoError(t, err) + assert.Equal(t, "values=foo&values=bar&values=baz", values.Encode()) + }) + + t.Run("nested object", func(t *testing.T) { + type nested struct { + Value *string `json:"value,omitempty" url:"value,omitempty"` + } + type example struct { + Required string `json:"required" url:"required"` + Nested *nested `json:"nested,omitempty" url:"nested,omitempty"` + } + + nestedValue := "nestedValue" + values, err := QueryValues( + &example{ + Required: "requiredValue", + Nested: &nested{ + Value: &nestedValue, + }, + }, + ) + require.NoError(t, err) + assert.Equal(t, "nested%5Bvalue%5D=nestedValue&required=requiredValue", values.Encode()) + }) + + t.Run("url unspecified", func(t *testing.T) { + type example struct { + Required string `json:"required" url:"required"` + NotFound string `json:"notFound"` + } + + values, err := QueryValues( + &example{ + Required: "requiredValue", + NotFound: "notFound", + }, + ) + require.NoError(t, err) + assert.Equal(t, "required=requiredValue", values.Encode()) + }) + + t.Run("url ignored", func(t *testing.T) { + type example struct { + Required string `json:"required" url:"required"` + NotFound string `json:"notFound" url:"-"` + } + + values, err := QueryValues( + &example{ + Required: "requiredValue", + NotFound: "notFound", + }, + ) + require.NoError(t, err) + assert.Equal(t, "required=requiredValue", values.Encode()) + }) + + t.Run("datetime", func(t *testing.T) { + type example struct { + DateTime time.Time `json:"dateTime" url:"dateTime"` + } + + values, err := QueryValues( + &example{ + DateTime: time.Date(1994, 3, 16, 12, 34, 56, 0, time.UTC), + }, + ) + require.NoError(t, err) + assert.Equal(t, "dateTime=1994-03-16T12%3A34%3A56Z", values.Encode()) + }) + + t.Run("date", func(t *testing.T) { + type example struct { + Date time.Time `json:"date" url:"date" format:"date"` + } + + values, err := QueryValues( + &example{ + Date: time.Date(1994, 3, 16, 12, 34, 56, 0, time.UTC), + }, + ) + require.NoError(t, err) + assert.Equal(t, "date=1994-03-16", values.Encode()) + }) + + t.Run("optional time", func(t *testing.T) { + type example struct { + Date *time.Time `json:"date,omitempty" url:"date,omitempty" format:"date"` + } + + values, err := QueryValues( + &example{}, + ) + require.NoError(t, err) + assert.Empty(t, values.Encode()) + }) + + t.Run("omitempty with non-pointer zero value", func(t *testing.T) { + type enum string + + type example struct { + Enum enum `json:"enum,omitempty" url:"enum,omitempty"` + } + + values, err := QueryValues( + &example{}, + ) + require.NoError(t, err) + assert.Empty(t, values.Encode()) + }) + + t.Run("object array", func(t *testing.T) { + type object struct { + Key string `json:"key" url:"key"` + Value string `json:"value" url:"value"` + } + type example struct { + Objects []*object `json:"objects,omitempty" url:"objects,omitempty"` + } + + values, err := QueryValues( + &example{ + Objects: []*object{ + { + Key: "hello", + Value: "world", + }, + { + Key: "foo", + Value: "bar", + }, + }, + }, + ) + require.NoError(t, err) + assert.Equal(t, "objects%5Bkey%5D=hello&objects%5Bkey%5D=foo&objects%5Bvalue%5D=world&objects%5Bvalue%5D=bar", values.Encode()) + }) + + t.Run("map", func(t *testing.T) { + type request struct { + Metadata map[string]interface{} `json:"metadata" url:"metadata"` + } + values, err := QueryValues( + &request{ + Metadata: map[string]interface{}{ + "foo": "bar", + "baz": "qux", + }, + }, + ) + require.NoError(t, err) + assert.Equal(t, "metadata%5Bbaz%5D=qux&metadata%5Bfoo%5D=bar", values.Encode()) + }) + + t.Run("nested map", func(t *testing.T) { + type request struct { + Metadata map[string]interface{} `json:"metadata" url:"metadata"` + } + values, err := QueryValues( + &request{ + Metadata: map[string]interface{}{ + "inner": map[string]interface{}{ + "foo": "bar", + }, + }, + }, + ) + require.NoError(t, err) + assert.Equal(t, "metadata%5Binner%5D%5Bfoo%5D=bar", values.Encode()) + }) + + t.Run("nested map array", func(t *testing.T) { + type request struct { + Metadata map[string]interface{} `json:"metadata" url:"metadata"` + } + values, err := QueryValues( + &request{ + Metadata: map[string]interface{}{ + "inner": []string{ + "one", + "two", + "three", + }, + }, + }, + ) + require.NoError(t, err) + assert.Equal(t, "metadata%5Binner%5D=one&metadata%5Binner%5D=two&metadata%5Binner%5D=three", values.Encode()) + }) +} + +func TestQueryValuesWithDefaults(t *testing.T) { + t.Run("apply defaults to zero values", func(t *testing.T) { + type example struct { + Name string `json:"name" url:"name"` + Age int `json:"age" url:"age"` + Enabled bool `json:"enabled" url:"enabled"` + } + + defaults := map[string]interface{}{ + "name": "default-name", + "age": 25, + "enabled": true, + } + + values, err := QueryValuesWithDefaults(&example{}, defaults) + require.NoError(t, err) + assert.Equal(t, "age=25&enabled=true&name=default-name", values.Encode()) + }) + + t.Run("preserve non-zero values over defaults", func(t *testing.T) { + type example struct { + Name string `json:"name" url:"name"` + Age int `json:"age" url:"age"` + Enabled bool `json:"enabled" url:"enabled"` + } + + defaults := map[string]interface{}{ + "name": "default-name", + "age": 25, + "enabled": true, + } + + values, err := QueryValuesWithDefaults(&example{ + Name: "actual-name", + Age: 30, + // Enabled remains false (zero value), should get default + }, defaults) + require.NoError(t, err) + assert.Equal(t, "age=30&enabled=true&name=actual-name", values.Encode()) + }) + + t.Run("ignore defaults for fields not in struct", func(t *testing.T) { + type example struct { + Name string `json:"name" url:"name"` + Age int `json:"age" url:"age"` + } + + defaults := map[string]interface{}{ + "name": "default-name", + "age": 25, + "nonexistent": "should-be-ignored", + } + + values, err := QueryValuesWithDefaults(&example{}, defaults) + require.NoError(t, err) + assert.Equal(t, "age=25&name=default-name", values.Encode()) + }) + + t.Run("type conversion for compatible defaults", func(t *testing.T) { + type example struct { + Count int64 `json:"count" url:"count"` + Rate float64 `json:"rate" url:"rate"` + Message string `json:"message" url:"message"` + } + + defaults := map[string]interface{}{ + "count": int(100), // int -> int64 conversion + "rate": float32(2.5), // float32 -> float64 conversion + "message": "hello", // string -> string (no conversion needed) + } + + values, err := QueryValuesWithDefaults(&example{}, defaults) + require.NoError(t, err) + assert.Equal(t, "count=100&message=hello&rate=2.5", values.Encode()) + }) + + t.Run("mixed with pointer fields and omitempty", func(t *testing.T) { + type example struct { + Required string `json:"required" url:"required"` + Optional *string `json:"optional,omitempty" url:"optional,omitempty"` + Count int `json:"count,omitempty" url:"count,omitempty"` + } + + defaultOptional := "default-optional" + defaults := map[string]interface{}{ + "required": "default-required", + "optional": &defaultOptional, // pointer type + "count": 42, + } + + values, err := QueryValuesWithDefaults(&example{ + Required: "custom-required", // should override default + // Optional is nil, should get default + // Count is 0, should get default + }, defaults) + require.NoError(t, err) + assert.Equal(t, "count=42&optional=default-optional&required=custom-required", values.Encode()) + }) + + t.Run("override non-zero defaults with explicit zero values", func(t *testing.T) { + type example struct { + Name *string `json:"name" url:"name"` + Age *int `json:"age" url:"age"` + Enabled *bool `json:"enabled" url:"enabled"` + } + + defaults := map[string]interface{}{ + "name": "default-name", + "age": 25, + "enabled": true, + } + + // first, test that a properly empty request is overridden: + { + values, err := QueryValuesWithDefaults(&example{}, defaults) + require.NoError(t, err) + assert.Equal(t, "age=25&enabled=true&name=default-name", values.Encode()) + } + + // second, test that a request that contains zeros is not overridden: + var ( + name = "" + age = 0 + enabled = false + ) + values, err := QueryValuesWithDefaults(&example{ + Name: &name, // explicit empty string should override default + Age: &age, // explicit zero should override default + Enabled: &enabled, // explicit false should override default + }, defaults) + require.NoError(t, err) + assert.Equal(t, "age=0&enabled=false&name=", values.Encode()) + }) + + t.Run("nil input returns empty values", func(t *testing.T) { + defaults := map[string]any{ + "name": "default-name", + "age": 25, + } + + // Test with nil + values, err := QueryValuesWithDefaults(nil, defaults) + require.NoError(t, err) + assert.Empty(t, values) + + // Test with nil pointer + type example struct { + Name string `json:"name" url:"name"` + } + var nilPtr *example + values, err = QueryValuesWithDefaults(nilPtr, defaults) + require.NoError(t, err) + assert.Empty(t, values) + }) +} diff --git a/seed/go-sdk/streaming/internal/retrier.go b/seed/go-sdk/streaming/internal/retrier.go new file mode 100644 index 000000000000..4efae1b4c286 --- /dev/null +++ b/seed/go-sdk/streaming/internal/retrier.go @@ -0,0 +1,230 @@ +package internal + +import ( + "crypto/rand" + "math/big" + "net/http" + "strconv" + "time" +) + +const ( + defaultRetryAttempts = 2 + minRetryDelay = 1000 * time.Millisecond + maxRetryDelay = 60000 * time.Millisecond +) + +// RetryOption adapts the behavior the *Retrier. +type RetryOption func(*retryOptions) + +// RetryFunc is a retryable HTTP function call (i.e. *http.Client.Do). +type RetryFunc func(*http.Request) (*http.Response, error) + +// WithMaxAttempts configures the maximum number of attempts +// of the *Retrier. +func WithMaxAttempts(attempts uint) RetryOption { + return func(opts *retryOptions) { + opts.attempts = attempts + } +} + +// Retrier retries failed requests a configurable number of times with an +// exponential back-off between each retry. +type Retrier struct { + attempts uint +} + +// NewRetrier constructs a new *Retrier with the given options, if any. +func NewRetrier(opts ...RetryOption) *Retrier { + options := new(retryOptions) + for _, opt := range opts { + opt(options) + } + attempts := uint(defaultRetryAttempts) + if options.attempts > 0 { + attempts = options.attempts + } + return &Retrier{ + attempts: attempts, + } +} + +// Run issues the request and, upon failure, retries the request if possible. +// +// The request will be retried as long as the request is deemed retryable and the +// number of retry attempts has not grown larger than the configured retry limit. +func (r *Retrier) Run( + fn RetryFunc, + request *http.Request, + errorDecoder ErrorDecoder, + opts ...RetryOption, +) (*http.Response, error) { + options := new(retryOptions) + for _, opt := range opts { + opt(options) + } + maxRetryAttempts := r.attempts + if options.attempts > 0 { + maxRetryAttempts = options.attempts + } + var ( + retryAttempt uint + previousError error + ) + return r.run( + fn, + request, + errorDecoder, + maxRetryAttempts, + retryAttempt, + previousError, + ) +} + +func (r *Retrier) run( + fn RetryFunc, + request *http.Request, + errorDecoder ErrorDecoder, + maxRetryAttempts uint, + retryAttempt uint, + previousError error, +) (*http.Response, error) { + if retryAttempt >= maxRetryAttempts { + return nil, previousError + } + + // If the call has been cancelled, don't issue the request. + if err := request.Context().Err(); err != nil { + return nil, err + } + + response, err := fn(request) + if err != nil { + return nil, err + } + + if r.shouldRetry(response) { + defer response.Body.Close() + + delay, err := r.retryDelay(response, retryAttempt) + if err != nil { + return nil, err + } + + time.Sleep(delay) + + return r.run( + fn, + request, + errorDecoder, + maxRetryAttempts, + retryAttempt+1, + decodeError(response, errorDecoder), + ) + } + + return response, nil +} + +// shouldRetry returns true if the request should be retried based on the given +// response status code. +func (r *Retrier) shouldRetry(response *http.Response) bool { + return response.StatusCode == http.StatusTooManyRequests || + response.StatusCode == http.StatusRequestTimeout || + response.StatusCode >= http.StatusInternalServerError +} + +// retryDelay calculates the delay time based on response headers, +// falling back to exponential backoff if no headers are present. +func (r *Retrier) retryDelay(response *http.Response, retryAttempt uint) (time.Duration, error) { + // Check for Retry-After header first (RFC 7231), applying no jitter + if retryAfter := response.Header.Get("Retry-After"); retryAfter != "" { + // Parse as number of seconds... + if seconds, err := strconv.Atoi(retryAfter); err == nil { + delay := time.Duration(seconds) * time.Second + if delay > 0 { + if delay > maxRetryDelay { + delay = maxRetryDelay + } + return delay, nil + } + } + + // ...or as an HTTP date; both are valid + if retryTime, err := time.Parse(time.RFC1123, retryAfter); err == nil { + delay := time.Until(retryTime) + if delay > 0 { + if delay > maxRetryDelay { + delay = maxRetryDelay + } + return delay, nil + } + } + } + + // Then check for industry-standard X-RateLimit-Reset header, applying positive jitter + if rateLimitReset := response.Header.Get("X-RateLimit-Reset"); rateLimitReset != "" { + if resetTimestamp, err := strconv.ParseInt(rateLimitReset, 10, 64); err == nil { + // Assume Unix timestamp in seconds + resetTime := time.Unix(resetTimestamp, 0) + delay := time.Until(resetTime) + if delay > 0 { + if delay > maxRetryDelay { + delay = maxRetryDelay + } + return r.addPositiveJitter(delay) + } + } + } + + // Fall back to exponential backoff + return r.exponentialBackoff(retryAttempt) +} + +// exponentialBackoff calculates the delay time based on the retry attempt +// and applies symmetric jitter (±10% around the delay). +func (r *Retrier) exponentialBackoff(retryAttempt uint) (time.Duration, error) { + if retryAttempt > 63 { // 2^63+ would overflow uint64 + retryAttempt = 63 + } + + delay := minRetryDelay << retryAttempt + if delay > maxRetryDelay { + delay = maxRetryDelay + } + + return r.addSymmetricJitter(delay) +} + +// addJitterWithRange applies jitter to the given delay. +// minPercent and maxPercent define the jitter range (e.g., 100, 120 for +0% to +20%). +func (r *Retrier) addJitterWithRange(delay time.Duration, minPercent, maxPercent int) (time.Duration, error) { + jitterRange := big.NewInt(int64(delay * time.Duration(maxPercent-minPercent) / 100)) + jitter, err := rand.Int(rand.Reader, jitterRange) + if err != nil { + return 0, err + } + + jitteredDelay := delay + time.Duration(jitter.Int64()) + delay*time.Duration(minPercent-100)/100 + if jitteredDelay < minRetryDelay { + jitteredDelay = minRetryDelay + } + if jitteredDelay > maxRetryDelay { + jitteredDelay = maxRetryDelay + } + return jitteredDelay, nil +} + +// addPositiveJitter applies positive jitter to the given delay (100%-120% range). +func (r *Retrier) addPositiveJitter(delay time.Duration) (time.Duration, error) { + return r.addJitterWithRange(delay, 100, 120) +} + +// addSymmetricJitter applies symmetric jitter to the given delay (90%-110% range). +func (r *Retrier) addSymmetricJitter(delay time.Duration) (time.Duration, error) { + return r.addJitterWithRange(delay, 90, 110) +} + +type retryOptions struct { + attempts uint +} diff --git a/seed/go-sdk/streaming/internal/retrier_test.go b/seed/go-sdk/streaming/internal/retrier_test.go new file mode 100644 index 000000000000..15242fb5062d --- /dev/null +++ b/seed/go-sdk/streaming/internal/retrier_test.go @@ -0,0 +1,300 @@ +package internal + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/fern-api/stream-go/v2/core" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type RetryTestCase struct { + description string + + giveAttempts uint + giveStatusCodes []int + giveResponse *InternalTestResponse + + wantResponse *InternalTestResponse + wantError *core.APIError +} + +func TestRetrier(t *testing.T) { + tests := []*RetryTestCase{ + { + description: "retry request succeeds after multiple failures", + giveAttempts: 3, + giveStatusCodes: []int{ + http.StatusServiceUnavailable, + http.StatusServiceUnavailable, + http.StatusOK, + }, + giveResponse: &InternalTestResponse{ + Id: "1", + }, + wantResponse: &InternalTestResponse{ + Id: "1", + }, + }, + { + description: "retry request fails if MaxAttempts is exceeded", + giveAttempts: 3, + giveStatusCodes: []int{ + http.StatusRequestTimeout, + http.StatusRequestTimeout, + http.StatusRequestTimeout, + http.StatusOK, + }, + wantError: &core.APIError{ + StatusCode: http.StatusRequestTimeout, + }, + }, + { + description: "retry durations increase exponentially and stay within the min and max delay values", + giveAttempts: 4, + giveStatusCodes: []int{ + http.StatusServiceUnavailable, + http.StatusServiceUnavailable, + http.StatusServiceUnavailable, + http.StatusOK, + }, + }, + { + description: "retry does not occur on status code 404", + giveAttempts: 2, + giveStatusCodes: []int{http.StatusNotFound, http.StatusOK}, + wantError: &core.APIError{ + StatusCode: http.StatusNotFound, + }, + }, + { + description: "retries occur on status code 429", + giveAttempts: 2, + giveStatusCodes: []int{http.StatusTooManyRequests, http.StatusOK}, + }, + { + description: "retries occur on status code 408", + giveAttempts: 2, + giveStatusCodes: []int{http.StatusRequestTimeout, http.StatusOK}, + }, + { + description: "retries occur on status code 500", + giveAttempts: 2, + giveStatusCodes: []int{http.StatusInternalServerError, http.StatusOK}, + }, + } + + for _, tc := range tests { + t.Run(tc.description, func(t *testing.T) { + var ( + test = tc + server = newTestRetryServer(t, test) + client = server.Client() + ) + + t.Parallel() + + caller := NewCaller( + &CallerParams{ + Client: client, + }, + ) + + var response *InternalTestResponse + _, err := caller.Call( + context.Background(), + &CallParams{ + URL: server.URL, + Method: http.MethodGet, + Request: &InternalTestRequest{}, + Response: &response, + MaxAttempts: test.giveAttempts, + ResponseIsOptional: true, + }, + ) + + if test.wantError != nil { + require.IsType(t, err, &core.APIError{}) + expectedErrorCode := test.wantError.StatusCode + actualErrorCode := err.(*core.APIError).StatusCode + assert.Equal(t, expectedErrorCode, actualErrorCode) + return + } + + require.NoError(t, err) + assert.Equal(t, test.wantResponse, response) + }) + } +} + +// newTestRetryServer returns a new *httptest.Server configured with the +// given test parameters, suitable for testing retries. +func newTestRetryServer(t *testing.T, tc *RetryTestCase) *httptest.Server { + var index int + timestamps := make([]time.Time, 0, len(tc.giveStatusCodes)) + + return httptest.NewServer( + http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + timestamps = append(timestamps, time.Now()) + if index > 0 && index < len(expectedRetryDurations) { + // Ensure that the duration between retries increases exponentially, + // and that it is within the minimum and maximum retry delay values. + actualDuration := timestamps[index].Sub(timestamps[index-1]) + expectedDurationMin := expectedRetryDurations[index-1] * 50 / 100 + expectedDurationMax := expectedRetryDurations[index-1] * 150 / 100 + assert.True( + t, + actualDuration >= expectedDurationMin && actualDuration <= expectedDurationMax, + "expected duration to be in range [%v, %v], got %v", + expectedDurationMin, + expectedDurationMax, + actualDuration, + ) + assert.LessOrEqual( + t, + actualDuration, + maxRetryDelay, + "expected duration to be less than the maxRetryDelay (%v), got %v", + maxRetryDelay, + actualDuration, + ) + assert.GreaterOrEqual( + t, + actualDuration, + minRetryDelay, + "expected duration to be greater than the minRetryDelay (%v), got %v", + minRetryDelay, + actualDuration, + ) + } + + request := new(InternalTestRequest) + bytes, err := io.ReadAll(r.Body) + require.NoError(t, err) + require.NoError(t, json.Unmarshal(bytes, request)) + require.LessOrEqual(t, index, len(tc.giveStatusCodes)) + + statusCode := tc.giveStatusCodes[index] + + w.WriteHeader(statusCode) + + if tc.giveResponse != nil && statusCode == http.StatusOK { + bytes, err = json.Marshal(tc.giveResponse) + require.NoError(t, err) + _, err = w.Write(bytes) + require.NoError(t, err) + } + + index++ + }, + ), + ) +} + +// expectedRetryDurations holds an array of calculated retry durations, +// where the index of the array should correspond to the retry attempt. +// +// Values are calculated based off of `minRetryDelay * 2^i`. +var expectedRetryDurations = []time.Duration{ + 1000 * time.Millisecond, // 500ms * 2^1 = 1000ms + 2000 * time.Millisecond, // 500ms * 2^2 = 2000ms + 4000 * time.Millisecond, // 500ms * 2^3 = 4000ms + 8000 * time.Millisecond, // 500ms * 2^4 = 8000ms +} + +func TestRetryDelayTiming(t *testing.T) { + tests := []struct { + name string + headerName string + headerValueFunc func() string + expectedMinMs int64 + expectedMaxMs int64 + }{ + { + name: "retry-after with seconds value", + headerName: "retry-after", + headerValueFunc: func() string { + return "1" + }, + expectedMinMs: 500, + expectedMaxMs: 1500, + }, + { + name: "retry-after with HTTP date", + headerName: "retry-after", + headerValueFunc: func() string { + return time.Now().Add(3 * time.Second).Format(time.RFC1123) + }, + expectedMinMs: 1500, + expectedMaxMs: 4500, + }, + { + name: "x-ratelimit-reset with future timestamp", + headerName: "x-ratelimit-reset", + headerValueFunc: func() string { + return fmt.Sprintf("%d", time.Now().Add(3*time.Second).Unix()) + }, + expectedMinMs: 1500, + expectedMaxMs: 4500, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + var timestamps []time.Time + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + timestamps = append(timestamps, time.Now()) + if len(timestamps) == 1 { + // First request - return retryable error with header + w.Header().Set(tt.headerName, tt.headerValueFunc()) + w.WriteHeader(http.StatusTooManyRequests) + } else { + // Second request - return success + w.WriteHeader(http.StatusOK) + response := &InternalTestResponse{Id: "success"} + bytes, _ := json.Marshal(response) + w.Write(bytes) + } + })) + defer server.Close() + + caller := NewCaller(&CallerParams{ + Client: server.Client(), + }) + + var response *InternalTestResponse + _, err := caller.Call( + context.Background(), + &CallParams{ + URL: server.URL, + Method: http.MethodGet, + Request: &InternalTestRequest{}, + Response: &response, + MaxAttempts: 2, + ResponseIsOptional: true, + }, + ) + + require.NoError(t, err) + require.Len(t, timestamps, 2, "Expected exactly 2 requests") + + actualDelayMs := timestamps[1].Sub(timestamps[0]).Milliseconds() + + assert.GreaterOrEqual(t, actualDelayMs, tt.expectedMinMs, + "Actual delay %dms should be >= expected min %dms", actualDelayMs, tt.expectedMinMs) + assert.LessOrEqual(t, actualDelayMs, tt.expectedMaxMs, + "Actual delay %dms should be <= expected max %dms", actualDelayMs, tt.expectedMaxMs) + }) + } +} diff --git a/seed/go-sdk/streaming/internal/streamer.go b/seed/go-sdk/streaming/internal/streamer.go new file mode 100644 index 000000000000..205dff896cab --- /dev/null +++ b/seed/go-sdk/streaming/internal/streamer.go @@ -0,0 +1,118 @@ +package internal + +import ( + "context" + "net/http" + "net/url" + + "github.com/fern-api/stream-go/v2/core" +) + +const ( + // DefaultDataPrefix is the default prefix used for SSE streaming. + DefaultSSEDataPrefix = "data: " + + // DefaultTerminator is the default terminator used for SSE streaming. + DefaultSSETerminator = "[DONE]" +) + +// Streamer calls APIs and streams responses using a *Stream. +type Streamer[T any] struct { + client HTTPClient + retrier *Retrier +} + +// NewStreamer returns a new *Streamer backed by the given caller's HTTP client. +func NewStreamer[T any](caller *Caller) *Streamer[T] { + return &Streamer[T]{ + client: caller.client, + retrier: caller.retrier, + } +} + +// StreamParams represents the parameters used to issue an API streaming call. +type StreamParams struct { + URL string + Method string + Prefix string + Delimiter string + Terminator string + MaxAttempts uint + Headers http.Header + BodyProperties map[string]interface{} + QueryParameters url.Values + Client HTTPClient + Request interface{} + ErrorDecoder ErrorDecoder + Format core.StreamFormat +} + +// Stream issues an API streaming call according to the given stream parameters. +func (s *Streamer[T]) Stream(ctx context.Context, params *StreamParams) (*core.Stream[T], error) { + url := buildURL(params.URL, params.QueryParameters) + req, err := newRequest( + ctx, + url, + params.Method, + params.Headers, + params.Request, + params.BodyProperties, + ) + if err != nil { + return nil, err + } + + // If the call has been cancelled, don't issue the request. + if err := ctx.Err(); err != nil { + return nil, err + } + + client := s.client + if params.Client != nil { + // Use the HTTP client scoped to the request. + client = params.Client + } + + var retryOptions []RetryOption + if params.MaxAttempts > 0 { + retryOptions = append(retryOptions, WithMaxAttempts(params.MaxAttempts)) + } + + resp, err := s.retrier.Run( + client.Do, + req, + params.ErrorDecoder, + retryOptions..., + ) + if err != nil { + return nil, err + } + + // Check if the call was cancelled before we return the error + // associated with the call and/or unmarshal the response data. + if err := ctx.Err(); err != nil { + defer resp.Body.Close() + return nil, err + } + + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + defer resp.Body.Close() + return nil, decodeError(resp, params.ErrorDecoder) + } + + var opts []core.StreamOption + if params.Delimiter != "" { + opts = append(opts, core.WithDelimiter(params.Delimiter)) + } + if params.Prefix != "" { + opts = append(opts, core.WithPrefix(params.Prefix)) + } + if params.Terminator != "" { + opts = append(opts, core.WithTerminator(params.Terminator)) + } + if params.Format != core.StreamFormatEmpty { + opts = append(opts, core.WithFormat(params.Format)) + } + + return core.NewStream[T](resp, opts...), nil +} diff --git a/seed/go-sdk/streaming/internal/stringer.go b/seed/go-sdk/streaming/internal/stringer.go new file mode 100644 index 000000000000..312801851e0e --- /dev/null +++ b/seed/go-sdk/streaming/internal/stringer.go @@ -0,0 +1,13 @@ +package internal + +import "encoding/json" + +// StringifyJSON returns a pretty JSON string representation of +// the given value. +func StringifyJSON(value interface{}) (string, error) { + bytes, err := json.MarshalIndent(value, "", " ") + if err != nil { + return "", err + } + return string(bytes), nil +} diff --git a/seed/go-sdk/streaming/internal/time.go b/seed/go-sdk/streaming/internal/time.go new file mode 100644 index 000000000000..ab0e269fade3 --- /dev/null +++ b/seed/go-sdk/streaming/internal/time.go @@ -0,0 +1,137 @@ +package internal + +import ( + "encoding/json" + "time" +) + +const dateFormat = "2006-01-02" + +// DateTime wraps time.Time and adapts its JSON representation +// to conform to a RFC3339 date (e.g. 2006-01-02). +// +// Ref: https://ijmacd.github.io/rfc3339-iso8601 +type Date struct { + t *time.Time +} + +// NewDate returns a new *Date. If the given time.Time +// is nil, nil will be returned. +func NewDate(t time.Time) *Date { + return &Date{t: &t} +} + +// NewOptionalDate returns a new *Date. If the given time.Time +// is nil, nil will be returned. +func NewOptionalDate(t *time.Time) *Date { + if t == nil { + return nil + } + return &Date{t: t} +} + +// Time returns the Date's underlying time, if any. If the +// date is nil, the zero value is returned. +func (d *Date) Time() time.Time { + if d == nil || d.t == nil { + return time.Time{} + } + return *d.t +} + +// TimePtr returns a pointer to the Date's underlying time.Time, if any. +func (d *Date) TimePtr() *time.Time { + if d == nil || d.t == nil { + return nil + } + if d.t.IsZero() { + return nil + } + return d.t +} + +func (d *Date) MarshalJSON() ([]byte, error) { + if d == nil || d.t == nil { + return nil, nil + } + return json.Marshal(d.t.Format(dateFormat)) +} + +func (d *Date) UnmarshalJSON(data []byte) error { + var raw string + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + + parsedTime, err := time.Parse(dateFormat, raw) + if err != nil { + return err + } + + *d = Date{t: &parsedTime} + return nil +} + +// DateTime wraps time.Time and adapts its JSON representation +// to conform to a RFC3339 date-time (e.g. 2017-07-21T17:32:28Z). +// +// Ref: https://ijmacd.github.io/rfc3339-iso8601 +type DateTime struct { + t *time.Time +} + +// NewDateTime returns a new *DateTime. +func NewDateTime(t time.Time) *DateTime { + return &DateTime{t: &t} +} + +// NewOptionalDateTime returns a new *DateTime. If the given time.Time +// is nil, nil will be returned. +func NewOptionalDateTime(t *time.Time) *DateTime { + if t == nil { + return nil + } + return &DateTime{t: t} +} + +// Time returns the DateTime's underlying time, if any. If the +// date-time is nil, the zero value is returned. +func (d *DateTime) Time() time.Time { + if d == nil || d.t == nil { + return time.Time{} + } + return *d.t +} + +// TimePtr returns a pointer to the DateTime's underlying time.Time, if any. +func (d *DateTime) TimePtr() *time.Time { + if d == nil || d.t == nil { + return nil + } + if d.t.IsZero() { + return nil + } + return d.t +} + +func (d *DateTime) MarshalJSON() ([]byte, error) { + if d == nil || d.t == nil { + return nil, nil + } + return json.Marshal(d.t.Format(time.RFC3339)) +} + +func (d *DateTime) UnmarshalJSON(data []byte) error { + var raw string + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + + parsedTime, err := time.Parse(time.RFC3339, raw) + if err != nil { + return err + } + + *d = DateTime{t: &parsedTime} + return nil +} diff --git a/seed/go-sdk/streaming/option/request_option.go b/seed/go-sdk/streaming/option/request_option.go new file mode 100644 index 000000000000..52c6b5d8bdf9 --- /dev/null +++ b/seed/go-sdk/streaming/option/request_option.go @@ -0,0 +1,64 @@ +// Code generated by Fern. DO NOT EDIT. + +package option + +import ( + core "github.com/fern-api/stream-go/v2/core" + http "net/http" + url "net/url" +) + +// RequestOption adapts the behavior of an individual request. +type RequestOption = core.RequestOption + +// WithBaseURL sets the base URL, overriding the default +// environment, if any. +func WithBaseURL(baseURL string) *core.BaseURLOption { + return &core.BaseURLOption{ + BaseURL: baseURL, + } +} + +// WithHTTPClient uses the given HTTPClient to issue the request. +func WithHTTPClient(httpClient core.HTTPClient) *core.HTTPClientOption { + return &core.HTTPClientOption{ + HTTPClient: httpClient, + } +} + +// WithHTTPHeader adds the given http.Header to the request. +func WithHTTPHeader(httpHeader http.Header) *core.HTTPHeaderOption { + return &core.HTTPHeaderOption{ + // Clone the headers so they can't be modified after the option call. + HTTPHeader: httpHeader.Clone(), + } +} + +// WithBodyProperties adds the given body properties to the request. +func WithBodyProperties(bodyProperties map[string]interface{}) *core.BodyPropertiesOption { + copiedBodyProperties := make(map[string]interface{}, len(bodyProperties)) + for key, value := range bodyProperties { + copiedBodyProperties[key] = value + } + return &core.BodyPropertiesOption{ + BodyProperties: copiedBodyProperties, + } +} + +// WithQueryParameters adds the given query parameters to the request. +func WithQueryParameters(queryParameters url.Values) *core.QueryParametersOption { + copiedQueryParameters := make(url.Values, len(queryParameters)) + for key, values := range queryParameters { + copiedQueryParameters[key] = values + } + return &core.QueryParametersOption{ + QueryParameters: copiedQueryParameters, + } +} + +// WithMaxAttempts configures the maximum number of retry attempts. +func WithMaxAttempts(attempts uint) *core.MaxAttemptsOption { + return &core.MaxAttemptsOption{ + MaxAttempts: attempts, + } +} diff --git a/seed/go-sdk/websocket-inferred-auth/dynamic-snippets/example0/snippet.go b/seed/go-sdk/websocket-inferred-auth/dynamic-snippets/example0/snippet.go index 4a4d0eb17841..3d354ff4f38d 100644 --- a/seed/go-sdk/websocket-inferred-auth/dynamic-snippets/example0/snippet.go +++ b/seed/go-sdk/websocket-inferred-auth/dynamic-snippets/example0/snippet.go @@ -12,6 +12,7 @@ func do() { option.WithBaseURL( "https://api.fern.com", ), + nil, ) request := &fern.GetTokenRequest{ XApiKey: "X-Api-Key", diff --git a/seed/go-sdk/websocket-inferred-auth/dynamic-snippets/example1/snippet.go b/seed/go-sdk/websocket-inferred-auth/dynamic-snippets/example1/snippet.go index adcb1d338e8d..7f71ffdb1a91 100644 --- a/seed/go-sdk/websocket-inferred-auth/dynamic-snippets/example1/snippet.go +++ b/seed/go-sdk/websocket-inferred-auth/dynamic-snippets/example1/snippet.go @@ -12,6 +12,7 @@ func do() { option.WithBaseURL( "https://api.fern.com", ), + nil, ) request := &fern.RefreshTokenRequest{ XApiKey: "X-Api-Key", diff --git a/seed/java-model/inferred-auth-implicit-api-key/.github/workflows/ci.yml b/seed/java-model/inferred-auth-implicit-api-key/.github/workflows/ci.yml new file mode 100644 index 000000000000..7bffd9de06d8 --- /dev/null +++ b/seed/java-model/inferred-auth-implicit-api-key/.github/workflows/ci.yml @@ -0,0 +1,61 @@ +name: ci + +on: [push] + +jobs: + compile: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Java + id: setup-jre + uses: actions/setup-java@v1 + with: + java-version: "11" + architecture: x64 + + - name: Compile + run: ./gradlew compileJava + + test: + needs: [ compile ] + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Java + id: setup-jre + uses: actions/setup-java@v1 + with: + java-version: "11" + architecture: x64 + + - name: Test + run: ./gradlew test + publish: + needs: [ compile, test ] + if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Java + id: setup-jre + uses: actions/setup-java@v1 + with: + java-version: "11" + architecture: x64 + + - name: Publish to maven + run: | + ./gradlew publish + env: + MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} + MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} + MAVEN_PUBLISH_REGISTRY_URL: "" diff --git a/seed/java-model/inferred-auth-implicit-api-key/.gitignore b/seed/java-model/inferred-auth-implicit-api-key/.gitignore new file mode 100644 index 000000000000..d4199abc2cd4 --- /dev/null +++ b/seed/java-model/inferred-auth-implicit-api-key/.gitignore @@ -0,0 +1,24 @@ +*.class +.project +.gradle +? +.classpath +.checkstyle +.settings +.node +build + +# IntelliJ +*.iml +*.ipr +*.iws +.idea/ +out/ + +# Eclipse/IntelliJ APT +generated_src/ +generated_testSrc/ +generated/ + +bin +build \ No newline at end of file diff --git a/seed/java-model/inferred-auth-implicit-api-key/build.gradle b/seed/java-model/inferred-auth-implicit-api-key/build.gradle new file mode 100644 index 000000000000..ccda327a9b5f --- /dev/null +++ b/seed/java-model/inferred-auth-implicit-api-key/build.gradle @@ -0,0 +1,98 @@ +plugins { + id 'java-library' + id 'maven-publish' + id 'com.diffplug.spotless' version '6.11.0' +} + +repositories { + mavenCentral() + maven { + url 'https://s01.oss.sonatype.org/content/repositories/releases/' + } +} + +dependencies { + api 'com.fasterxml.jackson.core:jackson-databind:2.18.2' + api 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.18.2' + api 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.2' +} + + +sourceCompatibility = 1.8 +targetCompatibility = 1.8 + +tasks.withType(Javadoc) { + failOnError false + options.addStringOption('Xdoclint:none', '-quiet') +} + +spotless { + java { + palantirJavaFormat() + } +} + + +java { + withSourcesJar() + withJavadocJar() +} + + +group = 'com.fern' + +version = '0.0.1' + +jar { + dependsOn(":generatePomFileForMavenPublication") + archiveBaseName = "inferred-auth-implicit-api-key" +} + +sourcesJar { + archiveBaseName = "inferred-auth-implicit-api-key" +} + +javadocJar { + archiveBaseName = "inferred-auth-implicit-api-key" +} + +test { + useJUnitPlatform() + testLogging { + showStandardStreams = true + } +} + +publishing { + publications { + maven(MavenPublication) { + groupId = 'com.fern' + artifactId = 'inferred-auth-implicit-api-key' + version = '0.0.1' + from components.java + pom { + licenses { + license { + name = 'The MIT License (MIT)' + url = 'https://mit-license.org/' + } + } + scm { + connection = 'scm:git:git://github.com/inferred-auth-implicit-api-key/fern.git' + developerConnection = 'scm:git:git://github.com/inferred-auth-implicit-api-key/fern.git' + url = 'https://github.com/inferred-auth-implicit-api-key/fern' + } + } + } + } + repositories { + maven { + url "$System.env.MAVEN_PUBLISH_REGISTRY_URL" + credentials { + username "$System.env.MAVEN_USERNAME" + password "$System.env.MAVEN_PASSWORD" + } + } + } +} + diff --git a/seed/java-model/inferred-auth-implicit-api-key/settings.gradle b/seed/java-model/inferred-auth-implicit-api-key/settings.gradle new file mode 100644 index 000000000000..4920a50bfe67 --- /dev/null +++ b/seed/java-model/inferred-auth-implicit-api-key/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = 'inferred-auth-implicit-api-key' + diff --git a/seed/java-model/inferred-auth-implicit-api-key/snippet.json b/seed/java-model/inferred-auth-implicit-api-key/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/java-model/inferred-auth-implicit-api-key/src/main/java/com/seed/inferredAuthImplicitApiKey/core/DateTimeDeserializer.java b/seed/java-model/inferred-auth-implicit-api-key/src/main/java/com/seed/inferredAuthImplicitApiKey/core/DateTimeDeserializer.java new file mode 100644 index 000000000000..75e3a4cb0fa5 --- /dev/null +++ b/seed/java-model/inferred-auth-implicit-api-key/src/main/java/com/seed/inferredAuthImplicitApiKey/core/DateTimeDeserializer.java @@ -0,0 +1,56 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package com.seed.inferredAuthImplicitApiKey.core; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.module.SimpleModule; +import java.io.IOException; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalQueries; + +/** + * Custom deserializer that handles converting ISO8601 dates into {@link OffsetDateTime} objects. + */ +class DateTimeDeserializer extends JsonDeserializer { + private static final SimpleModule MODULE; + + static { + MODULE = new SimpleModule().addDeserializer(OffsetDateTime.class, new DateTimeDeserializer()); + } + + /** + * Gets a module wrapping this deserializer as an adapter for the Jackson ObjectMapper. + * + * @return A {@link SimpleModule} to be plugged onto Jackson ObjectMapper. + */ + public static SimpleModule getModule() { + return MODULE; + } + + @Override + public OffsetDateTime deserialize(JsonParser parser, DeserializationContext context) throws IOException { + JsonToken token = parser.currentToken(); + if (token == JsonToken.VALUE_NUMBER_INT) { + return OffsetDateTime.ofInstant(Instant.ofEpochSecond(parser.getValueAsLong()), ZoneOffset.UTC); + } else { + TemporalAccessor temporal = DateTimeFormatter.ISO_DATE_TIME.parseBest( + parser.getValueAsString(), OffsetDateTime::from, LocalDateTime::from); + + if (temporal.query(TemporalQueries.offset()) == null) { + return LocalDateTime.from(temporal).atOffset(ZoneOffset.UTC); + } else { + return OffsetDateTime.from(temporal); + } + } + } +} \ No newline at end of file diff --git a/seed/java-model/inferred-auth-implicit-api-key/src/main/java/com/seed/inferredAuthImplicitApiKey/core/ObjectMappers.java b/seed/java-model/inferred-auth-implicit-api-key/src/main/java/com/seed/inferredAuthImplicitApiKey/core/ObjectMappers.java new file mode 100644 index 000000000000..0536e8628d9c --- /dev/null +++ b/seed/java-model/inferred-auth-implicit-api-key/src/main/java/com/seed/inferredAuthImplicitApiKey/core/ObjectMappers.java @@ -0,0 +1,51 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package com.seed.inferredAuthImplicitApiKey.core; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import java.io.IOException; +import java.lang.Integer; +import java.lang.Object; +import java.lang.String; + +public final class ObjectMappers { + public static final ObjectMapper JSON_MAPPER = JsonMapper.builder() + .addModule(new Jdk8Module()) + .addModule(new JavaTimeModule()) + .addModule(DateTimeDeserializer.getModule()) + .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .build(); + + private ObjectMappers() { + } + + public static String stringify(Object o) { + try { + return JSON_MAPPER.setSerializationInclusion(JsonInclude.Include.ALWAYS) + .writerWithDefaultPrettyPrinter() + .writeValueAsString(o); + } + catch (IOException e) { + return o.getClass().getName() + "@" + Integer.toHexString(o.hashCode()); + } + } + + public static Object parseErrorBody(String responseBodyString) { + try { + return JSON_MAPPER.readValue(responseBodyString, Object.class); + } + catch (JsonProcessingException ignored) { + return responseBodyString; + } + } + } diff --git a/seed/java-model/inferred-auth-implicit-api-key/src/main/java/com/seed/inferredAuthImplicitApiKey/model/auth/TokenResponse.java b/seed/java-model/inferred-auth-implicit-api-key/src/main/java/com/seed/inferredAuthImplicitApiKey/model/auth/TokenResponse.java new file mode 100644 index 000000000000..c86d20404dce --- /dev/null +++ b/seed/java-model/inferred-auth-implicit-api-key/src/main/java/com/seed/inferredAuthImplicitApiKey/model/auth/TokenResponse.java @@ -0,0 +1,172 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package com.seed.inferredAuthImplicitApiKey.model.auth; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.seed.inferredAuthImplicitApiKey.core.ObjectMappers; +import java.lang.Object; +import java.lang.String; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize( + builder = TokenResponse.Builder.class +) +public final class TokenResponse { + private final String accessToken; + + private final String tokenType; + + private final int expiresIn; + + private final Optional scope; + + private TokenResponse(String accessToken, String tokenType, int expiresIn, + Optional scope) { + this.accessToken = accessToken; + this.tokenType = tokenType; + this.expiresIn = expiresIn; + this.scope = scope; + } + + @JsonProperty("access_token") + public String getAccessToken() { + return accessToken; + } + + @JsonProperty("token_type") + public String getTokenType() { + return tokenType; + } + + @JsonProperty("expires_in") + public int getExpiresIn() { + return expiresIn; + } + + @JsonProperty("scope") + public Optional getScope() { + return scope; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof TokenResponse && equalTo((TokenResponse) other); + } + + private boolean equalTo(TokenResponse other) { + return accessToken.equals(other.accessToken) && tokenType.equals(other.tokenType) && expiresIn == other.expiresIn && scope.equals(other.scope); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.accessToken, this.tokenType, this.expiresIn, this.scope); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static AccessTokenStage builder() { + return new Builder(); + } + + public interface AccessTokenStage { + TokenTypeStage accessToken(String accessToken); + + Builder from(TokenResponse other); + } + + public interface TokenTypeStage { + ExpiresInStage tokenType(String tokenType); + } + + public interface ExpiresInStage { + _FinalStage expiresIn(int expiresIn); + } + + public interface _FinalStage { + TokenResponse build(); + + _FinalStage scope(Optional scope); + + _FinalStage scope(String scope); + } + + @JsonIgnoreProperties( + ignoreUnknown = true + ) + public static final class Builder implements AccessTokenStage, TokenTypeStage, ExpiresInStage, _FinalStage { + private String accessToken; + + private String tokenType; + + private int expiresIn; + + private Optional scope = Optional.empty(); + + private Builder() { + } + + @java.lang.Override + public Builder from(TokenResponse other) { + accessToken(other.getAccessToken()); + tokenType(other.getTokenType()); + expiresIn(other.getExpiresIn()); + scope(other.getScope()); + return this; + } + + @java.lang.Override + @JsonSetter("access_token") + public TokenTypeStage accessToken(String accessToken) { + this.accessToken = Objects.requireNonNull(accessToken, "accessToken must not be null"); + return this; + } + + @java.lang.Override + @JsonSetter("token_type") + public ExpiresInStage tokenType(String tokenType) { + this.tokenType = Objects.requireNonNull(tokenType, "tokenType must not be null"); + return this; + } + + @java.lang.Override + @JsonSetter("expires_in") + public _FinalStage expiresIn(int expiresIn) { + this.expiresIn = expiresIn; + return this; + } + + @java.lang.Override + public _FinalStage scope(String scope) { + this.scope = Optional.ofNullable(scope); + return this; + } + + @java.lang.Override + @JsonSetter( + value = "scope", + nulls = Nulls.SKIP + ) + public _FinalStage scope(Optional scope) { + this.scope = scope; + return this; + } + + @java.lang.Override + public TokenResponse build() { + return new TokenResponse(accessToken, tokenType, expiresIn, scope); + } + } +} diff --git a/seed/java-model/inferred-auth-implicit-reference/.github/workflows/ci.yml b/seed/java-model/inferred-auth-implicit-reference/.github/workflows/ci.yml new file mode 100644 index 000000000000..7bffd9de06d8 --- /dev/null +++ b/seed/java-model/inferred-auth-implicit-reference/.github/workflows/ci.yml @@ -0,0 +1,61 @@ +name: ci + +on: [push] + +jobs: + compile: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Java + id: setup-jre + uses: actions/setup-java@v1 + with: + java-version: "11" + architecture: x64 + + - name: Compile + run: ./gradlew compileJava + + test: + needs: [ compile ] + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Java + id: setup-jre + uses: actions/setup-java@v1 + with: + java-version: "11" + architecture: x64 + + - name: Test + run: ./gradlew test + publish: + needs: [ compile, test ] + if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Java + id: setup-jre + uses: actions/setup-java@v1 + with: + java-version: "11" + architecture: x64 + + - name: Publish to maven + run: | + ./gradlew publish + env: + MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} + MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} + MAVEN_PUBLISH_REGISTRY_URL: "" diff --git a/seed/java-model/inferred-auth-implicit-reference/.gitignore b/seed/java-model/inferred-auth-implicit-reference/.gitignore new file mode 100644 index 000000000000..d4199abc2cd4 --- /dev/null +++ b/seed/java-model/inferred-auth-implicit-reference/.gitignore @@ -0,0 +1,24 @@ +*.class +.project +.gradle +? +.classpath +.checkstyle +.settings +.node +build + +# IntelliJ +*.iml +*.ipr +*.iws +.idea/ +out/ + +# Eclipse/IntelliJ APT +generated_src/ +generated_testSrc/ +generated/ + +bin +build \ No newline at end of file diff --git a/seed/java-model/inferred-auth-implicit-reference/build.gradle b/seed/java-model/inferred-auth-implicit-reference/build.gradle new file mode 100644 index 000000000000..59a3f12f7293 --- /dev/null +++ b/seed/java-model/inferred-auth-implicit-reference/build.gradle @@ -0,0 +1,98 @@ +plugins { + id 'java-library' + id 'maven-publish' + id 'com.diffplug.spotless' version '6.11.0' +} + +repositories { + mavenCentral() + maven { + url 'https://s01.oss.sonatype.org/content/repositories/releases/' + } +} + +dependencies { + api 'com.fasterxml.jackson.core:jackson-databind:2.18.2' + api 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.18.2' + api 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.2' +} + + +sourceCompatibility = 1.8 +targetCompatibility = 1.8 + +tasks.withType(Javadoc) { + failOnError false + options.addStringOption('Xdoclint:none', '-quiet') +} + +spotless { + java { + palantirJavaFormat() + } +} + + +java { + withSourcesJar() + withJavadocJar() +} + + +group = 'com.fern' + +version = '0.0.1' + +jar { + dependsOn(":generatePomFileForMavenPublication") + archiveBaseName = "inferred-auth-implicit-reference" +} + +sourcesJar { + archiveBaseName = "inferred-auth-implicit-reference" +} + +javadocJar { + archiveBaseName = "inferred-auth-implicit-reference" +} + +test { + useJUnitPlatform() + testLogging { + showStandardStreams = true + } +} + +publishing { + publications { + maven(MavenPublication) { + groupId = 'com.fern' + artifactId = 'inferred-auth-implicit-reference' + version = '0.0.1' + from components.java + pom { + licenses { + license { + name = 'The MIT License (MIT)' + url = 'https://mit-license.org/' + } + } + scm { + connection = 'scm:git:git://github.com/inferred-auth-implicit-reference/fern.git' + developerConnection = 'scm:git:git://github.com/inferred-auth-implicit-reference/fern.git' + url = 'https://github.com/inferred-auth-implicit-reference/fern' + } + } + } + } + repositories { + maven { + url "$System.env.MAVEN_PUBLISH_REGISTRY_URL" + credentials { + username "$System.env.MAVEN_USERNAME" + password "$System.env.MAVEN_PASSWORD" + } + } + } +} + diff --git a/seed/java-model/inferred-auth-implicit-reference/settings.gradle b/seed/java-model/inferred-auth-implicit-reference/settings.gradle new file mode 100644 index 000000000000..65923da904c7 --- /dev/null +++ b/seed/java-model/inferred-auth-implicit-reference/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = 'inferred-auth-implicit-reference' + diff --git a/seed/java-model/inferred-auth-implicit-reference/snippet.json b/seed/java-model/inferred-auth-implicit-reference/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/java-model/inferred-auth-implicit-reference/src/main/java/com/seed/inferredAuthImplicit/core/DateTimeDeserializer.java b/seed/java-model/inferred-auth-implicit-reference/src/main/java/com/seed/inferredAuthImplicit/core/DateTimeDeserializer.java new file mode 100644 index 000000000000..e111ddc29908 --- /dev/null +++ b/seed/java-model/inferred-auth-implicit-reference/src/main/java/com/seed/inferredAuthImplicit/core/DateTimeDeserializer.java @@ -0,0 +1,56 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package com.seed.inferredAuthImplicit.core; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.module.SimpleModule; +import java.io.IOException; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalQueries; + +/** + * Custom deserializer that handles converting ISO8601 dates into {@link OffsetDateTime} objects. + */ +class DateTimeDeserializer extends JsonDeserializer { + private static final SimpleModule MODULE; + + static { + MODULE = new SimpleModule().addDeserializer(OffsetDateTime.class, new DateTimeDeserializer()); + } + + /** + * Gets a module wrapping this deserializer as an adapter for the Jackson ObjectMapper. + * + * @return A {@link SimpleModule} to be plugged onto Jackson ObjectMapper. + */ + public static SimpleModule getModule() { + return MODULE; + } + + @Override + public OffsetDateTime deserialize(JsonParser parser, DeserializationContext context) throws IOException { + JsonToken token = parser.currentToken(); + if (token == JsonToken.VALUE_NUMBER_INT) { + return OffsetDateTime.ofInstant(Instant.ofEpochSecond(parser.getValueAsLong()), ZoneOffset.UTC); + } else { + TemporalAccessor temporal = DateTimeFormatter.ISO_DATE_TIME.parseBest( + parser.getValueAsString(), OffsetDateTime::from, LocalDateTime::from); + + if (temporal.query(TemporalQueries.offset()) == null) { + return LocalDateTime.from(temporal).atOffset(ZoneOffset.UTC); + } else { + return OffsetDateTime.from(temporal); + } + } + } +} \ No newline at end of file diff --git a/seed/java-model/inferred-auth-implicit-reference/src/main/java/com/seed/inferredAuthImplicit/core/ObjectMappers.java b/seed/java-model/inferred-auth-implicit-reference/src/main/java/com/seed/inferredAuthImplicit/core/ObjectMappers.java new file mode 100644 index 000000000000..d10338a9896b --- /dev/null +++ b/seed/java-model/inferred-auth-implicit-reference/src/main/java/com/seed/inferredAuthImplicit/core/ObjectMappers.java @@ -0,0 +1,51 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package com.seed.inferredAuthImplicit.core; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import java.io.IOException; +import java.lang.Integer; +import java.lang.Object; +import java.lang.String; + +public final class ObjectMappers { + public static final ObjectMapper JSON_MAPPER = JsonMapper.builder() + .addModule(new Jdk8Module()) + .addModule(new JavaTimeModule()) + .addModule(DateTimeDeserializer.getModule()) + .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .build(); + + private ObjectMappers() { + } + + public static String stringify(Object o) { + try { + return JSON_MAPPER.setSerializationInclusion(JsonInclude.Include.ALWAYS) + .writerWithDefaultPrettyPrinter() + .writeValueAsString(o); + } + catch (IOException e) { + return o.getClass().getName() + "@" + Integer.toHexString(o.hashCode()); + } + } + + public static Object parseErrorBody(String responseBodyString) { + try { + return JSON_MAPPER.readValue(responseBodyString, Object.class); + } + catch (JsonProcessingException ignored) { + return responseBodyString; + } + } + } diff --git a/seed/java-model/inferred-auth-implicit-reference/src/main/java/com/seed/inferredAuthImplicit/model/auth/GetTokenRequest.java b/seed/java-model/inferred-auth-implicit-reference/src/main/java/com/seed/inferredAuthImplicit/model/auth/GetTokenRequest.java new file mode 100644 index 000000000000..03d4f14d5a66 --- /dev/null +++ b/seed/java-model/inferred-auth-implicit-reference/src/main/java/com/seed/inferredAuthImplicit/model/auth/GetTokenRequest.java @@ -0,0 +1,159 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package com.seed.inferredAuthImplicit.model.auth; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.seed.inferredAuthImplicit.core.ObjectMappers; +import java.lang.Object; +import java.lang.String; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize( + builder = GetTokenRequest.Builder.class +) +public final class GetTokenRequest { + private final String clientId; + + private final String clientSecret; + + private final Optional scope; + + private GetTokenRequest(String clientId, String clientSecret, Optional scope) { + this.clientId = clientId; + this.clientSecret = clientSecret; + this.scope = scope; + } + + @JsonProperty("client_id") + public String getClientId() { + return clientId; + } + + @JsonProperty("client_secret") + public String getClientSecret() { + return clientSecret; + } + + @JsonProperty("audience") + public String getAudience() { + return "https://api.example.com"; + } + + @JsonProperty("grant_type") + public String getGrantType() { + return "client_credentials"; + } + + @JsonProperty("scope") + public Optional getScope() { + return scope; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof GetTokenRequest && equalTo((GetTokenRequest) other); + } + + private boolean equalTo(GetTokenRequest other) { + return clientId.equals(other.clientId) && clientSecret.equals(other.clientSecret) && scope.equals(other.scope); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.clientId, this.clientSecret, this.scope); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static ClientIdStage builder() { + return new Builder(); + } + + public interface ClientIdStage { + ClientSecretStage clientId(String clientId); + + Builder from(GetTokenRequest other); + } + + public interface ClientSecretStage { + _FinalStage clientSecret(String clientSecret); + } + + public interface _FinalStage { + GetTokenRequest build(); + + _FinalStage scope(Optional scope); + + _FinalStage scope(String scope); + } + + @JsonIgnoreProperties( + ignoreUnknown = true + ) + public static final class Builder implements ClientIdStage, ClientSecretStage, _FinalStage { + private String clientId; + + private String clientSecret; + + private Optional scope = Optional.empty(); + + private Builder() { + } + + @java.lang.Override + public Builder from(GetTokenRequest other) { + clientId(other.getClientId()); + clientSecret(other.getClientSecret()); + scope(other.getScope()); + return this; + } + + @java.lang.Override + @JsonSetter("client_id") + public ClientSecretStage clientId(String clientId) { + this.clientId = Objects.requireNonNull(clientId, "clientId must not be null"); + return this; + } + + @java.lang.Override + @JsonSetter("client_secret") + public _FinalStage clientSecret(String clientSecret) { + this.clientSecret = Objects.requireNonNull(clientSecret, "clientSecret must not be null"); + return this; + } + + @java.lang.Override + public _FinalStage scope(String scope) { + this.scope = Optional.ofNullable(scope); + return this; + } + + @java.lang.Override + @JsonSetter( + value = "scope", + nulls = Nulls.SKIP + ) + public _FinalStage scope(Optional scope) { + this.scope = scope; + return this; + } + + @java.lang.Override + public GetTokenRequest build() { + return new GetTokenRequest(clientId, clientSecret, scope); + } + } +} diff --git a/seed/java-model/inferred-auth-implicit-reference/src/main/java/com/seed/inferredAuthImplicit/model/auth/RefreshTokenRequest.java b/seed/java-model/inferred-auth-implicit-reference/src/main/java/com/seed/inferredAuthImplicit/model/auth/RefreshTokenRequest.java new file mode 100644 index 000000000000..f413992f08c6 --- /dev/null +++ b/seed/java-model/inferred-auth-implicit-reference/src/main/java/com/seed/inferredAuthImplicit/model/auth/RefreshTokenRequest.java @@ -0,0 +1,182 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package com.seed.inferredAuthImplicit.model.auth; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.seed.inferredAuthImplicit.core.ObjectMappers; +import java.lang.Object; +import java.lang.String; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize( + builder = RefreshTokenRequest.Builder.class +) +public final class RefreshTokenRequest { + private final String clientId; + + private final String clientSecret; + + private final String refreshToken; + + private final Optional scope; + + private RefreshTokenRequest(String clientId, String clientSecret, String refreshToken, + Optional scope) { + this.clientId = clientId; + this.clientSecret = clientSecret; + this.refreshToken = refreshToken; + this.scope = scope; + } + + @JsonProperty("client_id") + public String getClientId() { + return clientId; + } + + @JsonProperty("client_secret") + public String getClientSecret() { + return clientSecret; + } + + @JsonProperty("refresh_token") + public String getRefreshToken() { + return refreshToken; + } + + @JsonProperty("audience") + public String getAudience() { + return "https://api.example.com"; + } + + @JsonProperty("grant_type") + public String getGrantType() { + return "refresh_token"; + } + + @JsonProperty("scope") + public Optional getScope() { + return scope; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof RefreshTokenRequest && equalTo((RefreshTokenRequest) other); + } + + private boolean equalTo(RefreshTokenRequest other) { + return clientId.equals(other.clientId) && clientSecret.equals(other.clientSecret) && refreshToken.equals(other.refreshToken) && scope.equals(other.scope); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.clientId, this.clientSecret, this.refreshToken, this.scope); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static ClientIdStage builder() { + return new Builder(); + } + + public interface ClientIdStage { + ClientSecretStage clientId(String clientId); + + Builder from(RefreshTokenRequest other); + } + + public interface ClientSecretStage { + RefreshTokenStage clientSecret(String clientSecret); + } + + public interface RefreshTokenStage { + _FinalStage refreshToken(String refreshToken); + } + + public interface _FinalStage { + RefreshTokenRequest build(); + + _FinalStage scope(Optional scope); + + _FinalStage scope(String scope); + } + + @JsonIgnoreProperties( + ignoreUnknown = true + ) + public static final class Builder implements ClientIdStage, ClientSecretStage, RefreshTokenStage, _FinalStage { + private String clientId; + + private String clientSecret; + + private String refreshToken; + + private Optional scope = Optional.empty(); + + private Builder() { + } + + @java.lang.Override + public Builder from(RefreshTokenRequest other) { + clientId(other.getClientId()); + clientSecret(other.getClientSecret()); + refreshToken(other.getRefreshToken()); + scope(other.getScope()); + return this; + } + + @java.lang.Override + @JsonSetter("client_id") + public ClientSecretStage clientId(String clientId) { + this.clientId = Objects.requireNonNull(clientId, "clientId must not be null"); + return this; + } + + @java.lang.Override + @JsonSetter("client_secret") + public RefreshTokenStage clientSecret(String clientSecret) { + this.clientSecret = Objects.requireNonNull(clientSecret, "clientSecret must not be null"); + return this; + } + + @java.lang.Override + @JsonSetter("refresh_token") + public _FinalStage refreshToken(String refreshToken) { + this.refreshToken = Objects.requireNonNull(refreshToken, "refreshToken must not be null"); + return this; + } + + @java.lang.Override + public _FinalStage scope(String scope) { + this.scope = Optional.ofNullable(scope); + return this; + } + + @java.lang.Override + @JsonSetter( + value = "scope", + nulls = Nulls.SKIP + ) + public _FinalStage scope(Optional scope) { + this.scope = scope; + return this; + } + + @java.lang.Override + public RefreshTokenRequest build() { + return new RefreshTokenRequest(clientId, clientSecret, refreshToken, scope); + } + } +} diff --git a/seed/java-model/inferred-auth-implicit-reference/src/main/java/com/seed/inferredAuthImplicit/model/auth/TokenResponse.java b/seed/java-model/inferred-auth-implicit-reference/src/main/java/com/seed/inferredAuthImplicit/model/auth/TokenResponse.java new file mode 100644 index 000000000000..15c06739b007 --- /dev/null +++ b/seed/java-model/inferred-auth-implicit-reference/src/main/java/com/seed/inferredAuthImplicit/model/auth/TokenResponse.java @@ -0,0 +1,149 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package com.seed.inferredAuthImplicit.model.auth; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.seed.inferredAuthImplicit.core.ObjectMappers; +import java.lang.Object; +import java.lang.String; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize( + builder = TokenResponse.Builder.class +) +public final class TokenResponse { + private final String accessToken; + + private final int expiresIn; + + private final Optional refreshToken; + + private TokenResponse(String accessToken, int expiresIn, Optional refreshToken) { + this.accessToken = accessToken; + this.expiresIn = expiresIn; + this.refreshToken = refreshToken; + } + + @JsonProperty("access_token") + public String getAccessToken() { + return accessToken; + } + + @JsonProperty("expires_in") + public int getExpiresIn() { + return expiresIn; + } + + @JsonProperty("refresh_token") + public Optional getRefreshToken() { + return refreshToken; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof TokenResponse && equalTo((TokenResponse) other); + } + + private boolean equalTo(TokenResponse other) { + return accessToken.equals(other.accessToken) && expiresIn == other.expiresIn && refreshToken.equals(other.refreshToken); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.accessToken, this.expiresIn, this.refreshToken); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static AccessTokenStage builder() { + return new Builder(); + } + + public interface AccessTokenStage { + ExpiresInStage accessToken(String accessToken); + + Builder from(TokenResponse other); + } + + public interface ExpiresInStage { + _FinalStage expiresIn(int expiresIn); + } + + public interface _FinalStage { + TokenResponse build(); + + _FinalStage refreshToken(Optional refreshToken); + + _FinalStage refreshToken(String refreshToken); + } + + @JsonIgnoreProperties( + ignoreUnknown = true + ) + public static final class Builder implements AccessTokenStage, ExpiresInStage, _FinalStage { + private String accessToken; + + private int expiresIn; + + private Optional refreshToken = Optional.empty(); + + private Builder() { + } + + @java.lang.Override + public Builder from(TokenResponse other) { + accessToken(other.getAccessToken()); + expiresIn(other.getExpiresIn()); + refreshToken(other.getRefreshToken()); + return this; + } + + @java.lang.Override + @JsonSetter("access_token") + public ExpiresInStage accessToken(String accessToken) { + this.accessToken = Objects.requireNonNull(accessToken, "accessToken must not be null"); + return this; + } + + @java.lang.Override + @JsonSetter("expires_in") + public _FinalStage expiresIn(int expiresIn) { + this.expiresIn = expiresIn; + return this; + } + + @java.lang.Override + public _FinalStage refreshToken(String refreshToken) { + this.refreshToken = Optional.ofNullable(refreshToken); + return this; + } + + @java.lang.Override + @JsonSetter( + value = "refresh_token", + nulls = Nulls.SKIP + ) + public _FinalStage refreshToken(Optional refreshToken) { + this.refreshToken = refreshToken; + return this; + } + + @java.lang.Override + public TokenResponse build() { + return new TokenResponse(accessToken, expiresIn, refreshToken); + } + } +} diff --git a/seed/java-model/java-default-timeout/.github/workflows/ci.yml b/seed/java-model/java-default-timeout/.github/workflows/ci.yml new file mode 100644 index 000000000000..7bffd9de06d8 --- /dev/null +++ b/seed/java-model/java-default-timeout/.github/workflows/ci.yml @@ -0,0 +1,61 @@ +name: ci + +on: [push] + +jobs: + compile: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Java + id: setup-jre + uses: actions/setup-java@v1 + with: + java-version: "11" + architecture: x64 + + - name: Compile + run: ./gradlew compileJava + + test: + needs: [ compile ] + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Java + id: setup-jre + uses: actions/setup-java@v1 + with: + java-version: "11" + architecture: x64 + + - name: Test + run: ./gradlew test + publish: + needs: [ compile, test ] + if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Java + id: setup-jre + uses: actions/setup-java@v1 + with: + java-version: "11" + architecture: x64 + + - name: Publish to maven + run: | + ./gradlew publish + env: + MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} + MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} + MAVEN_PUBLISH_REGISTRY_URL: "" diff --git a/seed/java-model/java-default-timeout/.gitignore b/seed/java-model/java-default-timeout/.gitignore new file mode 100644 index 000000000000..d4199abc2cd4 --- /dev/null +++ b/seed/java-model/java-default-timeout/.gitignore @@ -0,0 +1,24 @@ +*.class +.project +.gradle +? +.classpath +.checkstyle +.settings +.node +build + +# IntelliJ +*.iml +*.ipr +*.iws +.idea/ +out/ + +# Eclipse/IntelliJ APT +generated_src/ +generated_testSrc/ +generated/ + +bin +build \ No newline at end of file diff --git a/seed/java-model/java-default-timeout/build.gradle b/seed/java-model/java-default-timeout/build.gradle new file mode 100644 index 000000000000..eda657a3e2a6 --- /dev/null +++ b/seed/java-model/java-default-timeout/build.gradle @@ -0,0 +1,98 @@ +plugins { + id 'java-library' + id 'maven-publish' + id 'com.diffplug.spotless' version '6.11.0' +} + +repositories { + mavenCentral() + maven { + url 'https://s01.oss.sonatype.org/content/repositories/releases/' + } +} + +dependencies { + api 'com.fasterxml.jackson.core:jackson-databind:2.18.2' + api 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.18.2' + api 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.2' +} + + +sourceCompatibility = 1.8 +targetCompatibility = 1.8 + +tasks.withType(Javadoc) { + failOnError false + options.addStringOption('Xdoclint:none', '-quiet') +} + +spotless { + java { + palantirJavaFormat() + } +} + + +java { + withSourcesJar() + withJavadocJar() +} + + +group = 'com.fern' + +version = '0.0.1' + +jar { + dependsOn(":generatePomFileForMavenPublication") + archiveBaseName = "java-default-timeout" +} + +sourcesJar { + archiveBaseName = "java-default-timeout" +} + +javadocJar { + archiveBaseName = "java-default-timeout" +} + +test { + useJUnitPlatform() + testLogging { + showStandardStreams = true + } +} + +publishing { + publications { + maven(MavenPublication) { + groupId = 'com.fern' + artifactId = 'java-default-timeout' + version = '0.0.1' + from components.java + pom { + licenses { + license { + name = 'The MIT License (MIT)' + url = 'https://mit-license.org/' + } + } + scm { + connection = 'scm:git:git://github.com/java-default-timeout/fern.git' + developerConnection = 'scm:git:git://github.com/java-default-timeout/fern.git' + url = 'https://github.com/java-default-timeout/fern' + } + } + } + } + repositories { + maven { + url "$System.env.MAVEN_PUBLISH_REGISTRY_URL" + credentials { + username "$System.env.MAVEN_USERNAME" + password "$System.env.MAVEN_PASSWORD" + } + } + } +} + diff --git a/seed/java-model/java-default-timeout/settings.gradle b/seed/java-model/java-default-timeout/settings.gradle new file mode 100644 index 000000000000..b149e7ed5cea --- /dev/null +++ b/seed/java-model/java-default-timeout/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = 'java-default-timeout' + diff --git a/seed/java-model/java-default-timeout/snippet.json b/seed/java-model/java-default-timeout/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/java-model/java-default-timeout/src/main/java/com/seed/javaDefaultTimeout/core/DateTimeDeserializer.java b/seed/java-model/java-default-timeout/src/main/java/com/seed/javaDefaultTimeout/core/DateTimeDeserializer.java new file mode 100644 index 000000000000..59d276e0f19e --- /dev/null +++ b/seed/java-model/java-default-timeout/src/main/java/com/seed/javaDefaultTimeout/core/DateTimeDeserializer.java @@ -0,0 +1,56 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package com.seed.javaDefaultTimeout.core; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.module.SimpleModule; +import java.io.IOException; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalQueries; + +/** + * Custom deserializer that handles converting ISO8601 dates into {@link OffsetDateTime} objects. + */ +class DateTimeDeserializer extends JsonDeserializer { + private static final SimpleModule MODULE; + + static { + MODULE = new SimpleModule().addDeserializer(OffsetDateTime.class, new DateTimeDeserializer()); + } + + /** + * Gets a module wrapping this deserializer as an adapter for the Jackson ObjectMapper. + * + * @return A {@link SimpleModule} to be plugged onto Jackson ObjectMapper. + */ + public static SimpleModule getModule() { + return MODULE; + } + + @Override + public OffsetDateTime deserialize(JsonParser parser, DeserializationContext context) throws IOException { + JsonToken token = parser.currentToken(); + if (token == JsonToken.VALUE_NUMBER_INT) { + return OffsetDateTime.ofInstant(Instant.ofEpochSecond(parser.getValueAsLong()), ZoneOffset.UTC); + } else { + TemporalAccessor temporal = DateTimeFormatter.ISO_DATE_TIME.parseBest( + parser.getValueAsString(), OffsetDateTime::from, LocalDateTime::from); + + if (temporal.query(TemporalQueries.offset()) == null) { + return LocalDateTime.from(temporal).atOffset(ZoneOffset.UTC); + } else { + return OffsetDateTime.from(temporal); + } + } + } +} \ No newline at end of file diff --git a/seed/java-model/java-default-timeout/src/main/java/com/seed/javaDefaultTimeout/core/ObjectMappers.java b/seed/java-model/java-default-timeout/src/main/java/com/seed/javaDefaultTimeout/core/ObjectMappers.java new file mode 100644 index 000000000000..f4de2441b3d1 --- /dev/null +++ b/seed/java-model/java-default-timeout/src/main/java/com/seed/javaDefaultTimeout/core/ObjectMappers.java @@ -0,0 +1,51 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package com.seed.javaDefaultTimeout.core; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import java.io.IOException; +import java.lang.Integer; +import java.lang.Object; +import java.lang.String; + +public final class ObjectMappers { + public static final ObjectMapper JSON_MAPPER = JsonMapper.builder() + .addModule(new Jdk8Module()) + .addModule(new JavaTimeModule()) + .addModule(DateTimeDeserializer.getModule()) + .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .build(); + + private ObjectMappers() { + } + + public static String stringify(Object o) { + try { + return JSON_MAPPER.setSerializationInclusion(JsonInclude.Include.ALWAYS) + .writerWithDefaultPrettyPrinter() + .writeValueAsString(o); + } + catch (IOException e) { + return o.getClass().getName() + "@" + Integer.toHexString(o.hashCode()); + } + } + + public static Object parseErrorBody(String responseBodyString) { + try { + return JSON_MAPPER.readValue(responseBodyString, Object.class); + } + catch (JsonProcessingException ignored) { + return responseBodyString; + } + } + } diff --git a/seed/java-model/java-default-timeout/src/main/java/com/seed/javaDefaultTimeout/model/User.java b/seed/java-model/java-default-timeout/src/main/java/com/seed/javaDefaultTimeout/model/User.java new file mode 100644 index 000000000000..b55e4648bd31 --- /dev/null +++ b/seed/java-model/java-default-timeout/src/main/java/com/seed/javaDefaultTimeout/model/User.java @@ -0,0 +1,116 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package com.seed.javaDefaultTimeout.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.seed.javaDefaultTimeout.core.ObjectMappers; +import java.lang.Object; +import java.lang.String; +import java.util.Objects; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize( + builder = User.Builder.class +) +public final class User { + private final String id; + + private final String name; + + private User(String id, String name) { + this.id = id; + this.name = name; + } + + @JsonProperty("id") + public String getId() { + return id; + } + + @JsonProperty("name") + public String getName() { + return name; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof User && equalTo((User) other); + } + + private boolean equalTo(User other) { + return id.equals(other.id) && name.equals(other.name); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.id, this.name); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static IdStage builder() { + return new Builder(); + } + + public interface IdStage { + NameStage id(String id); + + Builder from(User other); + } + + public interface NameStage { + _FinalStage name(String name); + } + + public interface _FinalStage { + User build(); + } + + @JsonIgnoreProperties( + ignoreUnknown = true + ) + public static final class Builder implements IdStage, NameStage, _FinalStage { + private String id; + + private String name; + + private Builder() { + } + + @java.lang.Override + public Builder from(User other) { + id(other.getId()); + name(other.getName()); + return this; + } + + @java.lang.Override + @JsonSetter("id") + public NameStage id(String id) { + this.id = Objects.requireNonNull(id, "id must not be null"); + return this; + } + + @java.lang.Override + @JsonSetter("name") + public _FinalStage name(String name) { + this.name = Objects.requireNonNull(name, "name must not be null"); + return this; + } + + @java.lang.Override + public User build() { + return new User(id, name); + } + } +} diff --git a/seed/java-model/java-optional-nullable-query-params/.github/workflows/ci.yml b/seed/java-model/java-optional-nullable-query-params/.github/workflows/ci.yml new file mode 100644 index 000000000000..7bffd9de06d8 --- /dev/null +++ b/seed/java-model/java-optional-nullable-query-params/.github/workflows/ci.yml @@ -0,0 +1,61 @@ +name: ci + +on: [push] + +jobs: + compile: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Java + id: setup-jre + uses: actions/setup-java@v1 + with: + java-version: "11" + architecture: x64 + + - name: Compile + run: ./gradlew compileJava + + test: + needs: [ compile ] + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Java + id: setup-jre + uses: actions/setup-java@v1 + with: + java-version: "11" + architecture: x64 + + - name: Test + run: ./gradlew test + publish: + needs: [ compile, test ] + if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Java + id: setup-jre + uses: actions/setup-java@v1 + with: + java-version: "11" + architecture: x64 + + - name: Publish to maven + run: | + ./gradlew publish + env: + MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} + MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} + MAVEN_PUBLISH_REGISTRY_URL: "" diff --git a/seed/java-model/java-optional-nullable-query-params/.gitignore b/seed/java-model/java-optional-nullable-query-params/.gitignore new file mode 100644 index 000000000000..d4199abc2cd4 --- /dev/null +++ b/seed/java-model/java-optional-nullable-query-params/.gitignore @@ -0,0 +1,24 @@ +*.class +.project +.gradle +? +.classpath +.checkstyle +.settings +.node +build + +# IntelliJ +*.iml +*.ipr +*.iws +.idea/ +out/ + +# Eclipse/IntelliJ APT +generated_src/ +generated_testSrc/ +generated/ + +bin +build \ No newline at end of file diff --git a/seed/java-model/java-optional-nullable-query-params/build.gradle b/seed/java-model/java-optional-nullable-query-params/build.gradle new file mode 100644 index 000000000000..894f1611a346 --- /dev/null +++ b/seed/java-model/java-optional-nullable-query-params/build.gradle @@ -0,0 +1,98 @@ +plugins { + id 'java-library' + id 'maven-publish' + id 'com.diffplug.spotless' version '6.11.0' +} + +repositories { + mavenCentral() + maven { + url 'https://s01.oss.sonatype.org/content/repositories/releases/' + } +} + +dependencies { + api 'com.fasterxml.jackson.core:jackson-databind:2.18.2' + api 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.18.2' + api 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.2' +} + + +sourceCompatibility = 1.8 +targetCompatibility = 1.8 + +tasks.withType(Javadoc) { + failOnError false + options.addStringOption('Xdoclint:none', '-quiet') +} + +spotless { + java { + palantirJavaFormat() + } +} + + +java { + withSourcesJar() + withJavadocJar() +} + + +group = 'com.fern' + +version = '0.0.1' + +jar { + dependsOn(":generatePomFileForMavenPublication") + archiveBaseName = "java-optional-nullable-query-params" +} + +sourcesJar { + archiveBaseName = "java-optional-nullable-query-params" +} + +javadocJar { + archiveBaseName = "java-optional-nullable-query-params" +} + +test { + useJUnitPlatform() + testLogging { + showStandardStreams = true + } +} + +publishing { + publications { + maven(MavenPublication) { + groupId = 'com.fern' + artifactId = 'java-optional-nullable-query-params' + version = '0.0.1' + from components.java + pom { + licenses { + license { + name = 'The MIT License (MIT)' + url = 'https://mit-license.org/' + } + } + scm { + connection = 'scm:git:git://github.com/java-optional-nullable-query-params/fern.git' + developerConnection = 'scm:git:git://github.com/java-optional-nullable-query-params/fern.git' + url = 'https://github.com/java-optional-nullable-query-params/fern' + } + } + } + } + repositories { + maven { + url "$System.env.MAVEN_PUBLISH_REGISTRY_URL" + credentials { + username "$System.env.MAVEN_USERNAME" + password "$System.env.MAVEN_PASSWORD" + } + } + } +} + diff --git a/seed/java-model/java-optional-nullable-query-params/settings.gradle b/seed/java-model/java-optional-nullable-query-params/settings.gradle new file mode 100644 index 000000000000..ff9038f608c3 --- /dev/null +++ b/seed/java-model/java-optional-nullable-query-params/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = 'java-optional-nullable-query-params' + diff --git a/seed/java-model/java-optional-nullable-query-params/snippet.json b/seed/java-model/java-optional-nullable-query-params/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/java-model/java-optional-nullable-query-params/src/main/java/com/seed/javaOptionalNullableQueryParams/core/DateTimeDeserializer.java b/seed/java-model/java-optional-nullable-query-params/src/main/java/com/seed/javaOptionalNullableQueryParams/core/DateTimeDeserializer.java new file mode 100644 index 000000000000..c469079159d2 --- /dev/null +++ b/seed/java-model/java-optional-nullable-query-params/src/main/java/com/seed/javaOptionalNullableQueryParams/core/DateTimeDeserializer.java @@ -0,0 +1,56 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package com.seed.javaOptionalNullableQueryParams.core; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.module.SimpleModule; +import java.io.IOException; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalQueries; + +/** + * Custom deserializer that handles converting ISO8601 dates into {@link OffsetDateTime} objects. + */ +class DateTimeDeserializer extends JsonDeserializer { + private static final SimpleModule MODULE; + + static { + MODULE = new SimpleModule().addDeserializer(OffsetDateTime.class, new DateTimeDeserializer()); + } + + /** + * Gets a module wrapping this deserializer as an adapter for the Jackson ObjectMapper. + * + * @return A {@link SimpleModule} to be plugged onto Jackson ObjectMapper. + */ + public static SimpleModule getModule() { + return MODULE; + } + + @Override + public OffsetDateTime deserialize(JsonParser parser, DeserializationContext context) throws IOException { + JsonToken token = parser.currentToken(); + if (token == JsonToken.VALUE_NUMBER_INT) { + return OffsetDateTime.ofInstant(Instant.ofEpochSecond(parser.getValueAsLong()), ZoneOffset.UTC); + } else { + TemporalAccessor temporal = DateTimeFormatter.ISO_DATE_TIME.parseBest( + parser.getValueAsString(), OffsetDateTime::from, LocalDateTime::from); + + if (temporal.query(TemporalQueries.offset()) == null) { + return LocalDateTime.from(temporal).atOffset(ZoneOffset.UTC); + } else { + return OffsetDateTime.from(temporal); + } + } + } +} \ No newline at end of file diff --git a/seed/java-model/java-optional-nullable-query-params/src/main/java/com/seed/javaOptionalNullableQueryParams/core/ObjectMappers.java b/seed/java-model/java-optional-nullable-query-params/src/main/java/com/seed/javaOptionalNullableQueryParams/core/ObjectMappers.java new file mode 100644 index 000000000000..298a9f1b71e7 --- /dev/null +++ b/seed/java-model/java-optional-nullable-query-params/src/main/java/com/seed/javaOptionalNullableQueryParams/core/ObjectMappers.java @@ -0,0 +1,51 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package com.seed.javaOptionalNullableQueryParams.core; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import java.io.IOException; +import java.lang.Integer; +import java.lang.Object; +import java.lang.String; + +public final class ObjectMappers { + public static final ObjectMapper JSON_MAPPER = JsonMapper.builder() + .addModule(new Jdk8Module()) + .addModule(new JavaTimeModule()) + .addModule(DateTimeDeserializer.getModule()) + .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .build(); + + private ObjectMappers() { + } + + public static String stringify(Object o) { + try { + return JSON_MAPPER.setSerializationInclusion(JsonInclude.Include.ALWAYS) + .writerWithDefaultPrettyPrinter() + .writeValueAsString(o); + } + catch (IOException e) { + return o.getClass().getName() + "@" + Integer.toHexString(o.hashCode()); + } + } + + public static Object parseErrorBody(String responseBodyString) { + try { + return JSON_MAPPER.readValue(responseBodyString, Object.class); + } + catch (JsonProcessingException ignored) { + return responseBodyString; + } + } + } diff --git a/seed/java-model/java-optional-nullable-query-params/src/main/java/com/seed/javaOptionalNullableQueryParams/model/SearchResponse.java b/seed/java-model/java-optional-nullable-query-params/src/main/java/com/seed/javaOptionalNullableQueryParams/model/SearchResponse.java new file mode 100644 index 000000000000..b366fcf23849 --- /dev/null +++ b/seed/java-model/java-optional-nullable-query-params/src/main/java/com/seed/javaOptionalNullableQueryParams/model/SearchResponse.java @@ -0,0 +1,269 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package com.seed.javaOptionalNullableQueryParams.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.seed.javaOptionalNullableQueryParams.core.ObjectMappers; +import java.lang.Boolean; +import java.lang.Integer; +import java.lang.Object; +import java.lang.String; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize( + builder = SearchResponse.Builder.class +) +public final class SearchResponse { + private final List results; + + private final int totalCount; + + private final Optional query; + + private final Optional limit; + + private final Optional includeArchived; + + private final Optional sortOrder; + + private SearchResponse(List results, int totalCount, Optional query, + Optional limit, Optional includeArchived, Optional sortOrder) { + this.results = results; + this.totalCount = totalCount; + this.query = query; + this.limit = limit; + this.includeArchived = includeArchived; + this.sortOrder = sortOrder; + } + + @JsonProperty("results") + public List getResults() { + return results; + } + + @JsonProperty("totalCount") + public int getTotalCount() { + return totalCount; + } + + @JsonProperty("query") + public Optional getQuery() { + return query; + } + + @JsonProperty("limit") + public Optional getLimit() { + return limit; + } + + @JsonProperty("includeArchived") + public Optional getIncludeArchived() { + return includeArchived; + } + + @JsonProperty("sortOrder") + public Optional getSortOrder() { + return sortOrder; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof SearchResponse && equalTo((SearchResponse) other); + } + + private boolean equalTo(SearchResponse other) { + return results.equals(other.results) && totalCount == other.totalCount && query.equals(other.query) && limit.equals(other.limit) && includeArchived.equals(other.includeArchived) && sortOrder.equals(other.sortOrder); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.results, this.totalCount, this.query, this.limit, this.includeArchived, this.sortOrder); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static TotalCountStage builder() { + return new Builder(); + } + + public interface TotalCountStage { + _FinalStage totalCount(int totalCount); + + Builder from(SearchResponse other); + } + + public interface _FinalStage { + SearchResponse build(); + + _FinalStage results(List results); + + _FinalStage addResults(String results); + + _FinalStage addAllResults(List results); + + _FinalStage query(Optional query); + + _FinalStage query(String query); + + _FinalStage limit(Optional limit); + + _FinalStage limit(Integer limit); + + _FinalStage includeArchived(Optional includeArchived); + + _FinalStage includeArchived(Boolean includeArchived); + + _FinalStage sortOrder(Optional sortOrder); + + _FinalStage sortOrder(SortOrder sortOrder); + } + + @JsonIgnoreProperties( + ignoreUnknown = true + ) + public static final class Builder implements TotalCountStage, _FinalStage { + private int totalCount; + + private Optional sortOrder = Optional.empty(); + + private Optional includeArchived = Optional.empty(); + + private Optional limit = Optional.empty(); + + private Optional query = Optional.empty(); + + private List results = new ArrayList<>(); + + private Builder() { + } + + @java.lang.Override + public Builder from(SearchResponse other) { + results(other.getResults()); + totalCount(other.getTotalCount()); + query(other.getQuery()); + limit(other.getLimit()); + includeArchived(other.getIncludeArchived()); + sortOrder(other.getSortOrder()); + return this; + } + + @java.lang.Override + @JsonSetter("totalCount") + public _FinalStage totalCount(int totalCount) { + this.totalCount = totalCount; + return this; + } + + @java.lang.Override + public _FinalStage sortOrder(SortOrder sortOrder) { + this.sortOrder = Optional.ofNullable(sortOrder); + return this; + } + + @java.lang.Override + @JsonSetter( + value = "sortOrder", + nulls = Nulls.SKIP + ) + public _FinalStage sortOrder(Optional sortOrder) { + this.sortOrder = sortOrder; + return this; + } + + @java.lang.Override + public _FinalStage includeArchived(Boolean includeArchived) { + this.includeArchived = Optional.ofNullable(includeArchived); + return this; + } + + @java.lang.Override + @JsonSetter( + value = "includeArchived", + nulls = Nulls.SKIP + ) + public _FinalStage includeArchived(Optional includeArchived) { + this.includeArchived = includeArchived; + return this; + } + + @java.lang.Override + public _FinalStage limit(Integer limit) { + this.limit = Optional.ofNullable(limit); + return this; + } + + @java.lang.Override + @JsonSetter( + value = "limit", + nulls = Nulls.SKIP + ) + public _FinalStage limit(Optional limit) { + this.limit = limit; + return this; + } + + @java.lang.Override + public _FinalStage query(String query) { + this.query = Optional.ofNullable(query); + return this; + } + + @java.lang.Override + @JsonSetter( + value = "query", + nulls = Nulls.SKIP + ) + public _FinalStage query(Optional query) { + this.query = query; + return this; + } + + @java.lang.Override + public _FinalStage addAllResults(List results) { + if (results != null) { + this.results.addAll(results); + } + return this; + } + + @java.lang.Override + public _FinalStage addResults(String results) { + this.results.add(results); + return this; + } + + @java.lang.Override + @JsonSetter( + value = "results", + nulls = Nulls.SKIP + ) + public _FinalStage results(List results) { + this.results.clear(); + if (results != null) { + this.results.addAll(results); + } + return this; + } + + @java.lang.Override + public SearchResponse build() { + return new SearchResponse(results, totalCount, query, limit, includeArchived, sortOrder); + } + } +} diff --git a/seed/java-model/java-optional-nullable-query-params/src/main/java/com/seed/javaOptionalNullableQueryParams/model/SortOrder.java b/seed/java-model/java-optional-nullable-query-params/src/main/java/com/seed/javaOptionalNullableQueryParams/model/SortOrder.java new file mode 100644 index 000000000000..060d19b68766 --- /dev/null +++ b/seed/java-model/java-optional-nullable-query-params/src/main/java/com/seed/javaOptionalNullableQueryParams/model/SortOrder.java @@ -0,0 +1,26 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package com.seed.javaOptionalNullableQueryParams.model; + +import com.fasterxml.jackson.annotation.JsonValue; +import java.lang.String; + +public enum SortOrder { + ASC("ASC"), + + DESC("DESC"); + + private final String value; + + SortOrder(String value) { + this.value = value; + } + + @JsonValue + @java.lang.Override + public String toString() { + return this.value; + } +} diff --git a/seed/java-model/java-with-property-conflict/.github/workflows/ci.yml b/seed/java-model/java-with-property-conflict/.github/workflows/ci.yml new file mode 100644 index 000000000000..7bffd9de06d8 --- /dev/null +++ b/seed/java-model/java-with-property-conflict/.github/workflows/ci.yml @@ -0,0 +1,61 @@ +name: ci + +on: [push] + +jobs: + compile: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Java + id: setup-jre + uses: actions/setup-java@v1 + with: + java-version: "11" + architecture: x64 + + - name: Compile + run: ./gradlew compileJava + + test: + needs: [ compile ] + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Java + id: setup-jre + uses: actions/setup-java@v1 + with: + java-version: "11" + architecture: x64 + + - name: Test + run: ./gradlew test + publish: + needs: [ compile, test ] + if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Java + id: setup-jre + uses: actions/setup-java@v1 + with: + java-version: "11" + architecture: x64 + + - name: Publish to maven + run: | + ./gradlew publish + env: + MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} + MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} + MAVEN_PUBLISH_REGISTRY_URL: "" diff --git a/seed/java-model/java-with-property-conflict/.gitignore b/seed/java-model/java-with-property-conflict/.gitignore new file mode 100644 index 000000000000..d4199abc2cd4 --- /dev/null +++ b/seed/java-model/java-with-property-conflict/.gitignore @@ -0,0 +1,24 @@ +*.class +.project +.gradle +? +.classpath +.checkstyle +.settings +.node +build + +# IntelliJ +*.iml +*.ipr +*.iws +.idea/ +out/ + +# Eclipse/IntelliJ APT +generated_src/ +generated_testSrc/ +generated/ + +bin +build \ No newline at end of file diff --git a/seed/java-model/java-with-property-conflict/build.gradle b/seed/java-model/java-with-property-conflict/build.gradle new file mode 100644 index 000000000000..8f084cd162fc --- /dev/null +++ b/seed/java-model/java-with-property-conflict/build.gradle @@ -0,0 +1,98 @@ +plugins { + id 'java-library' + id 'maven-publish' + id 'com.diffplug.spotless' version '6.11.0' +} + +repositories { + mavenCentral() + maven { + url 'https://s01.oss.sonatype.org/content/repositories/releases/' + } +} + +dependencies { + api 'com.fasterxml.jackson.core:jackson-databind:2.18.2' + api 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.18.2' + api 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.2' +} + + +sourceCompatibility = 1.8 +targetCompatibility = 1.8 + +tasks.withType(Javadoc) { + failOnError false + options.addStringOption('Xdoclint:none', '-quiet') +} + +spotless { + java { + palantirJavaFormat() + } +} + + +java { + withSourcesJar() + withJavadocJar() +} + + +group = 'com.fern' + +version = '0.0.1' + +jar { + dependsOn(":generatePomFileForMavenPublication") + archiveBaseName = "java-with-property-conflict" +} + +sourcesJar { + archiveBaseName = "java-with-property-conflict" +} + +javadocJar { + archiveBaseName = "java-with-property-conflict" +} + +test { + useJUnitPlatform() + testLogging { + showStandardStreams = true + } +} + +publishing { + publications { + maven(MavenPublication) { + groupId = 'com.fern' + artifactId = 'java-with-property-conflict' + version = '0.0.1' + from components.java + pom { + licenses { + license { + name = 'The MIT License (MIT)' + url = 'https://mit-license.org/' + } + } + scm { + connection = 'scm:git:git://github.com/java-with-property-conflict/fern.git' + developerConnection = 'scm:git:git://github.com/java-with-property-conflict/fern.git' + url = 'https://github.com/java-with-property-conflict/fern' + } + } + } + } + repositories { + maven { + url "$System.env.MAVEN_PUBLISH_REGISTRY_URL" + credentials { + username "$System.env.MAVEN_USERNAME" + password "$System.env.MAVEN_PASSWORD" + } + } + } +} + diff --git a/seed/java-model/java-with-property-conflict/settings.gradle b/seed/java-model/java-with-property-conflict/settings.gradle new file mode 100644 index 000000000000..f4010fd2629f --- /dev/null +++ b/seed/java-model/java-with-property-conflict/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = 'java-with-property-conflict' + diff --git a/seed/java-model/java-with-property-conflict/snippet.json b/seed/java-model/java-with-property-conflict/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/java-model/java-with-property-conflict/src/main/java/com/seed/javaWithPropertyConflict/core/DateTimeDeserializer.java b/seed/java-model/java-with-property-conflict/src/main/java/com/seed/javaWithPropertyConflict/core/DateTimeDeserializer.java new file mode 100644 index 000000000000..4cf978ac16d9 --- /dev/null +++ b/seed/java-model/java-with-property-conflict/src/main/java/com/seed/javaWithPropertyConflict/core/DateTimeDeserializer.java @@ -0,0 +1,56 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package com.seed.javaWithPropertyConflict.core; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.module.SimpleModule; +import java.io.IOException; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalQueries; + +/** + * Custom deserializer that handles converting ISO8601 dates into {@link OffsetDateTime} objects. + */ +class DateTimeDeserializer extends JsonDeserializer { + private static final SimpleModule MODULE; + + static { + MODULE = new SimpleModule().addDeserializer(OffsetDateTime.class, new DateTimeDeserializer()); + } + + /** + * Gets a module wrapping this deserializer as an adapter for the Jackson ObjectMapper. + * + * @return A {@link SimpleModule} to be plugged onto Jackson ObjectMapper. + */ + public static SimpleModule getModule() { + return MODULE; + } + + @Override + public OffsetDateTime deserialize(JsonParser parser, DeserializationContext context) throws IOException { + JsonToken token = parser.currentToken(); + if (token == JsonToken.VALUE_NUMBER_INT) { + return OffsetDateTime.ofInstant(Instant.ofEpochSecond(parser.getValueAsLong()), ZoneOffset.UTC); + } else { + TemporalAccessor temporal = DateTimeFormatter.ISO_DATE_TIME.parseBest( + parser.getValueAsString(), OffsetDateTime::from, LocalDateTime::from); + + if (temporal.query(TemporalQueries.offset()) == null) { + return LocalDateTime.from(temporal).atOffset(ZoneOffset.UTC); + } else { + return OffsetDateTime.from(temporal); + } + } + } +} \ No newline at end of file diff --git a/seed/java-model/java-with-property-conflict/src/main/java/com/seed/javaWithPropertyConflict/core/ObjectMappers.java b/seed/java-model/java-with-property-conflict/src/main/java/com/seed/javaWithPropertyConflict/core/ObjectMappers.java new file mode 100644 index 000000000000..445e76c797b8 --- /dev/null +++ b/seed/java-model/java-with-property-conflict/src/main/java/com/seed/javaWithPropertyConflict/core/ObjectMappers.java @@ -0,0 +1,51 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package com.seed.javaWithPropertyConflict.core; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import java.io.IOException; +import java.lang.Integer; +import java.lang.Object; +import java.lang.String; + +public final class ObjectMappers { + public static final ObjectMapper JSON_MAPPER = JsonMapper.builder() + .addModule(new Jdk8Module()) + .addModule(new JavaTimeModule()) + .addModule(DateTimeDeserializer.getModule()) + .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .build(); + + private ObjectMappers() { + } + + public static String stringify(Object o) { + try { + return JSON_MAPPER.setSerializationInclusion(JsonInclude.Include.ALWAYS) + .writerWithDefaultPrettyPrinter() + .writeValueAsString(o); + } + catch (IOException e) { + return o.getClass().getName() + "@" + Integer.toHexString(o.hashCode()); + } + } + + public static Object parseErrorBody(String responseBodyString) { + try { + return JSON_MAPPER.readValue(responseBodyString, Object.class); + } + catch (JsonProcessingException ignored) { + return responseBodyString; + } + } + } diff --git a/seed/java-model/java-with-property-conflict/src/main/java/com/seed/javaWithPropertyConflict/model/ConfigurableProp.java b/seed/java-model/java-with-property-conflict/src/main/java/com/seed/javaWithPropertyConflict/model/ConfigurableProp.java new file mode 100644 index 000000000000..c9adedc5bd85 --- /dev/null +++ b/seed/java-model/java-with-property-conflict/src/main/java/com/seed/javaWithPropertyConflict/model/ConfigurableProp.java @@ -0,0 +1,243 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package com.seed.javaWithPropertyConflict.model; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.seed.javaWithPropertyConflict.core.ObjectMappers; +import java.lang.Boolean; +import java.lang.Object; +import java.lang.String; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize( + builder = ConfigurableProp.Builder.class +) +public final class ConfigurableProp { + private final String name; + + private final Optional label; + + private final Optional withLabel; + + private final Optional description; + + private ConfigurableProp(String name, Optional label, Optional withLabel, + Optional description) { + this.name = name; + this.label = label; + this.withLabel = withLabel; + this.description = description; + } + + /** + * @return The name of the prop + */ + @JsonProperty("name") + public String getName() { + return name; + } + + /** + * @return The display label + */ + @JsonProperty("label") + public Optional getLabel() { + return label; + } + + /** + * @return If true, requires label-value format + */ + @JsonProperty("withLabel") + public Optional getWithLabel() { + return withLabel; + } + + /** + * @return A description of the prop + */ + @JsonProperty("description") + public Optional getDescription() { + return description; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof ConfigurableProp && equalTo((ConfigurableProp) other); + } + + private boolean equalTo(ConfigurableProp other) { + return name.equals(other.name) && label.equals(other.label) && withLabel.equals(other.withLabel) && description.equals(other.description); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.name, this.label, this.withLabel, this.description); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static NameStage builder() { + return new Builder(); + } + + public interface NameStage { + /** + *

The name of the prop

+ */ + _FinalStage name(String name); + + Builder from(ConfigurableProp other); + } + + public interface _FinalStage { + ConfigurableProp build(); + + /** + *

The display label

+ */ + _FinalStage label(Optional label); + + _FinalStage label(String label); + + /** + *

If true, requires label-value format

+ */ + _FinalStage withLabel(Optional withLabel); + + _FinalStage withLabel(Boolean withLabel); + + /** + *

A description of the prop

+ */ + _FinalStage description(Optional description); + + _FinalStage description(String description); + } + + @JsonIgnoreProperties( + ignoreUnknown = true + ) + public static final class Builder implements NameStage, _FinalStage { + private String name; + + private Optional description = Optional.empty(); + + private Optional withLabel = Optional.empty(); + + private Optional label = Optional.empty(); + + private Builder() { + } + + @java.lang.Override + public Builder from(ConfigurableProp other) { + name(other.getName()); + label(other.getLabel()); + withLabel(other.getWithLabel()); + description(other.getDescription()); + return this; + } + + /** + *

The name of the prop

+ *

The name of the prop

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + @JsonSetter("name") + public _FinalStage name(String name) { + this.name = Objects.requireNonNull(name, "name must not be null"); + return this; + } + + /** + *

A description of the prop

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + public _FinalStage description(String description) { + this.description = Optional.ofNullable(description); + return this; + } + + /** + *

A description of the prop

+ */ + @java.lang.Override + @JsonSetter( + value = "description", + nulls = Nulls.SKIP + ) + public _FinalStage description(Optional description) { + this.description = description; + return this; + } + + /** + *

If true, requires label-value format

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + @JsonIgnore + public _FinalStage withLabel(Boolean withLabel) { + this.withLabel = Optional.ofNullable(withLabel); + return this; + } + + /** + *

If true, requires label-value format

+ */ + @java.lang.Override + @JsonSetter( + value = "withLabel", + nulls = Nulls.SKIP + ) + public _FinalStage withLabel(Optional withLabel) { + this.withLabel = withLabel; + return this; + } + + /** + *

The display label

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + public _FinalStage label(String label) { + this.label = Optional.ofNullable(label); + return this; + } + + /** + *

The display label

+ */ + @java.lang.Override + @JsonSetter( + value = "label", + nulls = Nulls.SKIP + ) + public _FinalStage label(Optional label) { + this.label = label; + return this; + } + + @java.lang.Override + public ConfigurableProp build() { + return new ConfigurableProp(name, label, withLabel, description); + } + } +} diff --git a/seed/java-model/nullable-allof-extends/.github/workflows/ci.yml b/seed/java-model/nullable-allof-extends/.github/workflows/ci.yml new file mode 100644 index 000000000000..7bffd9de06d8 --- /dev/null +++ b/seed/java-model/nullable-allof-extends/.github/workflows/ci.yml @@ -0,0 +1,61 @@ +name: ci + +on: [push] + +jobs: + compile: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Java + id: setup-jre + uses: actions/setup-java@v1 + with: + java-version: "11" + architecture: x64 + + - name: Compile + run: ./gradlew compileJava + + test: + needs: [ compile ] + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Java + id: setup-jre + uses: actions/setup-java@v1 + with: + java-version: "11" + architecture: x64 + + - name: Test + run: ./gradlew test + publish: + needs: [ compile, test ] + if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Java + id: setup-jre + uses: actions/setup-java@v1 + with: + java-version: "11" + architecture: x64 + + - name: Publish to maven + run: | + ./gradlew publish + env: + MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} + MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} + MAVEN_PUBLISH_REGISTRY_URL: "" diff --git a/seed/java-model/nullable-allof-extends/.gitignore b/seed/java-model/nullable-allof-extends/.gitignore new file mode 100644 index 000000000000..d4199abc2cd4 --- /dev/null +++ b/seed/java-model/nullable-allof-extends/.gitignore @@ -0,0 +1,24 @@ +*.class +.project +.gradle +? +.classpath +.checkstyle +.settings +.node +build + +# IntelliJ +*.iml +*.ipr +*.iws +.idea/ +out/ + +# Eclipse/IntelliJ APT +generated_src/ +generated_testSrc/ +generated/ + +bin +build \ No newline at end of file diff --git a/seed/java-model/nullable-allof-extends/build.gradle b/seed/java-model/nullable-allof-extends/build.gradle new file mode 100644 index 000000000000..c49dac5119c8 --- /dev/null +++ b/seed/java-model/nullable-allof-extends/build.gradle @@ -0,0 +1,98 @@ +plugins { + id 'java-library' + id 'maven-publish' + id 'com.diffplug.spotless' version '6.11.0' +} + +repositories { + mavenCentral() + maven { + url 'https://s01.oss.sonatype.org/content/repositories/releases/' + } +} + +dependencies { + api 'com.fasterxml.jackson.core:jackson-databind:2.18.2' + api 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.18.2' + api 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.2' +} + + +sourceCompatibility = 1.8 +targetCompatibility = 1.8 + +tasks.withType(Javadoc) { + failOnError false + options.addStringOption('Xdoclint:none', '-quiet') +} + +spotless { + java { + palantirJavaFormat() + } +} + + +java { + withSourcesJar() + withJavadocJar() +} + + +group = 'com.fern' + +version = '0.0.1' + +jar { + dependsOn(":generatePomFileForMavenPublication") + archiveBaseName = "nullable-allof-extends" +} + +sourcesJar { + archiveBaseName = "nullable-allof-extends" +} + +javadocJar { + archiveBaseName = "nullable-allof-extends" +} + +test { + useJUnitPlatform() + testLogging { + showStandardStreams = true + } +} + +publishing { + publications { + maven(MavenPublication) { + groupId = 'com.fern' + artifactId = 'nullable-allof-extends' + version = '0.0.1' + from components.java + pom { + licenses { + license { + name = 'The MIT License (MIT)' + url = 'https://mit-license.org/' + } + } + scm { + connection = 'scm:git:git://github.com/nullable-allof-extends/fern.git' + developerConnection = 'scm:git:git://github.com/nullable-allof-extends/fern.git' + url = 'https://github.com/nullable-allof-extends/fern' + } + } + } + } + repositories { + maven { + url "$System.env.MAVEN_PUBLISH_REGISTRY_URL" + credentials { + username "$System.env.MAVEN_USERNAME" + password "$System.env.MAVEN_PASSWORD" + } + } + } +} + diff --git a/seed/java-model/nullable-allof-extends/settings.gradle b/seed/java-model/nullable-allof-extends/settings.gradle new file mode 100644 index 000000000000..a769235760d3 --- /dev/null +++ b/seed/java-model/nullable-allof-extends/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = 'nullable-allof-extends' + diff --git a/seed/java-model/nullable-allof-extends/snippet.json b/seed/java-model/nullable-allof-extends/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/java-model/nullable-allof-extends/src/main/java/com/seed/api/core/DateTimeDeserializer.java b/seed/java-model/nullable-allof-extends/src/main/java/com/seed/api/core/DateTimeDeserializer.java new file mode 100644 index 000000000000..b67a9041b1f5 --- /dev/null +++ b/seed/java-model/nullable-allof-extends/src/main/java/com/seed/api/core/DateTimeDeserializer.java @@ -0,0 +1,56 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package com.seed.api.core; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.module.SimpleModule; +import java.io.IOException; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalQueries; + +/** + * Custom deserializer that handles converting ISO8601 dates into {@link OffsetDateTime} objects. + */ +class DateTimeDeserializer extends JsonDeserializer { + private static final SimpleModule MODULE; + + static { + MODULE = new SimpleModule().addDeserializer(OffsetDateTime.class, new DateTimeDeserializer()); + } + + /** + * Gets a module wrapping this deserializer as an adapter for the Jackson ObjectMapper. + * + * @return A {@link SimpleModule} to be plugged onto Jackson ObjectMapper. + */ + public static SimpleModule getModule() { + return MODULE; + } + + @Override + public OffsetDateTime deserialize(JsonParser parser, DeserializationContext context) throws IOException { + JsonToken token = parser.currentToken(); + if (token == JsonToken.VALUE_NUMBER_INT) { + return OffsetDateTime.ofInstant(Instant.ofEpochSecond(parser.getValueAsLong()), ZoneOffset.UTC); + } else { + TemporalAccessor temporal = DateTimeFormatter.ISO_DATE_TIME.parseBest( + parser.getValueAsString(), OffsetDateTime::from, LocalDateTime::from); + + if (temporal.query(TemporalQueries.offset()) == null) { + return LocalDateTime.from(temporal).atOffset(ZoneOffset.UTC); + } else { + return OffsetDateTime.from(temporal); + } + } + } +} \ No newline at end of file diff --git a/seed/java-model/nullable-allof-extends/src/main/java/com/seed/api/core/ObjectMappers.java b/seed/java-model/nullable-allof-extends/src/main/java/com/seed/api/core/ObjectMappers.java new file mode 100644 index 000000000000..1adf7ee82cca --- /dev/null +++ b/seed/java-model/nullable-allof-extends/src/main/java/com/seed/api/core/ObjectMappers.java @@ -0,0 +1,51 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package com.seed.api.core; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import java.io.IOException; +import java.lang.Integer; +import java.lang.Object; +import java.lang.String; + +public final class ObjectMappers { + public static final ObjectMapper JSON_MAPPER = JsonMapper.builder() + .addModule(new Jdk8Module()) + .addModule(new JavaTimeModule()) + .addModule(DateTimeDeserializer.getModule()) + .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .build(); + + private ObjectMappers() { + } + + public static String stringify(Object o) { + try { + return JSON_MAPPER.setSerializationInclusion(JsonInclude.Include.ALWAYS) + .writerWithDefaultPrettyPrinter() + .writeValueAsString(o); + } + catch (IOException e) { + return o.getClass().getName() + "@" + Integer.toHexString(o.hashCode()); + } + } + + public static Object parseErrorBody(String responseBodyString) { + try { + return JSON_MAPPER.readValue(responseBodyString, Object.class); + } + catch (JsonProcessingException ignored) { + return responseBodyString; + } + } + } diff --git a/seed/java-model/nullable-allof-extends/src/main/java/com/seed/api/model/INormalObject.java b/seed/java-model/nullable-allof-extends/src/main/java/com/seed/api/model/INormalObject.java new file mode 100644 index 000000000000..774b62d0dedb --- /dev/null +++ b/seed/java-model/nullable-allof-extends/src/main/java/com/seed/api/model/INormalObject.java @@ -0,0 +1,12 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package com.seed.api.model; + +import java.lang.String; +import java.util.Optional; + +public interface INormalObject { + Optional getNormalField(); +} diff --git a/seed/java-model/nullable-allof-extends/src/main/java/com/seed/api/model/INullableObject.java b/seed/java-model/nullable-allof-extends/src/main/java/com/seed/api/model/INullableObject.java new file mode 100644 index 000000000000..26b79097365d --- /dev/null +++ b/seed/java-model/nullable-allof-extends/src/main/java/com/seed/api/model/INullableObject.java @@ -0,0 +1,12 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package com.seed.api.model; + +import java.lang.String; +import java.util.Optional; + +public interface INullableObject { + Optional getNullableField(); +} diff --git a/seed/java-model/nullable-allof-extends/src/main/java/com/seed/api/model/NormalObject.java b/seed/java-model/nullable-allof-extends/src/main/java/com/seed/api/model/NormalObject.java new file mode 100644 index 000000000000..612ce5a7344d --- /dev/null +++ b/seed/java-model/nullable-allof-extends/src/main/java/com/seed/api/model/NormalObject.java @@ -0,0 +1,92 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package com.seed.api.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.seed.api.core.ObjectMappers; +import java.lang.Object; +import java.lang.String; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize( + builder = NormalObject.Builder.class +) +public final class NormalObject implements INormalObject { + private final Optional normalField; + + private NormalObject(Optional normalField) { + this.normalField = normalField; + } + + @JsonProperty("normalField") + @java.lang.Override + public Optional getNormalField() { + return normalField; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof NormalObject && equalTo((NormalObject) other); + } + + private boolean equalTo(NormalObject other) { + return normalField.equals(other.normalField); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.normalField); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties( + ignoreUnknown = true + ) + public static final class Builder { + private Optional normalField = Optional.empty(); + + private Builder() { + } + + public Builder from(NormalObject other) { + normalField(other.getNormalField()); + return this; + } + + @JsonSetter( + value = "normalField", + nulls = Nulls.SKIP + ) + public Builder normalField(Optional normalField) { + this.normalField = normalField; + return this; + } + + public Builder normalField(String normalField) { + this.normalField = Optional.ofNullable(normalField); + return this; + } + + public NormalObject build() { + return new NormalObject(normalField); + } + } +} diff --git a/seed/java-model/nullable-allof-extends/src/main/java/com/seed/api/model/NullableObject.java b/seed/java-model/nullable-allof-extends/src/main/java/com/seed/api/model/NullableObject.java new file mode 100644 index 000000000000..fbd4f8bdf26b --- /dev/null +++ b/seed/java-model/nullable-allof-extends/src/main/java/com/seed/api/model/NullableObject.java @@ -0,0 +1,92 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package com.seed.api.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.seed.api.core.ObjectMappers; +import java.lang.Object; +import java.lang.String; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize( + builder = NullableObject.Builder.class +) +public final class NullableObject implements INullableObject { + private final Optional nullableField; + + private NullableObject(Optional nullableField) { + this.nullableField = nullableField; + } + + @JsonProperty("nullableField") + @java.lang.Override + public Optional getNullableField() { + return nullableField; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof NullableObject && equalTo((NullableObject) other); + } + + private boolean equalTo(NullableObject other) { + return nullableField.equals(other.nullableField); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.nullableField); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties( + ignoreUnknown = true + ) + public static final class Builder { + private Optional nullableField = Optional.empty(); + + private Builder() { + } + + public Builder from(NullableObject other) { + nullableField(other.getNullableField()); + return this; + } + + @JsonSetter( + value = "nullableField", + nulls = Nulls.SKIP + ) + public Builder nullableField(Optional nullableField) { + this.nullableField = nullableField; + return this; + } + + public Builder nullableField(String nullableField) { + this.nullableField = Optional.ofNullable(nullableField); + return this; + } + + public NullableObject build() { + return new NullableObject(nullableField); + } + } +} diff --git a/seed/java-model/nullable-allof-extends/src/main/java/com/seed/api/model/RootObject.java b/seed/java-model/nullable-allof-extends/src/main/java/com/seed/api/model/RootObject.java new file mode 100644 index 000000000000..4370866618e6 --- /dev/null +++ b/seed/java-model/nullable-allof-extends/src/main/java/com/seed/api/model/RootObject.java @@ -0,0 +1,118 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package com.seed.api.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.seed.api.core.ObjectMappers; +import java.lang.Object; +import java.lang.String; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize( + builder = RootObject.Builder.class +) +public final class RootObject implements INormalObject, INullableObject { + private final Optional normalField; + + private final Optional nullableField; + + private RootObject(Optional normalField, Optional nullableField) { + this.normalField = normalField; + this.nullableField = nullableField; + } + + @JsonProperty("normalField") + @java.lang.Override + public Optional getNormalField() { + return normalField; + } + + @JsonProperty("nullableField") + @java.lang.Override + public Optional getNullableField() { + return nullableField; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof RootObject && equalTo((RootObject) other); + } + + private boolean equalTo(RootObject other) { + return normalField.equals(other.normalField) && nullableField.equals(other.nullableField); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.normalField, this.nullableField); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties( + ignoreUnknown = true + ) + public static final class Builder { + private Optional normalField = Optional.empty(); + + private Optional nullableField = Optional.empty(); + + private Builder() { + } + + public Builder from(RootObject other) { + normalField(other.getNormalField()); + nullableField(other.getNullableField()); + return this; + } + + @JsonSetter( + value = "normalField", + nulls = Nulls.SKIP + ) + public Builder normalField(Optional normalField) { + this.normalField = normalField; + return this; + } + + public Builder normalField(String normalField) { + this.normalField = Optional.ofNullable(normalField); + return this; + } + + @JsonSetter( + value = "nullableField", + nulls = Nulls.SKIP + ) + public Builder nullableField(Optional nullableField) { + this.nullableField = nullableField; + return this; + } + + public Builder nullableField(String nullableField) { + this.nullableField = Optional.ofNullable(nullableField); + return this; + } + + public RootObject build() { + return new RootObject(normalField, nullableField); + } + } +} diff --git a/seed/java-model/oauth-client-credentials-mandatory-auth/.github/workflows/ci.yml b/seed/java-model/oauth-client-credentials-mandatory-auth/.github/workflows/ci.yml new file mode 100644 index 000000000000..7bffd9de06d8 --- /dev/null +++ b/seed/java-model/oauth-client-credentials-mandatory-auth/.github/workflows/ci.yml @@ -0,0 +1,61 @@ +name: ci + +on: [push] + +jobs: + compile: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Java + id: setup-jre + uses: actions/setup-java@v1 + with: + java-version: "11" + architecture: x64 + + - name: Compile + run: ./gradlew compileJava + + test: + needs: [ compile ] + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Java + id: setup-jre + uses: actions/setup-java@v1 + with: + java-version: "11" + architecture: x64 + + - name: Test + run: ./gradlew test + publish: + needs: [ compile, test ] + if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Java + id: setup-jre + uses: actions/setup-java@v1 + with: + java-version: "11" + architecture: x64 + + - name: Publish to maven + run: | + ./gradlew publish + env: + MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} + MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} + MAVEN_PUBLISH_REGISTRY_URL: "" diff --git a/seed/java-model/oauth-client-credentials-mandatory-auth/.gitignore b/seed/java-model/oauth-client-credentials-mandatory-auth/.gitignore new file mode 100644 index 000000000000..d4199abc2cd4 --- /dev/null +++ b/seed/java-model/oauth-client-credentials-mandatory-auth/.gitignore @@ -0,0 +1,24 @@ +*.class +.project +.gradle +? +.classpath +.checkstyle +.settings +.node +build + +# IntelliJ +*.iml +*.ipr +*.iws +.idea/ +out/ + +# Eclipse/IntelliJ APT +generated_src/ +generated_testSrc/ +generated/ + +bin +build \ No newline at end of file diff --git a/seed/java-model/oauth-client-credentials-mandatory-auth/build.gradle b/seed/java-model/oauth-client-credentials-mandatory-auth/build.gradle new file mode 100644 index 000000000000..cadcf457a7b0 --- /dev/null +++ b/seed/java-model/oauth-client-credentials-mandatory-auth/build.gradle @@ -0,0 +1,98 @@ +plugins { + id 'java-library' + id 'maven-publish' + id 'com.diffplug.spotless' version '6.11.0' +} + +repositories { + mavenCentral() + maven { + url 'https://s01.oss.sonatype.org/content/repositories/releases/' + } +} + +dependencies { + api 'com.fasterxml.jackson.core:jackson-databind:2.18.2' + api 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.18.2' + api 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.2' +} + + +sourceCompatibility = 1.8 +targetCompatibility = 1.8 + +tasks.withType(Javadoc) { + failOnError false + options.addStringOption('Xdoclint:none', '-quiet') +} + +spotless { + java { + palantirJavaFormat() + } +} + + +java { + withSourcesJar() + withJavadocJar() +} + + +group = 'com.fern' + +version = '0.0.1' + +jar { + dependsOn(":generatePomFileForMavenPublication") + archiveBaseName = "oauth-client-credentials-mandatory-auth" +} + +sourcesJar { + archiveBaseName = "oauth-client-credentials-mandatory-auth" +} + +javadocJar { + archiveBaseName = "oauth-client-credentials-mandatory-auth" +} + +test { + useJUnitPlatform() + testLogging { + showStandardStreams = true + } +} + +publishing { + publications { + maven(MavenPublication) { + groupId = 'com.fern' + artifactId = 'oauth-client-credentials-mandatory-auth' + version = '0.0.1' + from components.java + pom { + licenses { + license { + name = 'The MIT License (MIT)' + url = 'https://mit-license.org/' + } + } + scm { + connection = 'scm:git:git://github.com/oauth-client-credentials-mandatory-auth/fern.git' + developerConnection = 'scm:git:git://github.com/oauth-client-credentials-mandatory-auth/fern.git' + url = 'https://github.com/oauth-client-credentials-mandatory-auth/fern' + } + } + } + } + repositories { + maven { + url "$System.env.MAVEN_PUBLISH_REGISTRY_URL" + credentials { + username "$System.env.MAVEN_USERNAME" + password "$System.env.MAVEN_PASSWORD" + } + } + } +} + diff --git a/seed/java-model/oauth-client-credentials-mandatory-auth/settings.gradle b/seed/java-model/oauth-client-credentials-mandatory-auth/settings.gradle new file mode 100644 index 000000000000..c3142e4fbd42 --- /dev/null +++ b/seed/java-model/oauth-client-credentials-mandatory-auth/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = 'oauth-client-credentials-mandatory-auth' + diff --git a/seed/java-model/oauth-client-credentials-mandatory-auth/snippet.json b/seed/java-model/oauth-client-credentials-mandatory-auth/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/java-model/oauth-client-credentials-mandatory-auth/src/main/java/com/seed/oauthClientCredentialsMandatoryAuth/core/DateTimeDeserializer.java b/seed/java-model/oauth-client-credentials-mandatory-auth/src/main/java/com/seed/oauthClientCredentialsMandatoryAuth/core/DateTimeDeserializer.java new file mode 100644 index 000000000000..18c8bd06a855 --- /dev/null +++ b/seed/java-model/oauth-client-credentials-mandatory-auth/src/main/java/com/seed/oauthClientCredentialsMandatoryAuth/core/DateTimeDeserializer.java @@ -0,0 +1,56 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package com.seed.oauthClientCredentialsMandatoryAuth.core; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.module.SimpleModule; +import java.io.IOException; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalQueries; + +/** + * Custom deserializer that handles converting ISO8601 dates into {@link OffsetDateTime} objects. + */ +class DateTimeDeserializer extends JsonDeserializer { + private static final SimpleModule MODULE; + + static { + MODULE = new SimpleModule().addDeserializer(OffsetDateTime.class, new DateTimeDeserializer()); + } + + /** + * Gets a module wrapping this deserializer as an adapter for the Jackson ObjectMapper. + * + * @return A {@link SimpleModule} to be plugged onto Jackson ObjectMapper. + */ + public static SimpleModule getModule() { + return MODULE; + } + + @Override + public OffsetDateTime deserialize(JsonParser parser, DeserializationContext context) throws IOException { + JsonToken token = parser.currentToken(); + if (token == JsonToken.VALUE_NUMBER_INT) { + return OffsetDateTime.ofInstant(Instant.ofEpochSecond(parser.getValueAsLong()), ZoneOffset.UTC); + } else { + TemporalAccessor temporal = DateTimeFormatter.ISO_DATE_TIME.parseBest( + parser.getValueAsString(), OffsetDateTime::from, LocalDateTime::from); + + if (temporal.query(TemporalQueries.offset()) == null) { + return LocalDateTime.from(temporal).atOffset(ZoneOffset.UTC); + } else { + return OffsetDateTime.from(temporal); + } + } + } +} \ No newline at end of file diff --git a/seed/java-model/oauth-client-credentials-mandatory-auth/src/main/java/com/seed/oauthClientCredentialsMandatoryAuth/core/ObjectMappers.java b/seed/java-model/oauth-client-credentials-mandatory-auth/src/main/java/com/seed/oauthClientCredentialsMandatoryAuth/core/ObjectMappers.java new file mode 100644 index 000000000000..2cb22861aad4 --- /dev/null +++ b/seed/java-model/oauth-client-credentials-mandatory-auth/src/main/java/com/seed/oauthClientCredentialsMandatoryAuth/core/ObjectMappers.java @@ -0,0 +1,51 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package com.seed.oauthClientCredentialsMandatoryAuth.core; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import java.io.IOException; +import java.lang.Integer; +import java.lang.Object; +import java.lang.String; + +public final class ObjectMappers { + public static final ObjectMapper JSON_MAPPER = JsonMapper.builder() + .addModule(new Jdk8Module()) + .addModule(new JavaTimeModule()) + .addModule(DateTimeDeserializer.getModule()) + .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .build(); + + private ObjectMappers() { + } + + public static String stringify(Object o) { + try { + return JSON_MAPPER.setSerializationInclusion(JsonInclude.Include.ALWAYS) + .writerWithDefaultPrettyPrinter() + .writeValueAsString(o); + } + catch (IOException e) { + return o.getClass().getName() + "@" + Integer.toHexString(o.hashCode()); + } + } + + public static Object parseErrorBody(String responseBodyString) { + try { + return JSON_MAPPER.readValue(responseBodyString, Object.class); + } + catch (JsonProcessingException ignored) { + return responseBodyString; + } + } + } diff --git a/seed/java-model/oauth-client-credentials-mandatory-auth/src/main/java/com/seed/oauthClientCredentialsMandatoryAuth/model/auth/TokenResponse.java b/seed/java-model/oauth-client-credentials-mandatory-auth/src/main/java/com/seed/oauthClientCredentialsMandatoryAuth/model/auth/TokenResponse.java new file mode 100644 index 000000000000..89cd6ec49079 --- /dev/null +++ b/seed/java-model/oauth-client-credentials-mandatory-auth/src/main/java/com/seed/oauthClientCredentialsMandatoryAuth/model/auth/TokenResponse.java @@ -0,0 +1,149 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package com.seed.oauthClientCredentialsMandatoryAuth.model.auth; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.seed.oauthClientCredentialsMandatoryAuth.core.ObjectMappers; +import java.lang.Object; +import java.lang.String; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize( + builder = TokenResponse.Builder.class +) +public final class TokenResponse { + private final String accessToken; + + private final int expiresIn; + + private final Optional refreshToken; + + private TokenResponse(String accessToken, int expiresIn, Optional refreshToken) { + this.accessToken = accessToken; + this.expiresIn = expiresIn; + this.refreshToken = refreshToken; + } + + @JsonProperty("access_token") + public String getAccessToken() { + return accessToken; + } + + @JsonProperty("expires_in") + public int getExpiresIn() { + return expiresIn; + } + + @JsonProperty("refresh_token") + public Optional getRefreshToken() { + return refreshToken; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof TokenResponse && equalTo((TokenResponse) other); + } + + private boolean equalTo(TokenResponse other) { + return accessToken.equals(other.accessToken) && expiresIn == other.expiresIn && refreshToken.equals(other.refreshToken); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.accessToken, this.expiresIn, this.refreshToken); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static AccessTokenStage builder() { + return new Builder(); + } + + public interface AccessTokenStage { + ExpiresInStage accessToken(String accessToken); + + Builder from(TokenResponse other); + } + + public interface ExpiresInStage { + _FinalStage expiresIn(int expiresIn); + } + + public interface _FinalStage { + TokenResponse build(); + + _FinalStage refreshToken(Optional refreshToken); + + _FinalStage refreshToken(String refreshToken); + } + + @JsonIgnoreProperties( + ignoreUnknown = true + ) + public static final class Builder implements AccessTokenStage, ExpiresInStage, _FinalStage { + private String accessToken; + + private int expiresIn; + + private Optional refreshToken = Optional.empty(); + + private Builder() { + } + + @java.lang.Override + public Builder from(TokenResponse other) { + accessToken(other.getAccessToken()); + expiresIn(other.getExpiresIn()); + refreshToken(other.getRefreshToken()); + return this; + } + + @java.lang.Override + @JsonSetter("access_token") + public ExpiresInStage accessToken(String accessToken) { + this.accessToken = Objects.requireNonNull(accessToken, "accessToken must not be null"); + return this; + } + + @java.lang.Override + @JsonSetter("expires_in") + public _FinalStage expiresIn(int expiresIn) { + this.expiresIn = expiresIn; + return this; + } + + @java.lang.Override + public _FinalStage refreshToken(String refreshToken) { + this.refreshToken = Optional.ofNullable(refreshToken); + return this; + } + + @java.lang.Override + @JsonSetter( + value = "refresh_token", + nulls = Nulls.SKIP + ) + public _FinalStage refreshToken(Optional refreshToken) { + this.refreshToken = refreshToken; + return this; + } + + @java.lang.Override + public TokenResponse build() { + return new TokenResponse(accessToken, expiresIn, refreshToken); + } + } +} diff --git a/seed/java-model/pagination/src/main/java/com/seed/pagination/model/users/ListUsersOptionalDataPaginationResponse.java b/seed/java-model/pagination/src/main/java/com/seed/pagination/model/users/ListUsersOptionalDataPaginationResponse.java new file mode 100644 index 000000000000..c65453c3b37b --- /dev/null +++ b/seed/java-model/pagination/src/main/java/com/seed/pagination/model/users/ListUsersOptionalDataPaginationResponse.java @@ -0,0 +1,203 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package com.seed.pagination.model.users; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.seed.pagination.core.ObjectMappers; +import java.lang.Boolean; +import java.lang.Object; +import java.lang.String; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize( + builder = ListUsersOptionalDataPaginationResponse.Builder.class +) +public final class ListUsersOptionalDataPaginationResponse { + private final Optional hasNextPage; + + private final Optional page; + + private final int totalCount; + + private final Optional> data; + + private ListUsersOptionalDataPaginationResponse(Optional hasNextPage, + Optional page, int totalCount, Optional> data) { + this.hasNextPage = hasNextPage; + this.page = page; + this.totalCount = totalCount; + this.data = data; + } + + @JsonProperty("hasNextPage") + public Optional getHasNextPage() { + return hasNextPage; + } + + @JsonProperty("page") + public Optional getPage() { + return page; + } + + /** + * @return The totall number of /users + */ + @JsonProperty("total_count") + public int getTotalCount() { + return totalCount; + } + + @JsonProperty("data") + public Optional> getData() { + return data; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof ListUsersOptionalDataPaginationResponse && equalTo((ListUsersOptionalDataPaginationResponse) other); + } + + private boolean equalTo(ListUsersOptionalDataPaginationResponse other) { + return hasNextPage.equals(other.hasNextPage) && page.equals(other.page) && totalCount == other.totalCount && data.equals(other.data); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.hasNextPage, this.page, this.totalCount, this.data); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static TotalCountStage builder() { + return new Builder(); + } + + public interface TotalCountStage { + /** + *

The totall number of /users

+ */ + _FinalStage totalCount(int totalCount); + + Builder from(ListUsersOptionalDataPaginationResponse other); + } + + public interface _FinalStage { + ListUsersOptionalDataPaginationResponse build(); + + _FinalStage hasNextPage(Optional hasNextPage); + + _FinalStage hasNextPage(Boolean hasNextPage); + + _FinalStage page(Optional page); + + _FinalStage page(Page page); + + _FinalStage data(Optional> data); + + _FinalStage data(List data); + } + + @JsonIgnoreProperties( + ignoreUnknown = true + ) + public static final class Builder implements TotalCountStage, _FinalStage { + private int totalCount; + + private Optional> data = Optional.empty(); + + private Optional page = Optional.empty(); + + private Optional hasNextPage = Optional.empty(); + + private Builder() { + } + + @java.lang.Override + public Builder from(ListUsersOptionalDataPaginationResponse other) { + hasNextPage(other.getHasNextPage()); + page(other.getPage()); + totalCount(other.getTotalCount()); + data(other.getData()); + return this; + } + + /** + *

The totall number of /users

+ *

The totall number of /users

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + @JsonSetter("total_count") + public _FinalStage totalCount(int totalCount) { + this.totalCount = totalCount; + return this; + } + + @java.lang.Override + public _FinalStage data(List data) { + this.data = Optional.ofNullable(data); + return this; + } + + @java.lang.Override + @JsonSetter( + value = "data", + nulls = Nulls.SKIP + ) + public _FinalStage data(Optional> data) { + this.data = data; + return this; + } + + @java.lang.Override + public _FinalStage page(Page page) { + this.page = Optional.ofNullable(page); + return this; + } + + @java.lang.Override + @JsonSetter( + value = "page", + nulls = Nulls.SKIP + ) + public _FinalStage page(Optional page) { + this.page = page; + return this; + } + + @java.lang.Override + public _FinalStage hasNextPage(Boolean hasNextPage) { + this.hasNextPage = Optional.ofNullable(hasNextPage); + return this; + } + + @java.lang.Override + @JsonSetter( + value = "hasNextPage", + nulls = Nulls.SKIP + ) + public _FinalStage hasNextPage(Optional hasNextPage) { + this.hasNextPage = hasNextPage; + return this; + } + + @java.lang.Override + public ListUsersOptionalDataPaginationResponse build() { + return new ListUsersOptionalDataPaginationResponse(hasNextPage, page, totalCount, data); + } + } +} diff --git a/seed/java-sdk/accept-header/accept-header/README.md b/seed/java-sdk/accept-header/accept-header/README.md index 5998d9690159..52c62d65a00e 100644 --- a/seed/java-sdk/accept-header/accept-header/README.md +++ b/seed/java-sdk/accept-header/accept-header/README.md @@ -143,7 +143,6 @@ SeedAcceptClient client = SeedAcceptClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.accept.SeedAcceptClient; import com.seed.accept.core.RequestOptions; @@ -151,7 +150,7 @@ import com.seed.accept.core.RequestOptions; // Client level SeedAcceptClient client = SeedAcceptClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -159,7 +158,7 @@ client.service().endpoint( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/alias/README.md b/seed/java-sdk/alias/README.md index 9d7eba423518..f1cab5f9045c 100644 --- a/seed/java-sdk/alias/README.md +++ b/seed/java-sdk/alias/README.md @@ -142,7 +142,6 @@ SeedAliasClient client = SeedAliasClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.alias.SeedAliasClient; import com.seed.alias.core.RequestOptions; @@ -150,7 +149,7 @@ import com.seed.alias.core.RequestOptions; // Client level SeedAliasClient client = SeedAliasClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -158,7 +157,7 @@ client.get( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/any-auth/README.md b/seed/java-sdk/any-auth/README.md index fbe14b900843..1afbc5e6ad0f 100644 --- a/seed/java-sdk/any-auth/README.md +++ b/seed/java-sdk/any-auth/README.md @@ -76,6 +76,29 @@ public class Example { } } ``` +## Authentication + +This SDK supports two authentication methods: + +### Option 1: Direct Bearer Token + +If you already have a valid access token, you can use it directly: + +```java +SeedAnyAuthClient client = SeedAnyAuthClient.withToken("your-access-token") + .url("https://api.example.com") + .build(); +``` + +### Option 2: OAuth Client Credentials + +The SDK can automatically handle token acquisition and refresh: + +```java +SeedAnyAuthClient client = SeedAnyAuthClient.withCredentials("client-id", "client-secret") + .url("https://api.example.com") + .build(); +``` ## Base Url @@ -151,7 +174,6 @@ SeedAnyAuthClient client = SeedAnyAuthClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.anyAuth.SeedAnyAuthClient; import com.seed.anyAuth.core.RequestOptions; @@ -159,7 +181,7 @@ import com.seed.anyAuth.core.RequestOptions; // Client level SeedAnyAuthClient client = SeedAnyAuthClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -167,7 +189,7 @@ client.auth().getToken( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/any-auth/src/main/java/com/seed/anyAuth/AsyncSeedAnyAuthClient.java b/seed/java-sdk/any-auth/src/main/java/com/seed/anyAuth/AsyncSeedAnyAuthClient.java index 529ca5256544..24828a670e76 100644 --- a/seed/java-sdk/any-auth/src/main/java/com/seed/anyAuth/AsyncSeedAnyAuthClient.java +++ b/seed/java-sdk/any-auth/src/main/java/com/seed/anyAuth/AsyncSeedAnyAuthClient.java @@ -30,7 +30,22 @@ public AsyncUserClient user() { return this.userClient.get(); } - public static AsyncSeedAnyAuthClientBuilder builder() { - return new AsyncSeedAnyAuthClientBuilder(); + /** + * Creates a client builder using a pre-generated access token. + * @param token The access token to use for authentication + * @return A builder configured for token authentication + */ + public static AsyncSeedAnyAuthClientBuilder._TokenAuth withToken(String token) { + return AsyncSeedAnyAuthClientBuilder.withToken(token); + } + + /** + * Creates a client builder using OAuth client credentials. + * @param clientId The OAuth client ID + * @param clientSecret The OAuth client secret + * @return A builder configured for OAuth authentication + */ + public static AsyncSeedAnyAuthClientBuilder._CredentialsAuth withCredentials(String clientId, String clientSecret) { + return AsyncSeedAnyAuthClientBuilder.withCredentials(clientId, clientSecret); } } diff --git a/seed/java-sdk/any-auth/src/main/java/com/seed/anyAuth/AsyncSeedAnyAuthClientBuilder.java b/seed/java-sdk/any-auth/src/main/java/com/seed/anyAuth/AsyncSeedAnyAuthClientBuilder.java index dd0b1c435ce6..a3bddf2d9942 100644 --- a/seed/java-sdk/any-auth/src/main/java/com/seed/anyAuth/AsyncSeedAnyAuthClientBuilder.java +++ b/seed/java-sdk/any-auth/src/main/java/com/seed/anyAuth/AsyncSeedAnyAuthClientBuilder.java @@ -23,17 +23,7 @@ public class AsyncSeedAnyAuthClientBuilder { private String apiKey = System.getenv("MY_API_KEY"); - private String clientId = System.getenv("MY_CLIENT_ID"); - - private String clientSecret = System.getenv("MY_CLIENT_SECRET"); - - private String audience = null; - - private String grantType = null; - - private String scope = null; - - private Environment environment; + protected Environment environment; private OkHttpClient httpClient; @@ -56,45 +46,27 @@ public AsyncSeedAnyAuthClientBuilder apiKey(String apiKey) { } /** - * Sets clientId. - * Defaults to the MY_CLIENT_ID environment variable. - */ - public AsyncSeedAnyAuthClientBuilder clientId(String clientId) { - this.clientId = clientId; - return this; - } - - /** - * Sets clientSecret. - * Defaults to the MY_CLIENT_SECRET environment variable. - */ - public AsyncSeedAnyAuthClientBuilder clientSecret(String clientSecret) { - this.clientSecret = clientSecret; - return this; - } - - /** - * Sets audience + * Creates a builder that uses a pre-generated access token for authentication. + * Use this when you already have a valid access token and want to bypass + * the OAuth client credentials flow. + * + * @param token The access token to use for Authorization header + * @return A builder configured for token authentication */ - public AsyncSeedAnyAuthClientBuilder audience(String audience) { - this.audience = audience; - return this; + public static _TokenAuth withToken(String token) { + return new _TokenAuth(token); } /** - * Sets grantType + * Creates a builder that uses OAuth client credentials for authentication. + * The builder will automatically handle token acquisition and refresh. + * + * @param clientId The OAuth client ID + * @param clientSecret The OAuth client secret + * @return A builder configured for OAuth client credentials authentication */ - public AsyncSeedAnyAuthClientBuilder grantType(String grantType) { - this.grantType = grantType; - return this; - } - - /** - * Sets scope - */ - public AsyncSeedAnyAuthClientBuilder scope(String scope) { - this.scope = scope; - return this; + public static _CredentialsAuth withCredentials(String clientId, String clientSecret) { + return new _CredentialsAuth(clientId, clientSecret); } public AsyncSeedAnyAuthClientBuilder url(String url) { @@ -183,14 +155,6 @@ protected void setAuthentication(ClientOptions.Builder builder) { builder.addHeader("Authorization", "Bearer " + this.token); } builder.addHeader("X-API-Key", this.apiKey); - if (this.clientId != null && this.clientSecret != null) { - ClientOptions.Builder authClientOptionsBuilder = - ClientOptions.builder().environment(this.environment); - AuthClient authClient = new AuthClient(authClientOptionsBuilder.build()); - OAuthTokenSupplier oAuthTokenSupplier = new OAuthTokenSupplier( - this.clientId, this.clientSecret, this.audience, this.grantType, this.scope, authClient); - builder.addHeader("Authorization", oAuthTokenSupplier); - } } /** @@ -272,4 +236,59 @@ public AsyncSeedAnyAuthClient build() { validateConfiguration(); return new AsyncSeedAnyAuthClient(buildClientOptions()); } + + public static final class _TokenAuth extends AsyncSeedAnyAuthClientBuilder { + private final String token; + + _TokenAuth(String token) { + this.token = token; + } + + @Override + protected void setAuthentication(ClientOptions.Builder builder) { + builder.addHeader("Authorization", "Bearer " + this.token); + } + } + + public static final class _CredentialsAuth extends AsyncSeedAnyAuthClientBuilder { + private final String clientId; + + private final String clientSecret; + + private String audience = null; + + private String grantType = null; + + private String scope = null; + + _CredentialsAuth(String clientId, String clientSecret) { + this.clientId = clientId; + this.clientSecret = clientSecret; + } + + public _CredentialsAuth audience(String audience) { + this.audience = audience; + return this; + } + + public _CredentialsAuth grantType(String grantType) { + this.grantType = grantType; + return this; + } + + public _CredentialsAuth scope(String scope) { + this.scope = scope; + return this; + } + + @Override + protected void setAuthentication(ClientOptions.Builder builder) { + ClientOptions.Builder authClientOptionsBuilder = + ClientOptions.builder().environment(this.environment); + AuthClient authClient = new AuthClient(authClientOptionsBuilder.build()); + OAuthTokenSupplier oAuthTokenSupplier = new OAuthTokenSupplier( + this.clientId, this.clientSecret, this.audience, this.grantType, this.scope, authClient); + builder.addHeader("Authorization", oAuthTokenSupplier); + } + } } diff --git a/seed/java-sdk/any-auth/src/main/java/com/seed/anyAuth/SeedAnyAuthClient.java b/seed/java-sdk/any-auth/src/main/java/com/seed/anyAuth/SeedAnyAuthClient.java index 15f91bb4dd82..800359a0ae68 100644 --- a/seed/java-sdk/any-auth/src/main/java/com/seed/anyAuth/SeedAnyAuthClient.java +++ b/seed/java-sdk/any-auth/src/main/java/com/seed/anyAuth/SeedAnyAuthClient.java @@ -30,7 +30,22 @@ public UserClient user() { return this.userClient.get(); } - public static SeedAnyAuthClientBuilder builder() { - return new SeedAnyAuthClientBuilder(); + /** + * Creates a client builder using a pre-generated access token. + * @param token The access token to use for authentication + * @return A builder configured for token authentication + */ + public static SeedAnyAuthClientBuilder._TokenAuth withToken(String token) { + return SeedAnyAuthClientBuilder.withToken(token); + } + + /** + * Creates a client builder using OAuth client credentials. + * @param clientId The OAuth client ID + * @param clientSecret The OAuth client secret + * @return A builder configured for OAuth authentication + */ + public static SeedAnyAuthClientBuilder._CredentialsAuth withCredentials(String clientId, String clientSecret) { + return SeedAnyAuthClientBuilder.withCredentials(clientId, clientSecret); } } diff --git a/seed/java-sdk/any-auth/src/main/java/com/seed/anyAuth/SeedAnyAuthClientBuilder.java b/seed/java-sdk/any-auth/src/main/java/com/seed/anyAuth/SeedAnyAuthClientBuilder.java index 8db318cc07f6..016590de8584 100644 --- a/seed/java-sdk/any-auth/src/main/java/com/seed/anyAuth/SeedAnyAuthClientBuilder.java +++ b/seed/java-sdk/any-auth/src/main/java/com/seed/anyAuth/SeedAnyAuthClientBuilder.java @@ -23,17 +23,7 @@ public class SeedAnyAuthClientBuilder { private String apiKey = System.getenv("MY_API_KEY"); - private String clientId = System.getenv("MY_CLIENT_ID"); - - private String clientSecret = System.getenv("MY_CLIENT_SECRET"); - - private String audience = null; - - private String grantType = null; - - private String scope = null; - - private Environment environment; + protected Environment environment; private OkHttpClient httpClient; @@ -56,45 +46,27 @@ public SeedAnyAuthClientBuilder apiKey(String apiKey) { } /** - * Sets clientId. - * Defaults to the MY_CLIENT_ID environment variable. - */ - public SeedAnyAuthClientBuilder clientId(String clientId) { - this.clientId = clientId; - return this; - } - - /** - * Sets clientSecret. - * Defaults to the MY_CLIENT_SECRET environment variable. - */ - public SeedAnyAuthClientBuilder clientSecret(String clientSecret) { - this.clientSecret = clientSecret; - return this; - } - - /** - * Sets audience + * Creates a builder that uses a pre-generated access token for authentication. + * Use this when you already have a valid access token and want to bypass + * the OAuth client credentials flow. + * + * @param token The access token to use for Authorization header + * @return A builder configured for token authentication */ - public SeedAnyAuthClientBuilder audience(String audience) { - this.audience = audience; - return this; + public static _TokenAuth withToken(String token) { + return new _TokenAuth(token); } /** - * Sets grantType + * Creates a builder that uses OAuth client credentials for authentication. + * The builder will automatically handle token acquisition and refresh. + * + * @param clientId The OAuth client ID + * @param clientSecret The OAuth client secret + * @return A builder configured for OAuth client credentials authentication */ - public SeedAnyAuthClientBuilder grantType(String grantType) { - this.grantType = grantType; - return this; - } - - /** - * Sets scope - */ - public SeedAnyAuthClientBuilder scope(String scope) { - this.scope = scope; - return this; + public static _CredentialsAuth withCredentials(String clientId, String clientSecret) { + return new _CredentialsAuth(clientId, clientSecret); } public SeedAnyAuthClientBuilder url(String url) { @@ -183,14 +155,6 @@ protected void setAuthentication(ClientOptions.Builder builder) { builder.addHeader("Authorization", "Bearer " + this.token); } builder.addHeader("X-API-Key", this.apiKey); - if (this.clientId != null && this.clientSecret != null) { - ClientOptions.Builder authClientOptionsBuilder = - ClientOptions.builder().environment(this.environment); - AuthClient authClient = new AuthClient(authClientOptionsBuilder.build()); - OAuthTokenSupplier oAuthTokenSupplier = new OAuthTokenSupplier( - this.clientId, this.clientSecret, this.audience, this.grantType, this.scope, authClient); - builder.addHeader("Authorization", oAuthTokenSupplier); - } } /** @@ -272,4 +236,59 @@ public SeedAnyAuthClient build() { validateConfiguration(); return new SeedAnyAuthClient(buildClientOptions()); } + + public static final class _TokenAuth extends SeedAnyAuthClientBuilder { + private final String token; + + _TokenAuth(String token) { + this.token = token; + } + + @Override + protected void setAuthentication(ClientOptions.Builder builder) { + builder.addHeader("Authorization", "Bearer " + this.token); + } + } + + public static final class _CredentialsAuth extends SeedAnyAuthClientBuilder { + private final String clientId; + + private final String clientSecret; + + private String audience = null; + + private String grantType = null; + + private String scope = null; + + _CredentialsAuth(String clientId, String clientSecret) { + this.clientId = clientId; + this.clientSecret = clientSecret; + } + + public _CredentialsAuth audience(String audience) { + this.audience = audience; + return this; + } + + public _CredentialsAuth grantType(String grantType) { + this.grantType = grantType; + return this; + } + + public _CredentialsAuth scope(String scope) { + this.scope = scope; + return this; + } + + @Override + protected void setAuthentication(ClientOptions.Builder builder) { + ClientOptions.Builder authClientOptionsBuilder = + ClientOptions.builder().environment(this.environment); + AuthClient authClient = new AuthClient(authClientOptionsBuilder.build()); + OAuthTokenSupplier oAuthTokenSupplier = new OAuthTokenSupplier( + this.clientId, this.clientSecret, this.audience, this.grantType, this.scope, authClient); + builder.addHeader("Authorization", oAuthTokenSupplier); + } + } } diff --git a/seed/java-sdk/api-wide-base-path/README.md b/seed/java-sdk/api-wide-base-path/README.md index de7a6fbb6710..064fb3364eec 100644 --- a/seed/java-sdk/api-wide-base-path/README.md +++ b/seed/java-sdk/api-wide-base-path/README.md @@ -143,7 +143,6 @@ SeedApiWideBasePathClient client = SeedApiWideBasePathClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.apiWideBasePath.SeedApiWideBasePathClient; import com.seed.apiWideBasePath.core.RequestOptions; @@ -151,7 +150,7 @@ import com.seed.apiWideBasePath.core.RequestOptions; // Client level SeedApiWideBasePathClient client = SeedApiWideBasePathClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -159,7 +158,7 @@ client.service().post( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/audiences/README.md b/seed/java-sdk/audiences/README.md index 4321d1ad3f65..3c7963fff1ec 100644 --- a/seed/java-sdk/audiences/README.md +++ b/seed/java-sdk/audiences/README.md @@ -165,7 +165,6 @@ SeedAudiencesClient client = SeedAudiencesClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.audiences.SeedAudiencesClient; import com.seed.audiences.core.RequestOptions; @@ -173,7 +172,7 @@ import com.seed.audiences.core.RequestOptions; // Client level SeedAudiencesClient client = SeedAudiencesClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -181,7 +180,7 @@ client.foo().find( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/basic-auth-environment-variables/README.md b/seed/java-sdk/basic-auth-environment-variables/README.md index 0d476f14d11e..269f986c5b12 100644 --- a/seed/java-sdk/basic-auth-environment-variables/README.md +++ b/seed/java-sdk/basic-auth-environment-variables/README.md @@ -146,7 +146,6 @@ SeedBasicAuthEnvironmentVariablesClient client = SeedBasicAuthEnvironmentVariabl ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.basicAuthEnvironmentVariables.SeedBasicAuthEnvironmentVariablesClient; import com.seed.basicAuthEnvironmentVariables.core.RequestOptions; @@ -154,7 +153,7 @@ import com.seed.basicAuthEnvironmentVariables.core.RequestOptions; // Client level SeedBasicAuthEnvironmentVariablesClient client = SeedBasicAuthEnvironmentVariablesClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -162,7 +161,7 @@ client.basicAuth().postWithBasicAuth( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/basic-auth/basic-auth/README.md b/seed/java-sdk/basic-auth/basic-auth/README.md index ba3cdebe7573..68d3e96a633c 100644 --- a/seed/java-sdk/basic-auth/basic-auth/README.md +++ b/seed/java-sdk/basic-auth/basic-auth/README.md @@ -146,7 +146,6 @@ SeedBasicAuthClient client = SeedBasicAuthClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.basicAuth.SeedBasicAuthClient; import com.seed.basicAuth.core.RequestOptions; @@ -154,7 +153,7 @@ import com.seed.basicAuth.core.RequestOptions; // Client level SeedBasicAuthClient client = SeedBasicAuthClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -162,7 +161,7 @@ client.basicAuth().postWithBasicAuth( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/bearer-token-environment-variable/README.md b/seed/java-sdk/bearer-token-environment-variable/README.md index 167fb19a72c7..d9c67c8b6e10 100644 --- a/seed/java-sdk/bearer-token-environment-variable/README.md +++ b/seed/java-sdk/bearer-token-environment-variable/README.md @@ -143,7 +143,6 @@ SeedBearerTokenEnvironmentVariableClient client = SeedBearerTokenEnvironmentVari ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.bearerTokenEnvironmentVariable.SeedBearerTokenEnvironmentVariableClient; import com.seed.bearerTokenEnvironmentVariable.core.RequestOptions; @@ -151,7 +150,7 @@ import com.seed.bearerTokenEnvironmentVariable.core.RequestOptions; // Client level SeedBearerTokenEnvironmentVariableClient client = SeedBearerTokenEnvironmentVariableClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -159,7 +158,7 @@ client.service().getWithBearerToken( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/bytes-download/README.md b/seed/java-sdk/bytes-download/README.md index d10d1016c07b..f2b129268a97 100644 --- a/seed/java-sdk/bytes-download/README.md +++ b/seed/java-sdk/bytes-download/README.md @@ -142,7 +142,6 @@ SeedBytesDownloadClient client = SeedBytesDownloadClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.bytesDownload.SeedBytesDownloadClient; import com.seed.bytesDownload.core.RequestOptions; @@ -150,7 +149,7 @@ import com.seed.bytesDownload.core.RequestOptions; // Client level SeedBytesDownloadClient client = SeedBytesDownloadClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -158,7 +157,7 @@ client.service().simple( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/bytes-upload/README.md b/seed/java-sdk/bytes-upload/README.md index 077919476b59..77adbb80aba7 100644 --- a/seed/java-sdk/bytes-upload/README.md +++ b/seed/java-sdk/bytes-upload/README.md @@ -142,7 +142,6 @@ SeedBytesUploadClient client = SeedBytesUploadClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.bytesUpload.SeedBytesUploadClient; import com.seed.bytesUpload.core.RequestOptions; @@ -150,7 +149,7 @@ import com.seed.bytesUpload.core.RequestOptions; // Client level SeedBytesUploadClient client = SeedBytesUploadClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -158,7 +157,7 @@ client.service().upload( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/client-side-params/default/README.md b/seed/java-sdk/client-side-params/default/README.md index 2dfaa6e6b32a..4f49027ceb8d 100644 --- a/seed/java-sdk/client-side-params/default/README.md +++ b/seed/java-sdk/client-side-params/default/README.md @@ -159,7 +159,6 @@ SeedClientSideParamsClient client = SeedClientSideParamsClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.clientSideParams.SeedClientSideParamsClient; import com.seed.clientSideParams.core.RequestOptions; @@ -167,7 +166,7 @@ import com.seed.clientSideParams.core.RequestOptions; // Client level SeedClientSideParamsClient client = SeedClientSideParamsClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -175,7 +174,7 @@ client.service().searchResources( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/content-type/README.md b/seed/java-sdk/content-type/README.md index 2f410b9a9be6..6b13dbbb2c65 100644 --- a/seed/java-sdk/content-type/README.md +++ b/seed/java-sdk/content-type/README.md @@ -149,7 +149,6 @@ SeedContentTypesClient client = SeedContentTypesClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.contentTypes.SeedContentTypesClient; import com.seed.contentTypes.core.RequestOptions; @@ -157,7 +156,7 @@ import com.seed.contentTypes.core.RequestOptions; // Client level SeedContentTypesClient client = SeedContentTypesClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -165,7 +164,7 @@ client.service().patch( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/cross-package-type-names/README.md b/seed/java-sdk/cross-package-type-names/README.md index 851ddddb1ccd..7d4ae4169c90 100644 --- a/seed/java-sdk/cross-package-type-names/README.md +++ b/seed/java-sdk/cross-package-type-names/README.md @@ -150,7 +150,6 @@ SeedCrossPackageTypeNamesClient client = SeedCrossPackageTypeNamesClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.crossPackageTypeNames.SeedCrossPackageTypeNamesClient; import com.seed.crossPackageTypeNames.core.RequestOptions; @@ -158,7 +157,7 @@ import com.seed.crossPackageTypeNames.core.RequestOptions; // Client level SeedCrossPackageTypeNamesClient client = SeedCrossPackageTypeNamesClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -166,7 +165,7 @@ client.foo().find( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/enum/forward-compatible-enums/README.md b/seed/java-sdk/enum/forward-compatible-enums/README.md index 840b2c946252..a802d21c0712 100644 --- a/seed/java-sdk/enum/forward-compatible-enums/README.md +++ b/seed/java-sdk/enum/forward-compatible-enums/README.md @@ -155,7 +155,6 @@ SeedEnumClient client = SeedEnumClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.enum.SeedEnumClient; import com.seed.enum.core.RequestOptions; @@ -163,7 +162,7 @@ import com.seed.enum.core.RequestOptions; // Client level SeedEnumClient client = SeedEnumClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -171,7 +170,7 @@ client.headers().send( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/enum/no-custom-config/README.md b/seed/java-sdk/enum/no-custom-config/README.md index 840b2c946252..a802d21c0712 100644 --- a/seed/java-sdk/enum/no-custom-config/README.md +++ b/seed/java-sdk/enum/no-custom-config/README.md @@ -155,7 +155,6 @@ SeedEnumClient client = SeedEnumClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.enum.SeedEnumClient; import com.seed.enum.core.RequestOptions; @@ -163,7 +162,7 @@ import com.seed.enum.core.RequestOptions; // Client level SeedEnumClient client = SeedEnumClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -171,7 +170,7 @@ client.headers().send( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/error-property/README.md b/seed/java-sdk/error-property/README.md index e34b69b9c3e9..3fbd13135423 100644 --- a/seed/java-sdk/error-property/README.md +++ b/seed/java-sdk/error-property/README.md @@ -142,7 +142,6 @@ SeedErrorPropertyClient client = SeedErrorPropertyClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.errorProperty.SeedErrorPropertyClient; import com.seed.errorProperty.core.RequestOptions; @@ -150,7 +149,7 @@ import com.seed.errorProperty.core.RequestOptions; // Client level SeedErrorPropertyClient client = SeedErrorPropertyClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -158,7 +157,7 @@ client.propertyBasedError().throwError( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/errors/README.md b/seed/java-sdk/errors/README.md index 2515e92702c0..2e1702442908 100644 --- a/seed/java-sdk/errors/README.md +++ b/seed/java-sdk/errors/README.md @@ -148,7 +148,6 @@ SeedErrorsClient client = SeedErrorsClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.errors.SeedErrorsClient; import com.seed.errors.core.RequestOptions; @@ -156,7 +155,7 @@ import com.seed.errors.core.RequestOptions; // Client level SeedErrorsClient client = SeedErrorsClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -164,7 +163,7 @@ client.simple().fooWithoutEndpointError( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/examples/default/README.md b/seed/java-sdk/examples/default/README.md index cc09132b68f5..5109808642e6 100644 --- a/seed/java-sdk/examples/default/README.md +++ b/seed/java-sdk/examples/default/README.md @@ -158,7 +158,6 @@ SeedExamplesClient client = SeedExamplesClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.examples.SeedExamplesClient; import com.seed.examples.core.RequestOptions; @@ -166,7 +165,7 @@ import com.seed.examples.core.RequestOptions; // Client level SeedExamplesClient client = SeedExamplesClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -174,7 +173,7 @@ client.echo( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/examples/inline-file-properties/README.md b/seed/java-sdk/examples/inline-file-properties/README.md index cc09132b68f5..5109808642e6 100644 --- a/seed/java-sdk/examples/inline-file-properties/README.md +++ b/seed/java-sdk/examples/inline-file-properties/README.md @@ -158,7 +158,6 @@ SeedExamplesClient client = SeedExamplesClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.examples.SeedExamplesClient; import com.seed.examples.core.RequestOptions; @@ -166,7 +165,7 @@ import com.seed.examples.core.RequestOptions; // Client level SeedExamplesClient client = SeedExamplesClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -174,7 +173,7 @@ client.echo( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/examples/no-custom-config/README.md b/seed/java-sdk/examples/no-custom-config/README.md index cc09132b68f5..5109808642e6 100644 --- a/seed/java-sdk/examples/no-custom-config/README.md +++ b/seed/java-sdk/examples/no-custom-config/README.md @@ -158,7 +158,6 @@ SeedExamplesClient client = SeedExamplesClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.examples.SeedExamplesClient; import com.seed.examples.core.RequestOptions; @@ -166,7 +165,7 @@ import com.seed.examples.core.RequestOptions; // Client level SeedExamplesClient client = SeedExamplesClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -174,7 +173,7 @@ client.echo( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/examples/readme-config/README.md b/seed/java-sdk/examples/readme-config/README.md index 3153dd1354da..532b7f14b8b1 100644 --- a/seed/java-sdk/examples/readme-config/README.md +++ b/seed/java-sdk/examples/readme-config/README.md @@ -199,7 +199,6 @@ SeedExamplesClient client = SeedExamplesClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.examples.SeedExamplesClient; import com.seed.examples.core.RequestOptions; @@ -207,7 +206,7 @@ import com.seed.examples.core.RequestOptions; // Client level SeedExamplesClient client = SeedExamplesClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -215,7 +214,7 @@ client.service().getMovie( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` @@ -227,7 +226,7 @@ import com.seed.examples.core.RequestOptions; // Client level SeedExamplesClient client = SeedExamplesClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -235,7 +234,7 @@ client.service().createMovie( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/exhaustive/custom-client-class-name/README.md b/seed/java-sdk/exhaustive/custom-client-class-name/README.md index 49a1894206db..9ce85800d4c0 100644 --- a/seed/java-sdk/exhaustive/custom-client-class-name/README.md +++ b/seed/java-sdk/exhaustive/custom-client-class-name/README.md @@ -146,7 +146,6 @@ Best client = Best ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.exhaustive.Best; import com.seed.exhaustive.core.RequestOptions; @@ -154,7 +153,7 @@ import com.seed.exhaustive.core.RequestOptions; // Client level Best client = Best .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -162,7 +161,7 @@ client.endpoints().container().getAndReturnListOfPrimitives( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/exhaustive/custom-dependency/README.md b/seed/java-sdk/exhaustive/custom-dependency/README.md index 7d9a01d785e5..e25d271b83e1 100644 --- a/seed/java-sdk/exhaustive/custom-dependency/README.md +++ b/seed/java-sdk/exhaustive/custom-dependency/README.md @@ -146,7 +146,6 @@ SeedExhaustiveClient client = SeedExhaustiveClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.exhaustive.SeedExhaustiveClient; import com.seed.exhaustive.core.RequestOptions; @@ -154,7 +153,7 @@ import com.seed.exhaustive.core.RequestOptions; // Client level SeedExhaustiveClient client = SeedExhaustiveClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -162,7 +161,7 @@ client.endpoints().container().getAndReturnListOfPrimitives( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/exhaustive/custom-error-names/README.md b/seed/java-sdk/exhaustive/custom-error-names/README.md index baf6b970526c..d5e4d96f4025 100644 --- a/seed/java-sdk/exhaustive/custom-error-names/README.md +++ b/seed/java-sdk/exhaustive/custom-error-names/README.md @@ -146,7 +146,6 @@ SeedExhaustiveClient client = SeedExhaustiveClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.exhaustive.SeedExhaustiveClient; import com.seed.exhaustive.core.RequestOptions; @@ -154,7 +153,7 @@ import com.seed.exhaustive.core.RequestOptions; // Client level SeedExhaustiveClient client = SeedExhaustiveClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -162,7 +161,7 @@ client.endpoints().container().getAndReturnListOfPrimitives( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/exhaustive/custom-license/README.md b/seed/java-sdk/exhaustive/custom-license/README.md index 7d9a01d785e5..e25d271b83e1 100644 --- a/seed/java-sdk/exhaustive/custom-license/README.md +++ b/seed/java-sdk/exhaustive/custom-license/README.md @@ -146,7 +146,6 @@ SeedExhaustiveClient client = SeedExhaustiveClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.exhaustive.SeedExhaustiveClient; import com.seed.exhaustive.core.RequestOptions; @@ -154,7 +153,7 @@ import com.seed.exhaustive.core.RequestOptions; // Client level SeedExhaustiveClient client = SeedExhaustiveClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -162,7 +161,7 @@ client.endpoints().container().getAndReturnListOfPrimitives( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/exhaustive/enable-public-constructors/README.md b/seed/java-sdk/exhaustive/enable-public-constructors/README.md index 7d9a01d785e5..e25d271b83e1 100644 --- a/seed/java-sdk/exhaustive/enable-public-constructors/README.md +++ b/seed/java-sdk/exhaustive/enable-public-constructors/README.md @@ -146,7 +146,6 @@ SeedExhaustiveClient client = SeedExhaustiveClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.exhaustive.SeedExhaustiveClient; import com.seed.exhaustive.core.RequestOptions; @@ -154,7 +153,7 @@ import com.seed.exhaustive.core.RequestOptions; // Client level SeedExhaustiveClient client = SeedExhaustiveClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -162,7 +161,7 @@ client.endpoints().container().getAndReturnListOfPrimitives( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/exhaustive/flat-package-layout/README.md b/seed/java-sdk/exhaustive/flat-package-layout/README.md index 7d9a01d785e5..e25d271b83e1 100644 --- a/seed/java-sdk/exhaustive/flat-package-layout/README.md +++ b/seed/java-sdk/exhaustive/flat-package-layout/README.md @@ -146,7 +146,6 @@ SeedExhaustiveClient client = SeedExhaustiveClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.exhaustive.SeedExhaustiveClient; import com.seed.exhaustive.core.RequestOptions; @@ -154,7 +153,7 @@ import com.seed.exhaustive.core.RequestOptions; // Client level SeedExhaustiveClient client = SeedExhaustiveClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -162,7 +161,7 @@ client.endpoints().container().getAndReturnListOfPrimitives( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/exhaustive/forward-compatible-enums/README.md b/seed/java-sdk/exhaustive/forward-compatible-enums/README.md index 7d9a01d785e5..e25d271b83e1 100644 --- a/seed/java-sdk/exhaustive/forward-compatible-enums/README.md +++ b/seed/java-sdk/exhaustive/forward-compatible-enums/README.md @@ -146,7 +146,6 @@ SeedExhaustiveClient client = SeedExhaustiveClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.exhaustive.SeedExhaustiveClient; import com.seed.exhaustive.core.RequestOptions; @@ -154,7 +153,7 @@ import com.seed.exhaustive.core.RequestOptions; // Client level SeedExhaustiveClient client = SeedExhaustiveClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -162,7 +161,7 @@ client.endpoints().container().getAndReturnListOfPrimitives( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/exhaustive/json-include-non-empty/README.md b/seed/java-sdk/exhaustive/json-include-non-empty/README.md index 7d9a01d785e5..e25d271b83e1 100644 --- a/seed/java-sdk/exhaustive/json-include-non-empty/README.md +++ b/seed/java-sdk/exhaustive/json-include-non-empty/README.md @@ -146,7 +146,6 @@ SeedExhaustiveClient client = SeedExhaustiveClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.exhaustive.SeedExhaustiveClient; import com.seed.exhaustive.core.RequestOptions; @@ -154,7 +153,7 @@ import com.seed.exhaustive.core.RequestOptions; // Client level SeedExhaustiveClient client = SeedExhaustiveClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -162,7 +161,7 @@ client.endpoints().container().getAndReturnListOfPrimitives( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/exhaustive/local-files/README.md b/seed/java-sdk/exhaustive/local-files/README.md index 4181957e9641..0600627a9bad 100644 --- a/seed/java-sdk/exhaustive/local-files/README.md +++ b/seed/java-sdk/exhaustive/local-files/README.md @@ -120,7 +120,6 @@ SeedExhaustiveClient client = SeedExhaustiveClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.fern.sdk.SeedExhaustiveClient; import com.fern.sdk.core.RequestOptions; @@ -128,7 +127,7 @@ import com.fern.sdk.core.RequestOptions; // Client level SeedExhaustiveClient client = SeedExhaustiveClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -136,7 +135,7 @@ client.endpoints().container().getAndReturnListOfPrimitives( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/exhaustive/no-custom-config/README.md b/seed/java-sdk/exhaustive/no-custom-config/README.md index 7d9a01d785e5..e25d271b83e1 100644 --- a/seed/java-sdk/exhaustive/no-custom-config/README.md +++ b/seed/java-sdk/exhaustive/no-custom-config/README.md @@ -146,7 +146,6 @@ SeedExhaustiveClient client = SeedExhaustiveClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.exhaustive.SeedExhaustiveClient; import com.seed.exhaustive.core.RequestOptions; @@ -154,7 +153,7 @@ import com.seed.exhaustive.core.RequestOptions; // Client level SeedExhaustiveClient client = SeedExhaustiveClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -162,7 +161,7 @@ client.endpoints().container().getAndReturnListOfPrimitives( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/exhaustive/publish-to/README.md b/seed/java-sdk/exhaustive/publish-to/README.md index 7d9a01d785e5..e25d271b83e1 100644 --- a/seed/java-sdk/exhaustive/publish-to/README.md +++ b/seed/java-sdk/exhaustive/publish-to/README.md @@ -146,7 +146,6 @@ SeedExhaustiveClient client = SeedExhaustiveClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.exhaustive.SeedExhaustiveClient; import com.seed.exhaustive.core.RequestOptions; @@ -154,7 +153,7 @@ import com.seed.exhaustive.core.RequestOptions; // Client level SeedExhaustiveClient client = SeedExhaustiveClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -162,7 +161,7 @@ client.endpoints().container().getAndReturnListOfPrimitives( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/exhaustive/signed_publish/README.md b/seed/java-sdk/exhaustive/signed_publish/README.md index 7d9a01d785e5..e25d271b83e1 100644 --- a/seed/java-sdk/exhaustive/signed_publish/README.md +++ b/seed/java-sdk/exhaustive/signed_publish/README.md @@ -146,7 +146,6 @@ SeedExhaustiveClient client = SeedExhaustiveClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.exhaustive.SeedExhaustiveClient; import com.seed.exhaustive.core.RequestOptions; @@ -154,7 +153,7 @@ import com.seed.exhaustive.core.RequestOptions; // Client level SeedExhaustiveClient client = SeedExhaustiveClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -162,7 +161,7 @@ client.endpoints().container().getAndReturnListOfPrimitives( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/extends/README.md b/seed/java-sdk/extends/README.md index 90d7f4c2c6e2..5d963a0edfc5 100644 --- a/seed/java-sdk/extends/README.md +++ b/seed/java-sdk/extends/README.md @@ -150,7 +150,6 @@ SeedExtendsClient client = SeedExtendsClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.extends.SeedExtendsClient; import com.seed.extends.core.RequestOptions; @@ -158,7 +157,7 @@ import com.seed.extends.core.RequestOptions; // Client level SeedExtendsClient client = SeedExtendsClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -166,7 +165,7 @@ client.extendedInlineRequestBody( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/extra-properties/README.md b/seed/java-sdk/extra-properties/README.md index fa4dc3059a48..189a906c2fc2 100644 --- a/seed/java-sdk/extra-properties/README.md +++ b/seed/java-sdk/extra-properties/README.md @@ -148,7 +148,6 @@ SeedExtraPropertiesClient client = SeedExtraPropertiesClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.extraProperties.SeedExtraPropertiesClient; import com.seed.extraProperties.core.RequestOptions; @@ -156,7 +155,7 @@ import com.seed.extraProperties.core.RequestOptions; // Client level SeedExtraPropertiesClient client = SeedExtraPropertiesClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -164,7 +163,7 @@ client.user().createUser( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/file-download/README.md b/seed/java-sdk/file-download/README.md index ffebdfcc270a..703a4e87b5b5 100644 --- a/seed/java-sdk/file-download/README.md +++ b/seed/java-sdk/file-download/README.md @@ -142,7 +142,6 @@ SeedFileDownloadClient client = SeedFileDownloadClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.fileDownload.SeedFileDownloadClient; import com.seed.fileDownload.core.RequestOptions; @@ -150,7 +149,7 @@ import com.seed.fileDownload.core.RequestOptions; // Client level SeedFileDownloadClient client = SeedFileDownloadClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -158,7 +157,7 @@ client.service().simple( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/file-upload-openapi/README.md b/seed/java-sdk/file-upload-openapi/README.md index c82f3750e6f5..ebee7cc46f7a 100644 --- a/seed/java-sdk/file-upload-openapi/README.md +++ b/seed/java-sdk/file-upload-openapi/README.md @@ -148,7 +148,6 @@ SeedApiClient client = SeedApiClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.api.SeedApiClient; import com.seed.api.core.RequestOptions; @@ -156,7 +155,7 @@ import com.seed.api.core.RequestOptions; // Client level SeedApiClient client = SeedApiClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -164,7 +163,7 @@ client.fileUploadExample().uploadFile( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/file-upload/inline-file-properties/README.md b/seed/java-sdk/file-upload/inline-file-properties/README.md index 064854225496..54348474d699 100644 --- a/seed/java-sdk/file-upload/inline-file-properties/README.md +++ b/seed/java-sdk/file-upload/inline-file-properties/README.md @@ -147,7 +147,6 @@ SeedFileUploadClient client = SeedFileUploadClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.fileUpload.SeedFileUploadClient; import com.seed.fileUpload.core.RequestOptions; @@ -155,7 +154,7 @@ import com.seed.fileUpload.core.RequestOptions; // Client level SeedFileUploadClient client = SeedFileUploadClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -163,7 +162,7 @@ client.service().justFile( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/file-upload/no-custom-config/README.md b/seed/java-sdk/file-upload/no-custom-config/README.md index 064854225496..54348474d699 100644 --- a/seed/java-sdk/file-upload/no-custom-config/README.md +++ b/seed/java-sdk/file-upload/no-custom-config/README.md @@ -147,7 +147,6 @@ SeedFileUploadClient client = SeedFileUploadClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.fileUpload.SeedFileUploadClient; import com.seed.fileUpload.core.RequestOptions; @@ -155,7 +154,7 @@ import com.seed.fileUpload.core.RequestOptions; // Client level SeedFileUploadClient client = SeedFileUploadClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -163,7 +162,7 @@ client.service().justFile( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/file-upload/wrapped-aliases/README.md b/seed/java-sdk/file-upload/wrapped-aliases/README.md index 064854225496..54348474d699 100644 --- a/seed/java-sdk/file-upload/wrapped-aliases/README.md +++ b/seed/java-sdk/file-upload/wrapped-aliases/README.md @@ -147,7 +147,6 @@ SeedFileUploadClient client = SeedFileUploadClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.fileUpload.SeedFileUploadClient; import com.seed.fileUpload.core.RequestOptions; @@ -155,7 +154,7 @@ import com.seed.fileUpload.core.RequestOptions; // Client level SeedFileUploadClient client = SeedFileUploadClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -163,7 +162,7 @@ client.service().justFile( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/folders/README.md b/seed/java-sdk/folders/README.md index 68c34a0f9680..f1692c875c29 100644 --- a/seed/java-sdk/folders/README.md +++ b/seed/java-sdk/folders/README.md @@ -142,7 +142,6 @@ SeedApiClient client = SeedApiClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.api.SeedApiClient; import com.seed.api.core.RequestOptions; @@ -150,7 +149,7 @@ import com.seed.api.core.RequestOptions; // Client level SeedApiClient client = SeedApiClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -158,7 +157,7 @@ client.foo( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/header-auth-environment-variable/README.md b/seed/java-sdk/header-auth-environment-variable/README.md index c799fa95eb43..0c536d18b092 100644 --- a/seed/java-sdk/header-auth-environment-variable/README.md +++ b/seed/java-sdk/header-auth-environment-variable/README.md @@ -143,7 +143,6 @@ SeedHeaderTokenEnvironmentVariableClient client = SeedHeaderTokenEnvironmentVari ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.headerTokenEnvironmentVariable.SeedHeaderTokenEnvironmentVariableClient; import com.seed.headerTokenEnvironmentVariable.core.RequestOptions; @@ -151,7 +150,7 @@ import com.seed.headerTokenEnvironmentVariable.core.RequestOptions; // Client level SeedHeaderTokenEnvironmentVariableClient client = SeedHeaderTokenEnvironmentVariableClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -159,7 +158,7 @@ client.service().getWithBearerToken( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/header-auth/header-auth/README.md b/seed/java-sdk/header-auth/header-auth/README.md index 3744b21df919..c94124d5a4e4 100644 --- a/seed/java-sdk/header-auth/header-auth/README.md +++ b/seed/java-sdk/header-auth/header-auth/README.md @@ -143,7 +143,6 @@ SeedHeaderTokenClient client = SeedHeaderTokenClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.headerToken.SeedHeaderTokenClient; import com.seed.headerToken.core.RequestOptions; @@ -151,7 +150,7 @@ import com.seed.headerToken.core.RequestOptions; // Client level SeedHeaderTokenClient client = SeedHeaderTokenClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -159,7 +158,7 @@ client.service().getWithBearerToken( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/http-head/README.md b/seed/java-sdk/http-head/README.md index dffbcfb1c8c3..8cd3aedcecf9 100644 --- a/seed/java-sdk/http-head/README.md +++ b/seed/java-sdk/http-head/README.md @@ -142,7 +142,6 @@ SeedHttpHeadClient client = SeedHttpHeadClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.httpHead.SeedHttpHeadClient; import com.seed.httpHead.core.RequestOptions; @@ -150,7 +149,7 @@ import com.seed.httpHead.core.RequestOptions; // Client level SeedHttpHeadClient client = SeedHttpHeadClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -158,7 +157,7 @@ client.user().head( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/idempotency-headers/README.md b/seed/java-sdk/idempotency-headers/README.md index 74f333d20642..189ca0f0ca99 100644 --- a/seed/java-sdk/idempotency-headers/README.md +++ b/seed/java-sdk/idempotency-headers/README.md @@ -151,7 +151,6 @@ SeedIdempotencyHeadersClient client = SeedIdempotencyHeadersClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.idempotencyHeaders.SeedIdempotencyHeadersClient; import com.seed.idempotencyHeaders.core.RequestOptions; @@ -159,7 +158,7 @@ import com.seed.idempotencyHeaders.core.RequestOptions; // Client level SeedIdempotencyHeadersClient client = SeedIdempotencyHeadersClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -167,7 +166,7 @@ client.payment().create( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/imdb/disable-required-property-builder-checks/README.md b/seed/java-sdk/imdb/disable-required-property-builder-checks/README.md index d4bcbf1109c1..a993a3f6d9c7 100644 --- a/seed/java-sdk/imdb/disable-required-property-builder-checks/README.md +++ b/seed/java-sdk/imdb/disable-required-property-builder-checks/README.md @@ -150,7 +150,6 @@ SeedApiClient client = SeedApiClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.api.SeedApiClient; import com.seed.api.core.RequestOptions; @@ -158,7 +157,7 @@ import com.seed.api.core.RequestOptions; // Client level SeedApiClient client = SeedApiClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -166,7 +165,7 @@ client.imdb().createMovie( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/imdb/flat-package-layout/README.md b/seed/java-sdk/imdb/flat-package-layout/README.md index eada6dde6341..5e70ee097770 100644 --- a/seed/java-sdk/imdb/flat-package-layout/README.md +++ b/seed/java-sdk/imdb/flat-package-layout/README.md @@ -150,7 +150,6 @@ SeedApiClient client = SeedApiClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.api.SeedApiClient; import com.seed.api.core.RequestOptions; @@ -158,7 +157,7 @@ import com.seed.api.core.RequestOptions; // Client level SeedApiClient client = SeedApiClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -166,7 +165,7 @@ client.imdb().createMovie( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/inferred-auth-explicit/README.md b/seed/java-sdk/inferred-auth-explicit/README.md index a81403704ebc..ee1ad6f408cf 100644 --- a/seed/java-sdk/inferred-auth-explicit/README.md +++ b/seed/java-sdk/inferred-auth-explicit/README.md @@ -151,7 +151,6 @@ SeedInferredAuthExplicitClient client = SeedInferredAuthExplicitClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.inferredAuthExplicit.SeedInferredAuthExplicitClient; import com.seed.inferredAuthExplicit.core.RequestOptions; @@ -159,7 +158,7 @@ import com.seed.inferredAuthExplicit.core.RequestOptions; // Client level SeedInferredAuthExplicitClient client = SeedInferredAuthExplicitClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -167,7 +166,7 @@ client.auth().getTokenWithClientCredentials( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/inferred-auth-implicit-no-expiry/README.md b/seed/java-sdk/inferred-auth-implicit-no-expiry/README.md index 23dc95089e6b..815f337b1dfd 100644 --- a/seed/java-sdk/inferred-auth-implicit-no-expiry/README.md +++ b/seed/java-sdk/inferred-auth-implicit-no-expiry/README.md @@ -151,7 +151,6 @@ SeedInferredAuthImplicitNoExpiryClient client = SeedInferredAuthImplicitNoExpiry ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.inferredAuthImplicitNoExpiry.SeedInferredAuthImplicitNoExpiryClient; import com.seed.inferredAuthImplicitNoExpiry.core.RequestOptions; @@ -159,7 +158,7 @@ import com.seed.inferredAuthImplicitNoExpiry.core.RequestOptions; // Client level SeedInferredAuthImplicitNoExpiryClient client = SeedInferredAuthImplicitNoExpiryClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -167,7 +166,7 @@ client.auth().getTokenWithClientCredentials( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/inferred-auth-implicit/README.md b/seed/java-sdk/inferred-auth-implicit/README.md index 995856683df3..39aa9a0c0450 100644 --- a/seed/java-sdk/inferred-auth-implicit/README.md +++ b/seed/java-sdk/inferred-auth-implicit/README.md @@ -151,7 +151,6 @@ SeedInferredAuthImplicitClient client = SeedInferredAuthImplicitClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.inferredAuthImplicit.SeedInferredAuthImplicitClient; import com.seed.inferredAuthImplicit.core.RequestOptions; @@ -159,7 +158,7 @@ import com.seed.inferredAuthImplicit.core.RequestOptions; // Client level SeedInferredAuthImplicitClient client = SeedInferredAuthImplicitClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -167,7 +166,7 @@ client.auth().getTokenWithClientCredentials( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/java-builder-extension/base-client/README.md b/seed/java-sdk/java-builder-extension/base-client/README.md index a54b9a01abf5..7d9cd0f3e279 100644 --- a/seed/java-sdk/java-builder-extension/base-client/README.md +++ b/seed/java-sdk/java-builder-extension/base-client/README.md @@ -158,7 +158,6 @@ BaseClient client = BaseClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.builderExtension.BaseClient; import com.seed.builderExtension.core.RequestOptions; @@ -166,7 +165,7 @@ import com.seed.builderExtension.core.RequestOptions; // Client level BaseClient client = BaseClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -174,7 +173,7 @@ client.service().hello( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/java-builder-extension/extensible-builders/README.md b/seed/java-sdk/java-builder-extension/extensible-builders/README.md index a54b9a01abf5..7d9cd0f3e279 100644 --- a/seed/java-sdk/java-builder-extension/extensible-builders/README.md +++ b/seed/java-sdk/java-builder-extension/extensible-builders/README.md @@ -158,7 +158,6 @@ BaseClient client = BaseClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.builderExtension.BaseClient; import com.seed.builderExtension.core.RequestOptions; @@ -166,7 +165,7 @@ import com.seed.builderExtension.core.RequestOptions; // Client level BaseClient client = BaseClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -174,7 +173,7 @@ client.service().hello( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/java-custom-package-prefix/java-custom-package-prefix/README.md b/seed/java-sdk/java-custom-package-prefix/java-custom-package-prefix/README.md index b696454c55f3..a569c7fc8e9a 100644 --- a/seed/java-sdk/java-custom-package-prefix/java-custom-package-prefix/README.md +++ b/seed/java-sdk/java-custom-package-prefix/java-custom-package-prefix/README.md @@ -150,7 +150,6 @@ SeedApiClient client = SeedApiClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.customprefix.SeedApiClient; import com.customprefix.core.RequestOptions; @@ -158,7 +157,7 @@ import com.customprefix.core.RequestOptions; // Client level SeedApiClient client = SeedApiClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -166,7 +165,7 @@ client.imdb().createMovie( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/java-inline-types/enable-forward-compatible-enums/README.md b/seed/java-sdk/java-inline-types/enable-forward-compatible-enums/README.md index 1651cd6b9285..6b5ff6a25d08 100644 --- a/seed/java-sdk/java-inline-types/enable-forward-compatible-enums/README.md +++ b/seed/java-sdk/java-inline-types/enable-forward-compatible-enums/README.md @@ -155,7 +155,6 @@ SeedObjectClient client = SeedObjectClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.object.SeedObjectClient; import com.seed.object.core.RequestOptions; @@ -163,7 +162,7 @@ import com.seed.object.core.RequestOptions; // Client level SeedObjectClient client = SeedObjectClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -171,7 +170,7 @@ client.getRoot( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/java-inline-types/inline/README.md b/seed/java-sdk/java-inline-types/inline/README.md index 1651cd6b9285..6b5ff6a25d08 100644 --- a/seed/java-sdk/java-inline-types/inline/README.md +++ b/seed/java-sdk/java-inline-types/inline/README.md @@ -155,7 +155,6 @@ SeedObjectClient client = SeedObjectClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.object.SeedObjectClient; import com.seed.object.core.RequestOptions; @@ -163,7 +162,7 @@ import com.seed.object.core.RequestOptions; // Client level SeedObjectClient client = SeedObjectClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -171,7 +170,7 @@ client.getRoot( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/java-inline-types/no-inline/README.md b/seed/java-sdk/java-inline-types/no-inline/README.md index 1651cd6b9285..6b5ff6a25d08 100644 --- a/seed/java-sdk/java-inline-types/no-inline/README.md +++ b/seed/java-sdk/java-inline-types/no-inline/README.md @@ -155,7 +155,6 @@ SeedObjectClient client = SeedObjectClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.object.SeedObjectClient; import com.seed.object.core.RequestOptions; @@ -163,7 +162,7 @@ import com.seed.object.core.RequestOptions; // Client level SeedObjectClient client = SeedObjectClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -171,7 +170,7 @@ client.getRoot( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/java-inline-types/no-wrapped-aliases/README.md b/seed/java-sdk/java-inline-types/no-wrapped-aliases/README.md index 1651cd6b9285..6b5ff6a25d08 100644 --- a/seed/java-sdk/java-inline-types/no-wrapped-aliases/README.md +++ b/seed/java-sdk/java-inline-types/no-wrapped-aliases/README.md @@ -155,7 +155,6 @@ SeedObjectClient client = SeedObjectClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.object.SeedObjectClient; import com.seed.object.core.RequestOptions; @@ -163,7 +162,7 @@ import com.seed.object.core.RequestOptions; // Client level SeedObjectClient client = SeedObjectClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -171,7 +170,7 @@ client.getRoot( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/java-nullable-named-request-types/custom-config/README.md b/seed/java-sdk/java-nullable-named-request-types/custom-config/README.md index 6d6501d1d2ad..0a68eb1dbeb7 100644 --- a/seed/java-sdk/java-nullable-named-request-types/custom-config/README.md +++ b/seed/java-sdk/java-nullable-named-request-types/custom-config/README.md @@ -157,7 +157,6 @@ SeedApiClient client = SeedApiClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.api.SeedApiClient; import com.seed.api.core.RequestOptions; @@ -165,7 +164,7 @@ import com.seed.api.core.RequestOptions; // Client level SeedApiClient client = SeedApiClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -173,7 +172,7 @@ client.postWithNullableNamedRequestBodyType( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/java-pagination-deep-cursor-path/java-pagination-deep-cursor-path/README.md b/seed/java-sdk/java-pagination-deep-cursor-path/java-pagination-deep-cursor-path/README.md index f69729e2aaa2..49f8756b723d 100644 --- a/seed/java-sdk/java-pagination-deep-cursor-path/java-pagination-deep-cursor-path/README.md +++ b/seed/java-sdk/java-pagination-deep-cursor-path/java-pagination-deep-cursor-path/README.md @@ -206,7 +206,6 @@ SeedDeepCursorPathClient client = SeedDeepCursorPathClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.deepCursorPath.SeedDeepCursorPathClient; import com.seed.deepCursorPath.core.RequestOptions; @@ -214,7 +213,7 @@ import com.seed.deepCursorPath.core.RequestOptions; // Client level SeedDeepCursorPathClient client = SeedDeepCursorPathClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -222,7 +221,7 @@ client.deepCursorPath().doThing( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/java-pagination-deep-cursor-path/wire-tests/README.md b/seed/java-sdk/java-pagination-deep-cursor-path/wire-tests/README.md index f69729e2aaa2..49f8756b723d 100644 --- a/seed/java-sdk/java-pagination-deep-cursor-path/wire-tests/README.md +++ b/seed/java-sdk/java-pagination-deep-cursor-path/wire-tests/README.md @@ -206,7 +206,6 @@ SeedDeepCursorPathClient client = SeedDeepCursorPathClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.deepCursorPath.SeedDeepCursorPathClient; import com.seed.deepCursorPath.core.RequestOptions; @@ -214,7 +213,7 @@ import com.seed.deepCursorPath.core.RequestOptions; // Client level SeedDeepCursorPathClient client = SeedDeepCursorPathClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -222,7 +221,7 @@ client.deepCursorPath().doThing( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/java-single-property-endpoint/java-single-property-endpoint/README.md b/seed/java-sdk/java-single-property-endpoint/java-single-property-endpoint/README.md index 0a9b8adfbc09..b4471a4f2274 100644 --- a/seed/java-sdk/java-single-property-endpoint/java-single-property-endpoint/README.md +++ b/seed/java-sdk/java-single-property-endpoint/java-single-property-endpoint/README.md @@ -149,7 +149,6 @@ SeedSinglePropertyClient client = SeedSinglePropertyClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.singleProperty.SeedSinglePropertyClient; import com.seed.singleProperty.core.RequestOptions; @@ -157,7 +156,7 @@ import com.seed.singleProperty.core.RequestOptions; // Client level SeedSinglePropertyClient client = SeedSinglePropertyClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -165,7 +164,7 @@ client.singleProperty().doThing( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/license/README.md b/seed/java-sdk/license/README.md index e335cef5ebf9..20fec9c600c2 100644 --- a/seed/java-sdk/license/README.md +++ b/seed/java-sdk/license/README.md @@ -142,7 +142,6 @@ SeedLicenseClient client = SeedLicenseClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.license.SeedLicenseClient; import com.seed.license.core.RequestOptions; @@ -150,7 +149,7 @@ import com.seed.license.core.RequestOptions; // Client level SeedLicenseClient client = SeedLicenseClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -158,7 +157,7 @@ client.get( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/literal/README.md b/seed/java-sdk/literal/README.md index e07e14102171..f1cbb7635ca3 100644 --- a/seed/java-sdk/literal/README.md +++ b/seed/java-sdk/literal/README.md @@ -148,7 +148,6 @@ SeedLiteralClient client = SeedLiteralClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.literal.SeedLiteralClient; import com.seed.literal.core.RequestOptions; @@ -156,7 +155,7 @@ import com.seed.literal.core.RequestOptions; // Client level SeedLiteralClient client = SeedLiteralClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -164,7 +163,7 @@ client.headers().send( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/mixed-case/README.md b/seed/java-sdk/mixed-case/README.md index 273a0ece3d9a..823a16fa5eb7 100644 --- a/seed/java-sdk/mixed-case/README.md +++ b/seed/java-sdk/mixed-case/README.md @@ -142,7 +142,6 @@ SeedMixedCaseClient client = SeedMixedCaseClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.mixedCase.SeedMixedCaseClient; import com.seed.mixedCase.core.RequestOptions; @@ -150,7 +149,7 @@ import com.seed.mixedCase.core.RequestOptions; // Client level SeedMixedCaseClient client = SeedMixedCaseClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -158,7 +157,7 @@ client.service().getResource( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/mixed-file-directory/README.md b/seed/java-sdk/mixed-file-directory/README.md index 9f2a2406bed9..975ad854d5e6 100644 --- a/seed/java-sdk/mixed-file-directory/README.md +++ b/seed/java-sdk/mixed-file-directory/README.md @@ -148,7 +148,6 @@ SeedMixedFileDirectoryClient client = SeedMixedFileDirectoryClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.mixedFileDirectory.SeedMixedFileDirectoryClient; import com.seed.mixedFileDirectory.core.RequestOptions; @@ -156,7 +155,7 @@ import com.seed.mixedFileDirectory.core.RequestOptions; // Client level SeedMixedFileDirectoryClient client = SeedMixedFileDirectoryClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -164,7 +163,7 @@ client.organization().create( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/multi-line-docs/README.md b/seed/java-sdk/multi-line-docs/README.md index 85aa6994f165..463e8a837fd0 100644 --- a/seed/java-sdk/multi-line-docs/README.md +++ b/seed/java-sdk/multi-line-docs/README.md @@ -149,7 +149,6 @@ SeedMultiLineDocsClient client = SeedMultiLineDocsClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.multiLineDocs.SeedMultiLineDocsClient; import com.seed.multiLineDocs.core.RequestOptions; @@ -157,7 +156,7 @@ import com.seed.multiLineDocs.core.RequestOptions; // Client level SeedMultiLineDocsClient client = SeedMultiLineDocsClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -165,7 +164,7 @@ client.user().createUser( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/multi-url-environment-no-default/README.md b/seed/java-sdk/multi-url-environment-no-default/README.md index 2cdc406e25ef..09c808dfb825 100644 --- a/seed/java-sdk/multi-url-environment-no-default/README.md +++ b/seed/java-sdk/multi-url-environment-no-default/README.md @@ -164,7 +164,6 @@ SeedMultiUrlEnvironmentNoDefaultClient client = SeedMultiUrlEnvironmentNoDefault ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.multiUrlEnvironmentNoDefault.SeedMultiUrlEnvironmentNoDefaultClient; import com.seed.multiUrlEnvironmentNoDefault.core.RequestOptions; @@ -172,7 +171,7 @@ import com.seed.multiUrlEnvironmentNoDefault.core.RequestOptions; // Client level SeedMultiUrlEnvironmentNoDefaultClient client = SeedMultiUrlEnvironmentNoDefaultClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -180,7 +179,7 @@ client.ec2().bootInstance( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/multi-url-environment/README.md b/seed/java-sdk/multi-url-environment/README.md index 8ddfc2cdee9b..c5c020da929c 100644 --- a/seed/java-sdk/multi-url-environment/README.md +++ b/seed/java-sdk/multi-url-environment/README.md @@ -164,7 +164,6 @@ SeedMultiUrlEnvironmentClient client = SeedMultiUrlEnvironmentClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.multiUrlEnvironment.SeedMultiUrlEnvironmentClient; import com.seed.multiUrlEnvironment.core.RequestOptions; @@ -172,7 +171,7 @@ import com.seed.multiUrlEnvironment.core.RequestOptions; // Client level SeedMultiUrlEnvironmentClient client = SeedMultiUrlEnvironmentClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -180,7 +179,7 @@ client.ec2().bootInstance( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/multiple-request-bodies/README.md b/seed/java-sdk/multiple-request-bodies/README.md index baa347572680..7c68d19d2e51 100644 --- a/seed/java-sdk/multiple-request-bodies/README.md +++ b/seed/java-sdk/multiple-request-bodies/README.md @@ -172,7 +172,6 @@ SeedApiClient client = SeedApiClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.api.SeedApiClient; import com.seed.api.core.RequestOptions; @@ -180,7 +179,7 @@ import com.seed.api.core.RequestOptions; // Client level SeedApiClient client = SeedApiClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -188,7 +187,7 @@ client.uploadJsonDocument( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/no-environment/README.md b/seed/java-sdk/no-environment/README.md index faef4cfb68cb..fec7f4fa2415 100644 --- a/seed/java-sdk/no-environment/README.md +++ b/seed/java-sdk/no-environment/README.md @@ -143,7 +143,6 @@ SeedNoEnvironmentClient client = SeedNoEnvironmentClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.noEnvironment.SeedNoEnvironmentClient; import com.seed.noEnvironment.core.RequestOptions; @@ -151,7 +150,7 @@ import com.seed.noEnvironment.core.RequestOptions; // Client level SeedNoEnvironmentClient client = SeedNoEnvironmentClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -159,7 +158,7 @@ client.dummy().getDummy( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/nullable-optional/collapse-optional-nullable/README.md b/seed/java-sdk/nullable-optional/collapse-optional-nullable/README.md index a8bb6946e277..0c43c6928660 100644 --- a/seed/java-sdk/nullable-optional/collapse-optional-nullable/README.md +++ b/seed/java-sdk/nullable-optional/collapse-optional-nullable/README.md @@ -202,7 +202,6 @@ SeedNullableOptionalClient client = SeedNullableOptionalClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.nullableOptional.SeedNullableOptionalClient; import com.seed.nullableOptional.core.RequestOptions; @@ -210,7 +209,7 @@ import com.seed.nullableOptional.core.RequestOptions; // Client level SeedNullableOptionalClient client = SeedNullableOptionalClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -218,7 +217,7 @@ client.nullableOptional().createUser( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/nullable-optional/collapse-optional-nullable/src/main/java/com/seed/nullableOptional/resources/nullableoptional/AsyncRawNullableOptionalClient.java b/seed/java-sdk/nullable-optional/collapse-optional-nullable/src/main/java/com/seed/nullableOptional/resources/nullableoptional/AsyncRawNullableOptionalClient.java index 91ba02a019e3..0d5e0cfb092d 100644 --- a/seed/java-sdk/nullable-optional/collapse-optional-nullable/src/main/java/com/seed/nullableOptional/resources/nullableoptional/AsyncRawNullableOptionalClient.java +++ b/seed/java-sdk/nullable-optional/collapse-optional-nullable/src/main/java/com/seed/nullableOptional/resources/nullableoptional/AsyncRawNullableOptionalClient.java @@ -281,7 +281,10 @@ public CompletableFuture>> l QueryStringMapper.addQueryParameter( httpUrl, "includeDeleted", request.getIncludeDeleted().get(), false); } - QueryStringMapper.addQueryParameter(httpUrl, "sortBy", request.getSortBy(), false); + if (!request.getSortBy().isAbsent()) { + QueryStringMapper.addQueryParameter( + httpUrl, "sortBy", request.getSortBy().orElse(null), false); + } Request.Builder _requestBuilder = new Request.Builder() .url(httpUrl.build()) .method("GET", null) @@ -342,12 +345,18 @@ public CompletableFuture>> s .addPathSegments("api") .addPathSegments("users/search"); QueryStringMapper.addQueryParameter(httpUrl, "query", request.getQuery(), false); - QueryStringMapper.addQueryParameter(httpUrl, "department", request.getDepartment(), false); + if (!request.getDepartment().isAbsent()) { + QueryStringMapper.addQueryParameter( + httpUrl, "department", request.getDepartment().orElse(null), false); + } if (request.getRole().isPresent()) { QueryStringMapper.addQueryParameter( httpUrl, "role", request.getRole().get(), false); } - QueryStringMapper.addQueryParameter(httpUrl, "isActive", request.getIsActive(), false); + if (!request.getIsActive().isAbsent()) { + QueryStringMapper.addQueryParameter( + httpUrl, "isActive", request.getIsActive().orElse(null), false); + } Request.Builder _requestBuilder = new Request.Builder() .url(httpUrl.build()) .method("GET", null) @@ -678,12 +687,18 @@ public CompletableFuture>> f .newBuilder() .addPathSegments("api") .addPathSegments("users/filter"); - QueryStringMapper.addQueryParameter(httpUrl, "role", request.getRole(), false); + if (!request.getRole().isAbsent()) { + QueryStringMapper.addQueryParameter( + httpUrl, "role", request.getRole().orElse(null), false); + } if (request.getStatus().isPresent()) { QueryStringMapper.addQueryParameter( httpUrl, "status", request.getStatus().get(), false); } - QueryStringMapper.addQueryParameter(httpUrl, "secondaryRole", request.getSecondaryRole(), false); + if (!request.getSecondaryRole().isAbsent()) { + QueryStringMapper.addQueryParameter( + httpUrl, "secondaryRole", request.getSecondaryRole().orElse(null), false); + } Request.Builder _requestBuilder = new Request.Builder() .url(httpUrl.build()) .method("GET", null) diff --git a/seed/java-sdk/nullable-optional/collapse-optional-nullable/src/main/java/com/seed/nullableOptional/resources/nullableoptional/RawNullableOptionalClient.java b/seed/java-sdk/nullable-optional/collapse-optional-nullable/src/main/java/com/seed/nullableOptional/resources/nullableoptional/RawNullableOptionalClient.java index 50738bee1f4b..f9c616dbfe4c 100644 --- a/seed/java-sdk/nullable-optional/collapse-optional-nullable/src/main/java/com/seed/nullableOptional/resources/nullableoptional/RawNullableOptionalClient.java +++ b/seed/java-sdk/nullable-optional/collapse-optional-nullable/src/main/java/com/seed/nullableOptional/resources/nullableoptional/RawNullableOptionalClient.java @@ -230,7 +230,10 @@ public SeedNullableOptionalHttpResponse> listUsers( QueryStringMapper.addQueryParameter( httpUrl, "includeDeleted", request.getIncludeDeleted().get(), false); } - QueryStringMapper.addQueryParameter(httpUrl, "sortBy", request.getSortBy(), false); + if (!request.getSortBy().isAbsent()) { + QueryStringMapper.addQueryParameter( + httpUrl, "sortBy", request.getSortBy().orElse(null), false); + } Request.Builder _requestBuilder = new Request.Builder() .url(httpUrl.build()) .method("GET", null) @@ -275,12 +278,18 @@ public SeedNullableOptionalHttpResponse> searchUsers( .addPathSegments("api") .addPathSegments("users/search"); QueryStringMapper.addQueryParameter(httpUrl, "query", request.getQuery(), false); - QueryStringMapper.addQueryParameter(httpUrl, "department", request.getDepartment(), false); + if (!request.getDepartment().isAbsent()) { + QueryStringMapper.addQueryParameter( + httpUrl, "department", request.getDepartment().orElse(null), false); + } if (request.getRole().isPresent()) { QueryStringMapper.addQueryParameter( httpUrl, "role", request.getRole().get(), false); } - QueryStringMapper.addQueryParameter(httpUrl, "isActive", request.getIsActive(), false); + if (!request.getIsActive().isAbsent()) { + QueryStringMapper.addQueryParameter( + httpUrl, "isActive", request.getIsActive().orElse(null), false); + } Request.Builder _requestBuilder = new Request.Builder() .url(httpUrl.build()) .method("GET", null) @@ -529,12 +538,18 @@ public SeedNullableOptionalHttpResponse> filterByRole( .newBuilder() .addPathSegments("api") .addPathSegments("users/filter"); - QueryStringMapper.addQueryParameter(httpUrl, "role", request.getRole(), false); + if (!request.getRole().isAbsent()) { + QueryStringMapper.addQueryParameter( + httpUrl, "role", request.getRole().orElse(null), false); + } if (request.getStatus().isPresent()) { QueryStringMapper.addQueryParameter( httpUrl, "status", request.getStatus().get(), false); } - QueryStringMapper.addQueryParameter(httpUrl, "secondaryRole", request.getSecondaryRole(), false); + if (!request.getSecondaryRole().isAbsent()) { + QueryStringMapper.addQueryParameter( + httpUrl, "secondaryRole", request.getSecondaryRole().orElse(null), false); + } Request.Builder _requestBuilder = new Request.Builder() .url(httpUrl.build()) .method("GET", null) diff --git a/seed/java-sdk/nullable-optional/legacy/README.md b/seed/java-sdk/nullable-optional/legacy/README.md index e45486844eec..2a3846061338 100644 --- a/seed/java-sdk/nullable-optional/legacy/README.md +++ b/seed/java-sdk/nullable-optional/legacy/README.md @@ -163,7 +163,6 @@ SeedNullableOptionalClient client = SeedNullableOptionalClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.nullableOptional.SeedNullableOptionalClient; import com.seed.nullableOptional.core.RequestOptions; @@ -171,7 +170,7 @@ import com.seed.nullableOptional.core.RequestOptions; // Client level SeedNullableOptionalClient client = SeedNullableOptionalClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -179,7 +178,7 @@ client.nullableOptional().createUser( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/nullable-optional/with-nullable-annotation/README.md b/seed/java-sdk/nullable-optional/with-nullable-annotation/README.md index e45486844eec..2a3846061338 100644 --- a/seed/java-sdk/nullable-optional/with-nullable-annotation/README.md +++ b/seed/java-sdk/nullable-optional/with-nullable-annotation/README.md @@ -163,7 +163,6 @@ SeedNullableOptionalClient client = SeedNullableOptionalClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.nullableOptional.SeedNullableOptionalClient; import com.seed.nullableOptional.core.RequestOptions; @@ -171,7 +170,7 @@ import com.seed.nullableOptional.core.RequestOptions; // Client level SeedNullableOptionalClient client = SeedNullableOptionalClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -179,7 +178,7 @@ client.nullableOptional().createUser( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/nullable/no-custom-config/README.md b/seed/java-sdk/nullable/no-custom-config/README.md index 502cd9929e1f..142037df2662 100644 --- a/seed/java-sdk/nullable/no-custom-config/README.md +++ b/seed/java-sdk/nullable/no-custom-config/README.md @@ -177,7 +177,6 @@ SeedNullableClient client = SeedNullableClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.nullable.SeedNullableClient; import com.seed.nullable.core.RequestOptions; @@ -185,7 +184,7 @@ import com.seed.nullable.core.RequestOptions; // Client level SeedNullableClient client = SeedNullableClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -193,7 +192,7 @@ client.nullable().createUser( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/nullable/wrapped-aliases/README.md b/seed/java-sdk/nullable/wrapped-aliases/README.md index 502cd9929e1f..142037df2662 100644 --- a/seed/java-sdk/nullable/wrapped-aliases/README.md +++ b/seed/java-sdk/nullable/wrapped-aliases/README.md @@ -177,7 +177,6 @@ SeedNullableClient client = SeedNullableClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.nullable.SeedNullableClient; import com.seed.nullable.core.RequestOptions; @@ -185,7 +184,7 @@ import com.seed.nullable.core.RequestOptions; // Client level SeedNullableClient client = SeedNullableClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -193,7 +192,7 @@ client.nullable().createUser( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/oauth-client-credentials-custom/README.md b/seed/java-sdk/oauth-client-credentials-custom/README.md index 9cde7f6eb204..ffb62313a698 100644 --- a/seed/java-sdk/oauth-client-credentials-custom/README.md +++ b/seed/java-sdk/oauth-client-credentials-custom/README.md @@ -79,6 +79,29 @@ public class Example { } } ``` +## Authentication + +This SDK supports two authentication methods: + +### Option 1: Direct Bearer Token + +If you already have a valid access token, you can use it directly: + +```java +SeedOauthClientCredentialsClient client = SeedOauthClientCredentialsClient.withToken("your-access-token") + .url("https://api.example.com") + .build(); +``` + +### Option 2: OAuth Client Credentials + +The SDK can automatically handle token acquisition and refresh: + +```java +SeedOauthClientCredentialsClient client = SeedOauthClientCredentialsClient.withCredentials("client-id", "client-secret") + .url("https://api.example.com") + .build(); +``` ## Base Url @@ -154,7 +177,6 @@ SeedOauthClientCredentialsClient client = SeedOauthClientCredentialsClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.oauthClientCredentials.SeedOauthClientCredentialsClient; import com.seed.oauthClientCredentials.core.RequestOptions; @@ -162,7 +184,7 @@ import com.seed.oauthClientCredentials.core.RequestOptions; // Client level SeedOauthClientCredentialsClient client = SeedOauthClientCredentialsClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -170,7 +192,7 @@ client.auth().getTokenWithClientCredentials( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/oauth-client-credentials-custom/src/main/java/com/seed/oauthClientCredentials/AsyncSeedOauthClientCredentialsClient.java b/seed/java-sdk/oauth-client-credentials-custom/src/main/java/com/seed/oauthClientCredentials/AsyncSeedOauthClientCredentialsClient.java index a9e9b7d91dae..f9f377eac451 100644 --- a/seed/java-sdk/oauth-client-credentials-custom/src/main/java/com/seed/oauthClientCredentials/AsyncSeedOauthClientCredentialsClient.java +++ b/seed/java-sdk/oauth-client-credentials-custom/src/main/java/com/seed/oauthClientCredentials/AsyncSeedOauthClientCredentialsClient.java @@ -46,7 +46,23 @@ public AsyncSimpleClient simple() { return this.simpleClient.get(); } - public static AsyncSeedOauthClientCredentialsClientBuilder builder() { - return new AsyncSeedOauthClientCredentialsClientBuilder(); + /** + * Creates a client builder using a pre-generated access token. + * @param token The access token to use for authentication + * @return A builder configured for token authentication + */ + public static AsyncSeedOauthClientCredentialsClientBuilder._TokenAuth withToken(String token) { + return AsyncSeedOauthClientCredentialsClientBuilder.withToken(token); + } + + /** + * Creates a client builder using OAuth client credentials. + * @param clientId The OAuth client ID + * @param clientSecret The OAuth client secret + * @return A builder configured for OAuth authentication + */ + public static AsyncSeedOauthClientCredentialsClientBuilder._CredentialsAuth withCredentials( + String clientId, String clientSecret) { + return AsyncSeedOauthClientCredentialsClientBuilder.withCredentials(clientId, clientSecret); } } diff --git a/seed/java-sdk/oauth-client-credentials-custom/src/main/java/com/seed/oauthClientCredentials/AsyncSeedOauthClientCredentialsClientBuilder.java b/seed/java-sdk/oauth-client-credentials-custom/src/main/java/com/seed/oauthClientCredentials/AsyncSeedOauthClientCredentialsClientBuilder.java index ef9a8b8dde0f..7a7cb7717dc3 100644 --- a/seed/java-sdk/oauth-client-credentials-custom/src/main/java/com/seed/oauthClientCredentials/AsyncSeedOauthClientCredentialsClientBuilder.java +++ b/seed/java-sdk/oauth-client-credentials-custom/src/main/java/com/seed/oauthClientCredentials/AsyncSeedOauthClientCredentialsClientBuilder.java @@ -19,68 +19,32 @@ public class AsyncSeedOauthClientCredentialsClientBuilder { private final Map customHeaders = new HashMap<>(); - private String clientId = null; - - private String clientSecret = null; - - private String entityId = null; - - private String audience = null; - - private String grantType = null; - - private String scope = null; - - private Environment environment; + protected Environment environment; private OkHttpClient httpClient; /** - * Sets clientId - */ - public AsyncSeedOauthClientCredentialsClientBuilder clientId(String clientId) { - this.clientId = clientId; - return this; - } - - /** - * Sets clientSecret - */ - public AsyncSeedOauthClientCredentialsClientBuilder clientSecret(String clientSecret) { - this.clientSecret = clientSecret; - return this; - } - - /** - * Sets entityId - */ - public AsyncSeedOauthClientCredentialsClientBuilder entityId(String entityId) { - this.entityId = entityId; - return this; - } - - /** - * Sets audience - */ - public AsyncSeedOauthClientCredentialsClientBuilder audience(String audience) { - this.audience = audience; - return this; - } - - /** - * Sets grantType + * Creates a builder that uses a pre-generated access token for authentication. + * Use this when you already have a valid access token and want to bypass + * the OAuth client credentials flow. + * + * @param token The access token to use for Authorization header + * @return A builder configured for token authentication */ - public AsyncSeedOauthClientCredentialsClientBuilder grantType(String grantType) { - this.grantType = grantType; - return this; + public static _TokenAuth withToken(String token) { + return new _TokenAuth(token); } /** - * Sets scope + * Creates a builder that uses OAuth client credentials for authentication. + * The builder will automatically handle token acquisition and refresh. + * + * @param clientId The OAuth client ID + * @param clientSecret The OAuth client secret + * @return A builder configured for OAuth client credentials authentication */ - public AsyncSeedOauthClientCredentialsClientBuilder scope(String scope) { - this.scope = scope; - return this; + public static _CredentialsAuth withCredentials(String clientId, String clientSecret) { + return new _CredentialsAuth(clientId, clientSecret); } public AsyncSeedOauthClientCredentialsClientBuilder url(String url) { @@ -164,22 +128,7 @@ protected void setEnvironment(ClientOptions.Builder builder) { * } * } */ - protected void setAuthentication(ClientOptions.Builder builder) { - if (this.clientId != null && this.clientSecret != null) { - ClientOptions.Builder authClientOptionsBuilder = - ClientOptions.builder().environment(this.environment); - AuthClient authClient = new AuthClient(authClientOptionsBuilder.build()); - OAuthTokenSupplier oAuthTokenSupplier = new OAuthTokenSupplier( - this.clientId, - this.clientSecret, - this.entityId, - this.audience, - this.grantType, - this.scope, - authClient); - builder.addHeader("Authorization", oAuthTokenSupplier); - } - } + protected void setAuthentication(ClientOptions.Builder builder) {} /** * Sets the request timeout configuration. @@ -257,4 +206,72 @@ public AsyncSeedOauthClientCredentialsClient build() { validateConfiguration(); return new AsyncSeedOauthClientCredentialsClient(buildClientOptions()); } + + public static final class _TokenAuth extends AsyncSeedOauthClientCredentialsClientBuilder { + private final String token; + + _TokenAuth(String token) { + this.token = token; + } + + @Override + protected void setAuthentication(ClientOptions.Builder builder) { + builder.addHeader("Authorization", "Bearer " + this.token); + } + } + + public static final class _CredentialsAuth extends AsyncSeedOauthClientCredentialsClientBuilder { + private final String clientId; + + private final String clientSecret; + + private String entityId = null; + + private String audience = null; + + private String grantType = null; + + private String scope = null; + + _CredentialsAuth(String clientId, String clientSecret) { + this.clientId = clientId; + this.clientSecret = clientSecret; + } + + public _CredentialsAuth entityId(String entityId) { + this.entityId = entityId; + return this; + } + + public _CredentialsAuth audience(String audience) { + this.audience = audience; + return this; + } + + public _CredentialsAuth grantType(String grantType) { + this.grantType = grantType; + return this; + } + + public _CredentialsAuth scope(String scope) { + this.scope = scope; + return this; + } + + @Override + protected void setAuthentication(ClientOptions.Builder builder) { + ClientOptions.Builder authClientOptionsBuilder = + ClientOptions.builder().environment(this.environment); + AuthClient authClient = new AuthClient(authClientOptionsBuilder.build()); + OAuthTokenSupplier oAuthTokenSupplier = new OAuthTokenSupplier( + this.clientId, + this.clientSecret, + this.entityId, + this.audience, + this.grantType, + this.scope, + authClient); + builder.addHeader("Authorization", oAuthTokenSupplier); + } + } } diff --git a/seed/java-sdk/oauth-client-credentials-custom/src/main/java/com/seed/oauthClientCredentials/SeedOauthClientCredentialsClient.java b/seed/java-sdk/oauth-client-credentials-custom/src/main/java/com/seed/oauthClientCredentials/SeedOauthClientCredentialsClient.java index 3889ea39460b..67b12bf426bd 100644 --- a/seed/java-sdk/oauth-client-credentials-custom/src/main/java/com/seed/oauthClientCredentials/SeedOauthClientCredentialsClient.java +++ b/seed/java-sdk/oauth-client-credentials-custom/src/main/java/com/seed/oauthClientCredentials/SeedOauthClientCredentialsClient.java @@ -46,7 +46,23 @@ public SimpleClient simple() { return this.simpleClient.get(); } - public static SeedOauthClientCredentialsClientBuilder builder() { - return new SeedOauthClientCredentialsClientBuilder(); + /** + * Creates a client builder using a pre-generated access token. + * @param token The access token to use for authentication + * @return A builder configured for token authentication + */ + public static SeedOauthClientCredentialsClientBuilder._TokenAuth withToken(String token) { + return SeedOauthClientCredentialsClientBuilder.withToken(token); + } + + /** + * Creates a client builder using OAuth client credentials. + * @param clientId The OAuth client ID + * @param clientSecret The OAuth client secret + * @return A builder configured for OAuth authentication + */ + public static SeedOauthClientCredentialsClientBuilder._CredentialsAuth withCredentials( + String clientId, String clientSecret) { + return SeedOauthClientCredentialsClientBuilder.withCredentials(clientId, clientSecret); } } diff --git a/seed/java-sdk/oauth-client-credentials-custom/src/main/java/com/seed/oauthClientCredentials/SeedOauthClientCredentialsClientBuilder.java b/seed/java-sdk/oauth-client-credentials-custom/src/main/java/com/seed/oauthClientCredentials/SeedOauthClientCredentialsClientBuilder.java index 37469184d8fd..812142329a6f 100644 --- a/seed/java-sdk/oauth-client-credentials-custom/src/main/java/com/seed/oauthClientCredentials/SeedOauthClientCredentialsClientBuilder.java +++ b/seed/java-sdk/oauth-client-credentials-custom/src/main/java/com/seed/oauthClientCredentials/SeedOauthClientCredentialsClientBuilder.java @@ -19,68 +19,32 @@ public class SeedOauthClientCredentialsClientBuilder { private final Map customHeaders = new HashMap<>(); - private String clientId = null; - - private String clientSecret = null; - - private String entityId = null; - - private String audience = null; - - private String grantType = null; - - private String scope = null; - - private Environment environment; + protected Environment environment; private OkHttpClient httpClient; /** - * Sets clientId - */ - public SeedOauthClientCredentialsClientBuilder clientId(String clientId) { - this.clientId = clientId; - return this; - } - - /** - * Sets clientSecret - */ - public SeedOauthClientCredentialsClientBuilder clientSecret(String clientSecret) { - this.clientSecret = clientSecret; - return this; - } - - /** - * Sets entityId - */ - public SeedOauthClientCredentialsClientBuilder entityId(String entityId) { - this.entityId = entityId; - return this; - } - - /** - * Sets audience - */ - public SeedOauthClientCredentialsClientBuilder audience(String audience) { - this.audience = audience; - return this; - } - - /** - * Sets grantType + * Creates a builder that uses a pre-generated access token for authentication. + * Use this when you already have a valid access token and want to bypass + * the OAuth client credentials flow. + * + * @param token The access token to use for Authorization header + * @return A builder configured for token authentication */ - public SeedOauthClientCredentialsClientBuilder grantType(String grantType) { - this.grantType = grantType; - return this; + public static _TokenAuth withToken(String token) { + return new _TokenAuth(token); } /** - * Sets scope + * Creates a builder that uses OAuth client credentials for authentication. + * The builder will automatically handle token acquisition and refresh. + * + * @param clientId The OAuth client ID + * @param clientSecret The OAuth client secret + * @return A builder configured for OAuth client credentials authentication */ - public SeedOauthClientCredentialsClientBuilder scope(String scope) { - this.scope = scope; - return this; + public static _CredentialsAuth withCredentials(String clientId, String clientSecret) { + return new _CredentialsAuth(clientId, clientSecret); } public SeedOauthClientCredentialsClientBuilder url(String url) { @@ -164,22 +128,7 @@ protected void setEnvironment(ClientOptions.Builder builder) { * } * } */ - protected void setAuthentication(ClientOptions.Builder builder) { - if (this.clientId != null && this.clientSecret != null) { - ClientOptions.Builder authClientOptionsBuilder = - ClientOptions.builder().environment(this.environment); - AuthClient authClient = new AuthClient(authClientOptionsBuilder.build()); - OAuthTokenSupplier oAuthTokenSupplier = new OAuthTokenSupplier( - this.clientId, - this.clientSecret, - this.entityId, - this.audience, - this.grantType, - this.scope, - authClient); - builder.addHeader("Authorization", oAuthTokenSupplier); - } - } + protected void setAuthentication(ClientOptions.Builder builder) {} /** * Sets the request timeout configuration. @@ -257,4 +206,72 @@ public SeedOauthClientCredentialsClient build() { validateConfiguration(); return new SeedOauthClientCredentialsClient(buildClientOptions()); } + + public static final class _TokenAuth extends SeedOauthClientCredentialsClientBuilder { + private final String token; + + _TokenAuth(String token) { + this.token = token; + } + + @Override + protected void setAuthentication(ClientOptions.Builder builder) { + builder.addHeader("Authorization", "Bearer " + this.token); + } + } + + public static final class _CredentialsAuth extends SeedOauthClientCredentialsClientBuilder { + private final String clientId; + + private final String clientSecret; + + private String entityId = null; + + private String audience = null; + + private String grantType = null; + + private String scope = null; + + _CredentialsAuth(String clientId, String clientSecret) { + this.clientId = clientId; + this.clientSecret = clientSecret; + } + + public _CredentialsAuth entityId(String entityId) { + this.entityId = entityId; + return this; + } + + public _CredentialsAuth audience(String audience) { + this.audience = audience; + return this; + } + + public _CredentialsAuth grantType(String grantType) { + this.grantType = grantType; + return this; + } + + public _CredentialsAuth scope(String scope) { + this.scope = scope; + return this; + } + + @Override + protected void setAuthentication(ClientOptions.Builder builder) { + ClientOptions.Builder authClientOptionsBuilder = + ClientOptions.builder().environment(this.environment); + AuthClient authClient = new AuthClient(authClientOptionsBuilder.build()); + OAuthTokenSupplier oAuthTokenSupplier = new OAuthTokenSupplier( + this.clientId, + this.clientSecret, + this.entityId, + this.audience, + this.grantType, + this.scope, + authClient); + builder.addHeader("Authorization", oAuthTokenSupplier); + } + } } diff --git a/seed/java-sdk/oauth-client-credentials-default/README.md b/seed/java-sdk/oauth-client-credentials-default/README.md index 426b6647dfd9..fe2876580e79 100644 --- a/seed/java-sdk/oauth-client-credentials-default/README.md +++ b/seed/java-sdk/oauth-client-credentials-default/README.md @@ -76,6 +76,29 @@ public class Example { } } ``` +## Authentication + +This SDK supports two authentication methods: + +### Option 1: Direct Bearer Token + +If you already have a valid access token, you can use it directly: + +```java +SeedOauthClientCredentialsDefaultClient client = SeedOauthClientCredentialsDefaultClient.withToken("your-access-token") + .url("https://api.example.com") + .build(); +``` + +### Option 2: OAuth Client Credentials + +The SDK can automatically handle token acquisition and refresh: + +```java +SeedOauthClientCredentialsDefaultClient client = SeedOauthClientCredentialsDefaultClient.withCredentials("client-id", "client-secret") + .url("https://api.example.com") + .build(); +``` ## Base Url @@ -151,7 +174,6 @@ SeedOauthClientCredentialsDefaultClient client = SeedOauthClientCredentialsDefau ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.oauthClientCredentialsDefault.SeedOauthClientCredentialsDefaultClient; import com.seed.oauthClientCredentialsDefault.core.RequestOptions; @@ -159,7 +181,7 @@ import com.seed.oauthClientCredentialsDefault.core.RequestOptions; // Client level SeedOauthClientCredentialsDefaultClient client = SeedOauthClientCredentialsDefaultClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -167,7 +189,7 @@ client.auth().getToken( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/oauth-client-credentials-default/src/main/java/com/seed/oauthClientCredentialsDefault/AsyncSeedOauthClientCredentialsDefaultClient.java b/seed/java-sdk/oauth-client-credentials-default/src/main/java/com/seed/oauthClientCredentialsDefault/AsyncSeedOauthClientCredentialsDefaultClient.java index 8e5743b5c86b..ec50c38fead2 100644 --- a/seed/java-sdk/oauth-client-credentials-default/src/main/java/com/seed/oauthClientCredentialsDefault/AsyncSeedOauthClientCredentialsDefaultClient.java +++ b/seed/java-sdk/oauth-client-credentials-default/src/main/java/com/seed/oauthClientCredentialsDefault/AsyncSeedOauthClientCredentialsDefaultClient.java @@ -46,7 +46,23 @@ public AsyncSimpleClient simple() { return this.simpleClient.get(); } - public static AsyncSeedOauthClientCredentialsDefaultClientBuilder builder() { - return new AsyncSeedOauthClientCredentialsDefaultClientBuilder(); + /** + * Creates a client builder using a pre-generated access token. + * @param token The access token to use for authentication + * @return A builder configured for token authentication + */ + public static AsyncSeedOauthClientCredentialsDefaultClientBuilder._TokenAuth withToken(String token) { + return AsyncSeedOauthClientCredentialsDefaultClientBuilder.withToken(token); + } + + /** + * Creates a client builder using OAuth client credentials. + * @param clientId The OAuth client ID + * @param clientSecret The OAuth client secret + * @return A builder configured for OAuth authentication + */ + public static AsyncSeedOauthClientCredentialsDefaultClientBuilder._CredentialsAuth withCredentials( + String clientId, String clientSecret) { + return AsyncSeedOauthClientCredentialsDefaultClientBuilder.withCredentials(clientId, clientSecret); } } diff --git a/seed/java-sdk/oauth-client-credentials-default/src/main/java/com/seed/oauthClientCredentialsDefault/AsyncSeedOauthClientCredentialsDefaultClientBuilder.java b/seed/java-sdk/oauth-client-credentials-default/src/main/java/com/seed/oauthClientCredentialsDefault/AsyncSeedOauthClientCredentialsDefaultClientBuilder.java index d9db26187315..62a9f3769260 100644 --- a/seed/java-sdk/oauth-client-credentials-default/src/main/java/com/seed/oauthClientCredentialsDefault/AsyncSeedOauthClientCredentialsDefaultClientBuilder.java +++ b/seed/java-sdk/oauth-client-credentials-default/src/main/java/com/seed/oauthClientCredentialsDefault/AsyncSeedOauthClientCredentialsDefaultClientBuilder.java @@ -19,38 +19,32 @@ public class AsyncSeedOauthClientCredentialsDefaultClientBuilder { private final Map customHeaders = new HashMap<>(); - private String clientId = null; - - private String clientSecret = null; - - private String grantType = null; - - private Environment environment; + protected Environment environment; private OkHttpClient httpClient; /** - * Sets clientId + * Creates a builder that uses a pre-generated access token for authentication. + * Use this when you already have a valid access token and want to bypass + * the OAuth client credentials flow. + * + * @param token The access token to use for Authorization header + * @return A builder configured for token authentication */ - public AsyncSeedOauthClientCredentialsDefaultClientBuilder clientId(String clientId) { - this.clientId = clientId; - return this; + public static _TokenAuth withToken(String token) { + return new _TokenAuth(token); } /** - * Sets clientSecret + * Creates a builder that uses OAuth client credentials for authentication. + * The builder will automatically handle token acquisition and refresh. + * + * @param clientId The OAuth client ID + * @param clientSecret The OAuth client secret + * @return A builder configured for OAuth client credentials authentication */ - public AsyncSeedOauthClientCredentialsDefaultClientBuilder clientSecret(String clientSecret) { - this.clientSecret = clientSecret; - return this; - } - - /** - * Sets grantType - */ - public AsyncSeedOauthClientCredentialsDefaultClientBuilder grantType(String grantType) { - this.grantType = grantType; - return this; + public static _CredentialsAuth withCredentials(String clientId, String clientSecret) { + return new _CredentialsAuth(clientId, clientSecret); } public AsyncSeedOauthClientCredentialsDefaultClientBuilder url(String url) { @@ -134,16 +128,7 @@ protected void setEnvironment(ClientOptions.Builder builder) { * } * } */ - protected void setAuthentication(ClientOptions.Builder builder) { - if (this.clientId != null && this.clientSecret != null) { - ClientOptions.Builder authClientOptionsBuilder = - ClientOptions.builder().environment(this.environment); - AuthClient authClient = new AuthClient(authClientOptionsBuilder.build()); - OAuthTokenSupplier oAuthTokenSupplier = - new OAuthTokenSupplier(this.clientId, this.clientSecret, this.grantType, authClient); - builder.addHeader("Authorization", oAuthTokenSupplier); - } - } + protected void setAuthentication(ClientOptions.Builder builder) {} /** * Sets the request timeout configuration. @@ -221,4 +206,45 @@ public AsyncSeedOauthClientCredentialsDefaultClient build() { validateConfiguration(); return new AsyncSeedOauthClientCredentialsDefaultClient(buildClientOptions()); } + + public static final class _TokenAuth extends AsyncSeedOauthClientCredentialsDefaultClientBuilder { + private final String token; + + _TokenAuth(String token) { + this.token = token; + } + + @Override + protected void setAuthentication(ClientOptions.Builder builder) { + builder.addHeader("Authorization", "Bearer " + this.token); + } + } + + public static final class _CredentialsAuth extends AsyncSeedOauthClientCredentialsDefaultClientBuilder { + private final String clientId; + + private final String clientSecret; + + private String grantType = null; + + _CredentialsAuth(String clientId, String clientSecret) { + this.clientId = clientId; + this.clientSecret = clientSecret; + } + + public _CredentialsAuth grantType(String grantType) { + this.grantType = grantType; + return this; + } + + @Override + protected void setAuthentication(ClientOptions.Builder builder) { + ClientOptions.Builder authClientOptionsBuilder = + ClientOptions.builder().environment(this.environment); + AuthClient authClient = new AuthClient(authClientOptionsBuilder.build()); + OAuthTokenSupplier oAuthTokenSupplier = + new OAuthTokenSupplier(this.clientId, this.clientSecret, this.grantType, authClient); + builder.addHeader("Authorization", oAuthTokenSupplier); + } + } } diff --git a/seed/java-sdk/oauth-client-credentials-default/src/main/java/com/seed/oauthClientCredentialsDefault/SeedOauthClientCredentialsDefaultClient.java b/seed/java-sdk/oauth-client-credentials-default/src/main/java/com/seed/oauthClientCredentialsDefault/SeedOauthClientCredentialsDefaultClient.java index 1ec88a7a9430..ccb6e7c3e096 100644 --- a/seed/java-sdk/oauth-client-credentials-default/src/main/java/com/seed/oauthClientCredentialsDefault/SeedOauthClientCredentialsDefaultClient.java +++ b/seed/java-sdk/oauth-client-credentials-default/src/main/java/com/seed/oauthClientCredentialsDefault/SeedOauthClientCredentialsDefaultClient.java @@ -46,7 +46,23 @@ public SimpleClient simple() { return this.simpleClient.get(); } - public static SeedOauthClientCredentialsDefaultClientBuilder builder() { - return new SeedOauthClientCredentialsDefaultClientBuilder(); + /** + * Creates a client builder using a pre-generated access token. + * @param token The access token to use for authentication + * @return A builder configured for token authentication + */ + public static SeedOauthClientCredentialsDefaultClientBuilder._TokenAuth withToken(String token) { + return SeedOauthClientCredentialsDefaultClientBuilder.withToken(token); + } + + /** + * Creates a client builder using OAuth client credentials. + * @param clientId The OAuth client ID + * @param clientSecret The OAuth client secret + * @return A builder configured for OAuth authentication + */ + public static SeedOauthClientCredentialsDefaultClientBuilder._CredentialsAuth withCredentials( + String clientId, String clientSecret) { + return SeedOauthClientCredentialsDefaultClientBuilder.withCredentials(clientId, clientSecret); } } diff --git a/seed/java-sdk/oauth-client-credentials-default/src/main/java/com/seed/oauthClientCredentialsDefault/SeedOauthClientCredentialsDefaultClientBuilder.java b/seed/java-sdk/oauth-client-credentials-default/src/main/java/com/seed/oauthClientCredentialsDefault/SeedOauthClientCredentialsDefaultClientBuilder.java index 609b6ae1de8d..d2de7a96c748 100644 --- a/seed/java-sdk/oauth-client-credentials-default/src/main/java/com/seed/oauthClientCredentialsDefault/SeedOauthClientCredentialsDefaultClientBuilder.java +++ b/seed/java-sdk/oauth-client-credentials-default/src/main/java/com/seed/oauthClientCredentialsDefault/SeedOauthClientCredentialsDefaultClientBuilder.java @@ -19,38 +19,32 @@ public class SeedOauthClientCredentialsDefaultClientBuilder { private final Map customHeaders = new HashMap<>(); - private String clientId = null; - - private String clientSecret = null; - - private String grantType = null; - - private Environment environment; + protected Environment environment; private OkHttpClient httpClient; /** - * Sets clientId + * Creates a builder that uses a pre-generated access token for authentication. + * Use this when you already have a valid access token and want to bypass + * the OAuth client credentials flow. + * + * @param token The access token to use for Authorization header + * @return A builder configured for token authentication */ - public SeedOauthClientCredentialsDefaultClientBuilder clientId(String clientId) { - this.clientId = clientId; - return this; + public static _TokenAuth withToken(String token) { + return new _TokenAuth(token); } /** - * Sets clientSecret + * Creates a builder that uses OAuth client credentials for authentication. + * The builder will automatically handle token acquisition and refresh. + * + * @param clientId The OAuth client ID + * @param clientSecret The OAuth client secret + * @return A builder configured for OAuth client credentials authentication */ - public SeedOauthClientCredentialsDefaultClientBuilder clientSecret(String clientSecret) { - this.clientSecret = clientSecret; - return this; - } - - /** - * Sets grantType - */ - public SeedOauthClientCredentialsDefaultClientBuilder grantType(String grantType) { - this.grantType = grantType; - return this; + public static _CredentialsAuth withCredentials(String clientId, String clientSecret) { + return new _CredentialsAuth(clientId, clientSecret); } public SeedOauthClientCredentialsDefaultClientBuilder url(String url) { @@ -134,16 +128,7 @@ protected void setEnvironment(ClientOptions.Builder builder) { * } * } */ - protected void setAuthentication(ClientOptions.Builder builder) { - if (this.clientId != null && this.clientSecret != null) { - ClientOptions.Builder authClientOptionsBuilder = - ClientOptions.builder().environment(this.environment); - AuthClient authClient = new AuthClient(authClientOptionsBuilder.build()); - OAuthTokenSupplier oAuthTokenSupplier = - new OAuthTokenSupplier(this.clientId, this.clientSecret, this.grantType, authClient); - builder.addHeader("Authorization", oAuthTokenSupplier); - } - } + protected void setAuthentication(ClientOptions.Builder builder) {} /** * Sets the request timeout configuration. @@ -221,4 +206,45 @@ public SeedOauthClientCredentialsDefaultClient build() { validateConfiguration(); return new SeedOauthClientCredentialsDefaultClient(buildClientOptions()); } + + public static final class _TokenAuth extends SeedOauthClientCredentialsDefaultClientBuilder { + private final String token; + + _TokenAuth(String token) { + this.token = token; + } + + @Override + protected void setAuthentication(ClientOptions.Builder builder) { + builder.addHeader("Authorization", "Bearer " + this.token); + } + } + + public static final class _CredentialsAuth extends SeedOauthClientCredentialsDefaultClientBuilder { + private final String clientId; + + private final String clientSecret; + + private String grantType = null; + + _CredentialsAuth(String clientId, String clientSecret) { + this.clientId = clientId; + this.clientSecret = clientSecret; + } + + public _CredentialsAuth grantType(String grantType) { + this.grantType = grantType; + return this; + } + + @Override + protected void setAuthentication(ClientOptions.Builder builder) { + ClientOptions.Builder authClientOptionsBuilder = + ClientOptions.builder().environment(this.environment); + AuthClient authClient = new AuthClient(authClientOptionsBuilder.build()); + OAuthTokenSupplier oAuthTokenSupplier = + new OAuthTokenSupplier(this.clientId, this.clientSecret, this.grantType, authClient); + builder.addHeader("Authorization", oAuthTokenSupplier); + } + } } diff --git a/seed/java-sdk/oauth-client-credentials-environment-variables/README.md b/seed/java-sdk/oauth-client-credentials-environment-variables/README.md index 3430ce064a78..16e10c0c0468 100644 --- a/seed/java-sdk/oauth-client-credentials-environment-variables/README.md +++ b/seed/java-sdk/oauth-client-credentials-environment-variables/README.md @@ -77,6 +77,29 @@ public class Example { } } ``` +## Authentication + +This SDK supports two authentication methods: + +### Option 1: Direct Bearer Token + +If you already have a valid access token, you can use it directly: + +```java +SeedOauthClientCredentialsEnvironmentVariablesClient client = SeedOauthClientCredentialsEnvironmentVariablesClient.withToken("your-access-token") + .url("https://api.example.com") + .build(); +``` + +### Option 2: OAuth Client Credentials + +The SDK can automatically handle token acquisition and refresh: + +```java +SeedOauthClientCredentialsEnvironmentVariablesClient client = SeedOauthClientCredentialsEnvironmentVariablesClient.withCredentials("client-id", "client-secret") + .url("https://api.example.com") + .build(); +``` ## Base Url @@ -152,7 +175,6 @@ SeedOauthClientCredentialsEnvironmentVariablesClient client = SeedOauthClientCre ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.oauthClientCredentialsEnvironmentVariables.SeedOauthClientCredentialsEnvironmentVariablesClient; import com.seed.oauthClientCredentialsEnvironmentVariables.core.RequestOptions; @@ -160,7 +182,7 @@ import com.seed.oauthClientCredentialsEnvironmentVariables.core.RequestOptions; // Client level SeedOauthClientCredentialsEnvironmentVariablesClient client = SeedOauthClientCredentialsEnvironmentVariablesClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -168,7 +190,7 @@ client.auth().getTokenWithClientCredentials( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/oauth-client-credentials-environment-variables/src/main/java/com/seed/oauthClientCredentialsEnvironmentVariables/AsyncSeedOauthClientCredentialsEnvironmentVariablesClient.java b/seed/java-sdk/oauth-client-credentials-environment-variables/src/main/java/com/seed/oauthClientCredentialsEnvironmentVariables/AsyncSeedOauthClientCredentialsEnvironmentVariablesClient.java index 0cdd2aa37772..86b45a55b8ef 100644 --- a/seed/java-sdk/oauth-client-credentials-environment-variables/src/main/java/com/seed/oauthClientCredentialsEnvironmentVariables/AsyncSeedOauthClientCredentialsEnvironmentVariablesClient.java +++ b/seed/java-sdk/oauth-client-credentials-environment-variables/src/main/java/com/seed/oauthClientCredentialsEnvironmentVariables/AsyncSeedOauthClientCredentialsEnvironmentVariablesClient.java @@ -46,7 +46,23 @@ public AsyncSimpleClient simple() { return this.simpleClient.get(); } - public static AsyncSeedOauthClientCredentialsEnvironmentVariablesClientBuilder builder() { - return new AsyncSeedOauthClientCredentialsEnvironmentVariablesClientBuilder(); + /** + * Creates a client builder using a pre-generated access token. + * @param token The access token to use for authentication + * @return A builder configured for token authentication + */ + public static AsyncSeedOauthClientCredentialsEnvironmentVariablesClientBuilder._TokenAuth withToken(String token) { + return AsyncSeedOauthClientCredentialsEnvironmentVariablesClientBuilder.withToken(token); + } + + /** + * Creates a client builder using OAuth client credentials. + * @param clientId The OAuth client ID + * @param clientSecret The OAuth client secret + * @return A builder configured for OAuth authentication + */ + public static AsyncSeedOauthClientCredentialsEnvironmentVariablesClientBuilder._CredentialsAuth withCredentials( + String clientId, String clientSecret) { + return AsyncSeedOauthClientCredentialsEnvironmentVariablesClientBuilder.withCredentials(clientId, clientSecret); } } diff --git a/seed/java-sdk/oauth-client-credentials-environment-variables/src/main/java/com/seed/oauthClientCredentialsEnvironmentVariables/AsyncSeedOauthClientCredentialsEnvironmentVariablesClientBuilder.java b/seed/java-sdk/oauth-client-credentials-environment-variables/src/main/java/com/seed/oauthClientCredentialsEnvironmentVariables/AsyncSeedOauthClientCredentialsEnvironmentVariablesClientBuilder.java index d828373aa733..a1298840b266 100644 --- a/seed/java-sdk/oauth-client-credentials-environment-variables/src/main/java/com/seed/oauthClientCredentialsEnvironmentVariables/AsyncSeedOauthClientCredentialsEnvironmentVariablesClientBuilder.java +++ b/seed/java-sdk/oauth-client-credentials-environment-variables/src/main/java/com/seed/oauthClientCredentialsEnvironmentVariables/AsyncSeedOauthClientCredentialsEnvironmentVariablesClientBuilder.java @@ -19,60 +19,32 @@ public class AsyncSeedOauthClientCredentialsEnvironmentVariablesClientBuilder { private final Map customHeaders = new HashMap<>(); - private String clientId = System.getenv("CLIENT_ID"); - - private String clientSecret = System.getenv("CLIENT_SECRET"); - - private String audience = null; - - private String grantType = null; - - private String scope = null; - - private Environment environment; + protected Environment environment; private OkHttpClient httpClient; /** - * Sets clientId. - * Defaults to the CLIENT_ID environment variable. - */ - public AsyncSeedOauthClientCredentialsEnvironmentVariablesClientBuilder clientId(String clientId) { - this.clientId = clientId; - return this; - } - - /** - * Sets clientSecret. - * Defaults to the CLIENT_SECRET environment variable. - */ - public AsyncSeedOauthClientCredentialsEnvironmentVariablesClientBuilder clientSecret(String clientSecret) { - this.clientSecret = clientSecret; - return this; - } - - /** - * Sets audience + * Creates a builder that uses a pre-generated access token for authentication. + * Use this when you already have a valid access token and want to bypass + * the OAuth client credentials flow. + * + * @param token The access token to use for Authorization header + * @return A builder configured for token authentication */ - public AsyncSeedOauthClientCredentialsEnvironmentVariablesClientBuilder audience(String audience) { - this.audience = audience; - return this; + public static _TokenAuth withToken(String token) { + return new _TokenAuth(token); } /** - * Sets grantType + * Creates a builder that uses OAuth client credentials for authentication. + * The builder will automatically handle token acquisition and refresh. + * + * @param clientId The OAuth client ID + * @param clientSecret The OAuth client secret + * @return A builder configured for OAuth client credentials authentication */ - public AsyncSeedOauthClientCredentialsEnvironmentVariablesClientBuilder grantType(String grantType) { - this.grantType = grantType; - return this; - } - - /** - * Sets scope - */ - public AsyncSeedOauthClientCredentialsEnvironmentVariablesClientBuilder scope(String scope) { - this.scope = scope; - return this; + public static _CredentialsAuth withCredentials(String clientId, String clientSecret) { + return new _CredentialsAuth(clientId, clientSecret); } public AsyncSeedOauthClientCredentialsEnvironmentVariablesClientBuilder url(String url) { @@ -156,16 +128,7 @@ protected void setEnvironment(ClientOptions.Builder builder) { * } * } */ - protected void setAuthentication(ClientOptions.Builder builder) { - if (this.clientId != null && this.clientSecret != null) { - ClientOptions.Builder authClientOptionsBuilder = - ClientOptions.builder().environment(this.environment); - AuthClient authClient = new AuthClient(authClientOptionsBuilder.build()); - OAuthTokenSupplier oAuthTokenSupplier = new OAuthTokenSupplier( - this.clientId, this.clientSecret, this.audience, this.grantType, this.scope, authClient); - builder.addHeader("Authorization", oAuthTokenSupplier); - } - } + protected void setAuthentication(ClientOptions.Builder builder) {} /** * Sets the request timeout configuration. @@ -243,4 +206,60 @@ public AsyncSeedOauthClientCredentialsEnvironmentVariablesClient build() { validateConfiguration(); return new AsyncSeedOauthClientCredentialsEnvironmentVariablesClient(buildClientOptions()); } + + public static final class _TokenAuth extends AsyncSeedOauthClientCredentialsEnvironmentVariablesClientBuilder { + private final String token; + + _TokenAuth(String token) { + this.token = token; + } + + @Override + protected void setAuthentication(ClientOptions.Builder builder) { + builder.addHeader("Authorization", "Bearer " + this.token); + } + } + + public static final class _CredentialsAuth + extends AsyncSeedOauthClientCredentialsEnvironmentVariablesClientBuilder { + private final String clientId; + + private final String clientSecret; + + private String audience = null; + + private String grantType = null; + + private String scope = null; + + _CredentialsAuth(String clientId, String clientSecret) { + this.clientId = clientId; + this.clientSecret = clientSecret; + } + + public _CredentialsAuth audience(String audience) { + this.audience = audience; + return this; + } + + public _CredentialsAuth grantType(String grantType) { + this.grantType = grantType; + return this; + } + + public _CredentialsAuth scope(String scope) { + this.scope = scope; + return this; + } + + @Override + protected void setAuthentication(ClientOptions.Builder builder) { + ClientOptions.Builder authClientOptionsBuilder = + ClientOptions.builder().environment(this.environment); + AuthClient authClient = new AuthClient(authClientOptionsBuilder.build()); + OAuthTokenSupplier oAuthTokenSupplier = new OAuthTokenSupplier( + this.clientId, this.clientSecret, this.audience, this.grantType, this.scope, authClient); + builder.addHeader("Authorization", oAuthTokenSupplier); + } + } } diff --git a/seed/java-sdk/oauth-client-credentials-environment-variables/src/main/java/com/seed/oauthClientCredentialsEnvironmentVariables/SeedOauthClientCredentialsEnvironmentVariablesClient.java b/seed/java-sdk/oauth-client-credentials-environment-variables/src/main/java/com/seed/oauthClientCredentialsEnvironmentVariables/SeedOauthClientCredentialsEnvironmentVariablesClient.java index 289ed0e05fe6..7d3501da87c4 100644 --- a/seed/java-sdk/oauth-client-credentials-environment-variables/src/main/java/com/seed/oauthClientCredentialsEnvironmentVariables/SeedOauthClientCredentialsEnvironmentVariablesClient.java +++ b/seed/java-sdk/oauth-client-credentials-environment-variables/src/main/java/com/seed/oauthClientCredentialsEnvironmentVariables/SeedOauthClientCredentialsEnvironmentVariablesClient.java @@ -46,7 +46,23 @@ public SimpleClient simple() { return this.simpleClient.get(); } - public static SeedOauthClientCredentialsEnvironmentVariablesClientBuilder builder() { - return new SeedOauthClientCredentialsEnvironmentVariablesClientBuilder(); + /** + * Creates a client builder using a pre-generated access token. + * @param token The access token to use for authentication + * @return A builder configured for token authentication + */ + public static SeedOauthClientCredentialsEnvironmentVariablesClientBuilder._TokenAuth withToken(String token) { + return SeedOauthClientCredentialsEnvironmentVariablesClientBuilder.withToken(token); + } + + /** + * Creates a client builder using OAuth client credentials. + * @param clientId The OAuth client ID + * @param clientSecret The OAuth client secret + * @return A builder configured for OAuth authentication + */ + public static SeedOauthClientCredentialsEnvironmentVariablesClientBuilder._CredentialsAuth withCredentials( + String clientId, String clientSecret) { + return SeedOauthClientCredentialsEnvironmentVariablesClientBuilder.withCredentials(clientId, clientSecret); } } diff --git a/seed/java-sdk/oauth-client-credentials-environment-variables/src/main/java/com/seed/oauthClientCredentialsEnvironmentVariables/SeedOauthClientCredentialsEnvironmentVariablesClientBuilder.java b/seed/java-sdk/oauth-client-credentials-environment-variables/src/main/java/com/seed/oauthClientCredentialsEnvironmentVariables/SeedOauthClientCredentialsEnvironmentVariablesClientBuilder.java index 873918f64598..df4e0e893ab2 100644 --- a/seed/java-sdk/oauth-client-credentials-environment-variables/src/main/java/com/seed/oauthClientCredentialsEnvironmentVariables/SeedOauthClientCredentialsEnvironmentVariablesClientBuilder.java +++ b/seed/java-sdk/oauth-client-credentials-environment-variables/src/main/java/com/seed/oauthClientCredentialsEnvironmentVariables/SeedOauthClientCredentialsEnvironmentVariablesClientBuilder.java @@ -19,60 +19,32 @@ public class SeedOauthClientCredentialsEnvironmentVariablesClientBuilder { private final Map customHeaders = new HashMap<>(); - private String clientId = System.getenv("CLIENT_ID"); - - private String clientSecret = System.getenv("CLIENT_SECRET"); - - private String audience = null; - - private String grantType = null; - - private String scope = null; - - private Environment environment; + protected Environment environment; private OkHttpClient httpClient; /** - * Sets clientId. - * Defaults to the CLIENT_ID environment variable. - */ - public SeedOauthClientCredentialsEnvironmentVariablesClientBuilder clientId(String clientId) { - this.clientId = clientId; - return this; - } - - /** - * Sets clientSecret. - * Defaults to the CLIENT_SECRET environment variable. - */ - public SeedOauthClientCredentialsEnvironmentVariablesClientBuilder clientSecret(String clientSecret) { - this.clientSecret = clientSecret; - return this; - } - - /** - * Sets audience + * Creates a builder that uses a pre-generated access token for authentication. + * Use this when you already have a valid access token and want to bypass + * the OAuth client credentials flow. + * + * @param token The access token to use for Authorization header + * @return A builder configured for token authentication */ - public SeedOauthClientCredentialsEnvironmentVariablesClientBuilder audience(String audience) { - this.audience = audience; - return this; + public static _TokenAuth withToken(String token) { + return new _TokenAuth(token); } /** - * Sets grantType + * Creates a builder that uses OAuth client credentials for authentication. + * The builder will automatically handle token acquisition and refresh. + * + * @param clientId The OAuth client ID + * @param clientSecret The OAuth client secret + * @return A builder configured for OAuth client credentials authentication */ - public SeedOauthClientCredentialsEnvironmentVariablesClientBuilder grantType(String grantType) { - this.grantType = grantType; - return this; - } - - /** - * Sets scope - */ - public SeedOauthClientCredentialsEnvironmentVariablesClientBuilder scope(String scope) { - this.scope = scope; - return this; + public static _CredentialsAuth withCredentials(String clientId, String clientSecret) { + return new _CredentialsAuth(clientId, clientSecret); } public SeedOauthClientCredentialsEnvironmentVariablesClientBuilder url(String url) { @@ -156,16 +128,7 @@ protected void setEnvironment(ClientOptions.Builder builder) { * } * } */ - protected void setAuthentication(ClientOptions.Builder builder) { - if (this.clientId != null && this.clientSecret != null) { - ClientOptions.Builder authClientOptionsBuilder = - ClientOptions.builder().environment(this.environment); - AuthClient authClient = new AuthClient(authClientOptionsBuilder.build()); - OAuthTokenSupplier oAuthTokenSupplier = new OAuthTokenSupplier( - this.clientId, this.clientSecret, this.audience, this.grantType, this.scope, authClient); - builder.addHeader("Authorization", oAuthTokenSupplier); - } - } + protected void setAuthentication(ClientOptions.Builder builder) {} /** * Sets the request timeout configuration. @@ -243,4 +206,59 @@ public SeedOauthClientCredentialsEnvironmentVariablesClient build() { validateConfiguration(); return new SeedOauthClientCredentialsEnvironmentVariablesClient(buildClientOptions()); } + + public static final class _TokenAuth extends SeedOauthClientCredentialsEnvironmentVariablesClientBuilder { + private final String token; + + _TokenAuth(String token) { + this.token = token; + } + + @Override + protected void setAuthentication(ClientOptions.Builder builder) { + builder.addHeader("Authorization", "Bearer " + this.token); + } + } + + public static final class _CredentialsAuth extends SeedOauthClientCredentialsEnvironmentVariablesClientBuilder { + private final String clientId; + + private final String clientSecret; + + private String audience = null; + + private String grantType = null; + + private String scope = null; + + _CredentialsAuth(String clientId, String clientSecret) { + this.clientId = clientId; + this.clientSecret = clientSecret; + } + + public _CredentialsAuth audience(String audience) { + this.audience = audience; + return this; + } + + public _CredentialsAuth grantType(String grantType) { + this.grantType = grantType; + return this; + } + + public _CredentialsAuth scope(String scope) { + this.scope = scope; + return this; + } + + @Override + protected void setAuthentication(ClientOptions.Builder builder) { + ClientOptions.Builder authClientOptionsBuilder = + ClientOptions.builder().environment(this.environment); + AuthClient authClient = new AuthClient(authClientOptionsBuilder.build()); + OAuthTokenSupplier oAuthTokenSupplier = new OAuthTokenSupplier( + this.clientId, this.clientSecret, this.audience, this.grantType, this.scope, authClient); + builder.addHeader("Authorization", oAuthTokenSupplier); + } + } } diff --git a/seed/java-sdk/oauth-client-credentials-nested-root/README.md b/seed/java-sdk/oauth-client-credentials-nested-root/README.md index 4f1f710a7d72..2671187a7aeb 100644 --- a/seed/java-sdk/oauth-client-credentials-nested-root/README.md +++ b/seed/java-sdk/oauth-client-credentials-nested-root/README.md @@ -77,6 +77,29 @@ public class Example { } } ``` +## Authentication + +This SDK supports two authentication methods: + +### Option 1: Direct Bearer Token + +If you already have a valid access token, you can use it directly: + +```java +SeedOauthClientCredentialsClient client = SeedOauthClientCredentialsClient.withToken("your-access-token") + .url("https://api.example.com") + .build(); +``` + +### Option 2: OAuth Client Credentials + +The SDK can automatically handle token acquisition and refresh: + +```java +SeedOauthClientCredentialsClient client = SeedOauthClientCredentialsClient.withCredentials("client-id", "client-secret") + .url("https://api.example.com") + .build(); +``` ## Base Url @@ -152,7 +175,6 @@ SeedOauthClientCredentialsClient client = SeedOauthClientCredentialsClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.oauthClientCredentials.SeedOauthClientCredentialsClient; import com.seed.oauthClientCredentials.core.RequestOptions; @@ -160,7 +182,7 @@ import com.seed.oauthClientCredentials.core.RequestOptions; // Client level SeedOauthClientCredentialsClient client = SeedOauthClientCredentialsClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -168,7 +190,7 @@ client.auth().getToken( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/oauth-client-credentials-nested-root/src/main/java/com/seed/oauthClientCredentials/AsyncSeedOauthClientCredentialsClient.java b/seed/java-sdk/oauth-client-credentials-nested-root/src/main/java/com/seed/oauthClientCredentials/AsyncSeedOauthClientCredentialsClient.java index a9e9b7d91dae..f9f377eac451 100644 --- a/seed/java-sdk/oauth-client-credentials-nested-root/src/main/java/com/seed/oauthClientCredentials/AsyncSeedOauthClientCredentialsClient.java +++ b/seed/java-sdk/oauth-client-credentials-nested-root/src/main/java/com/seed/oauthClientCredentials/AsyncSeedOauthClientCredentialsClient.java @@ -46,7 +46,23 @@ public AsyncSimpleClient simple() { return this.simpleClient.get(); } - public static AsyncSeedOauthClientCredentialsClientBuilder builder() { - return new AsyncSeedOauthClientCredentialsClientBuilder(); + /** + * Creates a client builder using a pre-generated access token. + * @param token The access token to use for authentication + * @return A builder configured for token authentication + */ + public static AsyncSeedOauthClientCredentialsClientBuilder._TokenAuth withToken(String token) { + return AsyncSeedOauthClientCredentialsClientBuilder.withToken(token); + } + + /** + * Creates a client builder using OAuth client credentials. + * @param clientId The OAuth client ID + * @param clientSecret The OAuth client secret + * @return A builder configured for OAuth authentication + */ + public static AsyncSeedOauthClientCredentialsClientBuilder._CredentialsAuth withCredentials( + String clientId, String clientSecret) { + return AsyncSeedOauthClientCredentialsClientBuilder.withCredentials(clientId, clientSecret); } } diff --git a/seed/java-sdk/oauth-client-credentials-nested-root/src/main/java/com/seed/oauthClientCredentials/AsyncSeedOauthClientCredentialsClientBuilder.java b/seed/java-sdk/oauth-client-credentials-nested-root/src/main/java/com/seed/oauthClientCredentials/AsyncSeedOauthClientCredentialsClientBuilder.java index 570898df1e2e..8978b10154c9 100644 --- a/seed/java-sdk/oauth-client-credentials-nested-root/src/main/java/com/seed/oauthClientCredentials/AsyncSeedOauthClientCredentialsClientBuilder.java +++ b/seed/java-sdk/oauth-client-credentials-nested-root/src/main/java/com/seed/oauthClientCredentials/AsyncSeedOauthClientCredentialsClientBuilder.java @@ -19,58 +19,32 @@ public class AsyncSeedOauthClientCredentialsClientBuilder { private final Map customHeaders = new HashMap<>(); - private String clientId = null; - - private String clientSecret = null; - - private String audience = null; - - private String grantType = null; - - private String scope = null; - - private Environment environment; + protected Environment environment; private OkHttpClient httpClient; /** - * Sets clientId - */ - public AsyncSeedOauthClientCredentialsClientBuilder clientId(String clientId) { - this.clientId = clientId; - return this; - } - - /** - * Sets clientSecret - */ - public AsyncSeedOauthClientCredentialsClientBuilder clientSecret(String clientSecret) { - this.clientSecret = clientSecret; - return this; - } - - /** - * Sets audience + * Creates a builder that uses a pre-generated access token for authentication. + * Use this when you already have a valid access token and want to bypass + * the OAuth client credentials flow. + * + * @param token The access token to use for Authorization header + * @return A builder configured for token authentication */ - public AsyncSeedOauthClientCredentialsClientBuilder audience(String audience) { - this.audience = audience; - return this; + public static _TokenAuth withToken(String token) { + return new _TokenAuth(token); } /** - * Sets grantType + * Creates a builder that uses OAuth client credentials for authentication. + * The builder will automatically handle token acquisition and refresh. + * + * @param clientId The OAuth client ID + * @param clientSecret The OAuth client secret + * @return A builder configured for OAuth client credentials authentication */ - public AsyncSeedOauthClientCredentialsClientBuilder grantType(String grantType) { - this.grantType = grantType; - return this; - } - - /** - * Sets scope - */ - public AsyncSeedOauthClientCredentialsClientBuilder scope(String scope) { - this.scope = scope; - return this; + public static _CredentialsAuth withCredentials(String clientId, String clientSecret) { + return new _CredentialsAuth(clientId, clientSecret); } public AsyncSeedOauthClientCredentialsClientBuilder url(String url) { @@ -154,16 +128,7 @@ protected void setEnvironment(ClientOptions.Builder builder) { * } * } */ - protected void setAuthentication(ClientOptions.Builder builder) { - if (this.clientId != null && this.clientSecret != null) { - ClientOptions.Builder authClientOptionsBuilder = - ClientOptions.builder().environment(this.environment); - AuthClient authClient = new AuthClient(authClientOptionsBuilder.build()); - OAuthTokenSupplier oAuthTokenSupplier = new OAuthTokenSupplier( - this.clientId, this.clientSecret, this.audience, this.grantType, this.scope, authClient); - builder.addHeader("Authorization", oAuthTokenSupplier); - } - } + protected void setAuthentication(ClientOptions.Builder builder) {} /** * Sets the request timeout configuration. @@ -241,4 +206,59 @@ public AsyncSeedOauthClientCredentialsClient build() { validateConfiguration(); return new AsyncSeedOauthClientCredentialsClient(buildClientOptions()); } + + public static final class _TokenAuth extends AsyncSeedOauthClientCredentialsClientBuilder { + private final String token; + + _TokenAuth(String token) { + this.token = token; + } + + @Override + protected void setAuthentication(ClientOptions.Builder builder) { + builder.addHeader("Authorization", "Bearer " + this.token); + } + } + + public static final class _CredentialsAuth extends AsyncSeedOauthClientCredentialsClientBuilder { + private final String clientId; + + private final String clientSecret; + + private String audience = null; + + private String grantType = null; + + private String scope = null; + + _CredentialsAuth(String clientId, String clientSecret) { + this.clientId = clientId; + this.clientSecret = clientSecret; + } + + public _CredentialsAuth audience(String audience) { + this.audience = audience; + return this; + } + + public _CredentialsAuth grantType(String grantType) { + this.grantType = grantType; + return this; + } + + public _CredentialsAuth scope(String scope) { + this.scope = scope; + return this; + } + + @Override + protected void setAuthentication(ClientOptions.Builder builder) { + ClientOptions.Builder authClientOptionsBuilder = + ClientOptions.builder().environment(this.environment); + AuthClient authClient = new AuthClient(authClientOptionsBuilder.build()); + OAuthTokenSupplier oAuthTokenSupplier = new OAuthTokenSupplier( + this.clientId, this.clientSecret, this.audience, this.grantType, this.scope, authClient); + builder.addHeader("Authorization", oAuthTokenSupplier); + } + } } diff --git a/seed/java-sdk/oauth-client-credentials-nested-root/src/main/java/com/seed/oauthClientCredentials/SeedOauthClientCredentialsClient.java b/seed/java-sdk/oauth-client-credentials-nested-root/src/main/java/com/seed/oauthClientCredentials/SeedOauthClientCredentialsClient.java index 3889ea39460b..67b12bf426bd 100644 --- a/seed/java-sdk/oauth-client-credentials-nested-root/src/main/java/com/seed/oauthClientCredentials/SeedOauthClientCredentialsClient.java +++ b/seed/java-sdk/oauth-client-credentials-nested-root/src/main/java/com/seed/oauthClientCredentials/SeedOauthClientCredentialsClient.java @@ -46,7 +46,23 @@ public SimpleClient simple() { return this.simpleClient.get(); } - public static SeedOauthClientCredentialsClientBuilder builder() { - return new SeedOauthClientCredentialsClientBuilder(); + /** + * Creates a client builder using a pre-generated access token. + * @param token The access token to use for authentication + * @return A builder configured for token authentication + */ + public static SeedOauthClientCredentialsClientBuilder._TokenAuth withToken(String token) { + return SeedOauthClientCredentialsClientBuilder.withToken(token); + } + + /** + * Creates a client builder using OAuth client credentials. + * @param clientId The OAuth client ID + * @param clientSecret The OAuth client secret + * @return A builder configured for OAuth authentication + */ + public static SeedOauthClientCredentialsClientBuilder._CredentialsAuth withCredentials( + String clientId, String clientSecret) { + return SeedOauthClientCredentialsClientBuilder.withCredentials(clientId, clientSecret); } } diff --git a/seed/java-sdk/oauth-client-credentials-nested-root/src/main/java/com/seed/oauthClientCredentials/SeedOauthClientCredentialsClientBuilder.java b/seed/java-sdk/oauth-client-credentials-nested-root/src/main/java/com/seed/oauthClientCredentials/SeedOauthClientCredentialsClientBuilder.java index f435aaf77d81..4cfd31292e9f 100644 --- a/seed/java-sdk/oauth-client-credentials-nested-root/src/main/java/com/seed/oauthClientCredentials/SeedOauthClientCredentialsClientBuilder.java +++ b/seed/java-sdk/oauth-client-credentials-nested-root/src/main/java/com/seed/oauthClientCredentials/SeedOauthClientCredentialsClientBuilder.java @@ -19,58 +19,32 @@ public class SeedOauthClientCredentialsClientBuilder { private final Map customHeaders = new HashMap<>(); - private String clientId = null; - - private String clientSecret = null; - - private String audience = null; - - private String grantType = null; - - private String scope = null; - - private Environment environment; + protected Environment environment; private OkHttpClient httpClient; /** - * Sets clientId - */ - public SeedOauthClientCredentialsClientBuilder clientId(String clientId) { - this.clientId = clientId; - return this; - } - - /** - * Sets clientSecret - */ - public SeedOauthClientCredentialsClientBuilder clientSecret(String clientSecret) { - this.clientSecret = clientSecret; - return this; - } - - /** - * Sets audience + * Creates a builder that uses a pre-generated access token for authentication. + * Use this when you already have a valid access token and want to bypass + * the OAuth client credentials flow. + * + * @param token The access token to use for Authorization header + * @return A builder configured for token authentication */ - public SeedOauthClientCredentialsClientBuilder audience(String audience) { - this.audience = audience; - return this; + public static _TokenAuth withToken(String token) { + return new _TokenAuth(token); } /** - * Sets grantType + * Creates a builder that uses OAuth client credentials for authentication. + * The builder will automatically handle token acquisition and refresh. + * + * @param clientId The OAuth client ID + * @param clientSecret The OAuth client secret + * @return A builder configured for OAuth client credentials authentication */ - public SeedOauthClientCredentialsClientBuilder grantType(String grantType) { - this.grantType = grantType; - return this; - } - - /** - * Sets scope - */ - public SeedOauthClientCredentialsClientBuilder scope(String scope) { - this.scope = scope; - return this; + public static _CredentialsAuth withCredentials(String clientId, String clientSecret) { + return new _CredentialsAuth(clientId, clientSecret); } public SeedOauthClientCredentialsClientBuilder url(String url) { @@ -154,16 +128,7 @@ protected void setEnvironment(ClientOptions.Builder builder) { * } * } */ - protected void setAuthentication(ClientOptions.Builder builder) { - if (this.clientId != null && this.clientSecret != null) { - ClientOptions.Builder authClientOptionsBuilder = - ClientOptions.builder().environment(this.environment); - AuthClient authClient = new AuthClient(authClientOptionsBuilder.build()); - OAuthTokenSupplier oAuthTokenSupplier = new OAuthTokenSupplier( - this.clientId, this.clientSecret, this.audience, this.grantType, this.scope, authClient); - builder.addHeader("Authorization", oAuthTokenSupplier); - } - } + protected void setAuthentication(ClientOptions.Builder builder) {} /** * Sets the request timeout configuration. @@ -241,4 +206,59 @@ public SeedOauthClientCredentialsClient build() { validateConfiguration(); return new SeedOauthClientCredentialsClient(buildClientOptions()); } + + public static final class _TokenAuth extends SeedOauthClientCredentialsClientBuilder { + private final String token; + + _TokenAuth(String token) { + this.token = token; + } + + @Override + protected void setAuthentication(ClientOptions.Builder builder) { + builder.addHeader("Authorization", "Bearer " + this.token); + } + } + + public static final class _CredentialsAuth extends SeedOauthClientCredentialsClientBuilder { + private final String clientId; + + private final String clientSecret; + + private String audience = null; + + private String grantType = null; + + private String scope = null; + + _CredentialsAuth(String clientId, String clientSecret) { + this.clientId = clientId; + this.clientSecret = clientSecret; + } + + public _CredentialsAuth audience(String audience) { + this.audience = audience; + return this; + } + + public _CredentialsAuth grantType(String grantType) { + this.grantType = grantType; + return this; + } + + public _CredentialsAuth scope(String scope) { + this.scope = scope; + return this; + } + + @Override + protected void setAuthentication(ClientOptions.Builder builder) { + ClientOptions.Builder authClientOptionsBuilder = + ClientOptions.builder().environment(this.environment); + AuthClient authClient = new AuthClient(authClientOptionsBuilder.build()); + OAuthTokenSupplier oAuthTokenSupplier = new OAuthTokenSupplier( + this.clientId, this.clientSecret, this.audience, this.grantType, this.scope, authClient); + builder.addHeader("Authorization", oAuthTokenSupplier); + } + } } diff --git a/seed/java-sdk/oauth-client-credentials-with-variables/README.md b/seed/java-sdk/oauth-client-credentials-with-variables/README.md index 51687646cf62..7db7b27263d7 100644 --- a/seed/java-sdk/oauth-client-credentials-with-variables/README.md +++ b/seed/java-sdk/oauth-client-credentials-with-variables/README.md @@ -77,6 +77,29 @@ public class Example { } } ``` +## Authentication + +This SDK supports two authentication methods: + +### Option 1: Direct Bearer Token + +If you already have a valid access token, you can use it directly: + +```java +SeedOauthClientCredentialsWithVariablesClient client = SeedOauthClientCredentialsWithVariablesClient.withToken("your-access-token") + .url("https://api.example.com") + .build(); +``` + +### Option 2: OAuth Client Credentials + +The SDK can automatically handle token acquisition and refresh: + +```java +SeedOauthClientCredentialsWithVariablesClient client = SeedOauthClientCredentialsWithVariablesClient.withCredentials("client-id", "client-secret") + .url("https://api.example.com") + .build(); +``` ## Base Url @@ -152,7 +175,6 @@ SeedOauthClientCredentialsWithVariablesClient client = SeedOauthClientCredential ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.oauthClientCredentialsWithVariables.SeedOauthClientCredentialsWithVariablesClient; import com.seed.oauthClientCredentialsWithVariables.core.RequestOptions; @@ -160,7 +182,7 @@ import com.seed.oauthClientCredentialsWithVariables.core.RequestOptions; // Client level SeedOauthClientCredentialsWithVariablesClient client = SeedOauthClientCredentialsWithVariablesClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -168,7 +190,7 @@ client.auth().getTokenWithClientCredentials( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/oauth-client-credentials-with-variables/src/main/java/com/seed/oauthClientCredentialsWithVariables/AsyncSeedOauthClientCredentialsWithVariablesClient.java b/seed/java-sdk/oauth-client-credentials-with-variables/src/main/java/com/seed/oauthClientCredentialsWithVariables/AsyncSeedOauthClientCredentialsWithVariablesClient.java index 18780b827d3b..d27f411a592c 100644 --- a/seed/java-sdk/oauth-client-credentials-with-variables/src/main/java/com/seed/oauthClientCredentialsWithVariables/AsyncSeedOauthClientCredentialsWithVariablesClient.java +++ b/seed/java-sdk/oauth-client-credentials-with-variables/src/main/java/com/seed/oauthClientCredentialsWithVariables/AsyncSeedOauthClientCredentialsWithVariablesClient.java @@ -54,7 +54,23 @@ public AsyncSimpleClient simple() { return this.simpleClient.get(); } - public static AsyncSeedOauthClientCredentialsWithVariablesClientBuilder builder() { - return new AsyncSeedOauthClientCredentialsWithVariablesClientBuilder(); + /** + * Creates a client builder using a pre-generated access token. + * @param token The access token to use for authentication + * @return A builder configured for token authentication + */ + public static AsyncSeedOauthClientCredentialsWithVariablesClientBuilder._TokenAuth withToken(String token) { + return AsyncSeedOauthClientCredentialsWithVariablesClientBuilder.withToken(token); + } + + /** + * Creates a client builder using OAuth client credentials. + * @param clientId The OAuth client ID + * @param clientSecret The OAuth client secret + * @return A builder configured for OAuth authentication + */ + public static AsyncSeedOauthClientCredentialsWithVariablesClientBuilder._CredentialsAuth withCredentials( + String clientId, String clientSecret) { + return AsyncSeedOauthClientCredentialsWithVariablesClientBuilder.withCredentials(clientId, clientSecret); } } diff --git a/seed/java-sdk/oauth-client-credentials-with-variables/src/main/java/com/seed/oauthClientCredentialsWithVariables/AsyncSeedOauthClientCredentialsWithVariablesClientBuilder.java b/seed/java-sdk/oauth-client-credentials-with-variables/src/main/java/com/seed/oauthClientCredentialsWithVariables/AsyncSeedOauthClientCredentialsWithVariablesClientBuilder.java index 3d9e49d05876..0f32ddfd0ffb 100644 --- a/seed/java-sdk/oauth-client-credentials-with-variables/src/main/java/com/seed/oauthClientCredentialsWithVariables/AsyncSeedOauthClientCredentialsWithVariablesClientBuilder.java +++ b/seed/java-sdk/oauth-client-credentials-with-variables/src/main/java/com/seed/oauthClientCredentialsWithVariables/AsyncSeedOauthClientCredentialsWithVariablesClientBuilder.java @@ -19,60 +19,34 @@ public class AsyncSeedOauthClientCredentialsWithVariablesClientBuilder { private final Map customHeaders = new HashMap<>(); - private String clientId = null; - - private String clientSecret = null; - - private String audience = null; - - private String grantType = null; - - private String scope = null; - - private Environment environment; + protected Environment environment; private OkHttpClient httpClient; private String rootVariable; /** - * Sets clientId - */ - public AsyncSeedOauthClientCredentialsWithVariablesClientBuilder clientId(String clientId) { - this.clientId = clientId; - return this; - } - - /** - * Sets clientSecret - */ - public AsyncSeedOauthClientCredentialsWithVariablesClientBuilder clientSecret(String clientSecret) { - this.clientSecret = clientSecret; - return this; - } - - /** - * Sets audience - */ - public AsyncSeedOauthClientCredentialsWithVariablesClientBuilder audience(String audience) { - this.audience = audience; - return this; - } - - /** - * Sets grantType + * Creates a builder that uses a pre-generated access token for authentication. + * Use this when you already have a valid access token and want to bypass + * the OAuth client credentials flow. + * + * @param token The access token to use for Authorization header + * @return A builder configured for token authentication */ - public AsyncSeedOauthClientCredentialsWithVariablesClientBuilder grantType(String grantType) { - this.grantType = grantType; - return this; + public static _TokenAuth withToken(String token) { + return new _TokenAuth(token); } /** - * Sets scope + * Creates a builder that uses OAuth client credentials for authentication. + * The builder will automatically handle token acquisition and refresh. + * + * @param clientId The OAuth client ID + * @param clientSecret The OAuth client secret + * @return A builder configured for OAuth client credentials authentication */ - public AsyncSeedOauthClientCredentialsWithVariablesClientBuilder scope(String scope) { - this.scope = scope; - return this; + public static _CredentialsAuth withCredentials(String clientId, String clientSecret) { + return new _CredentialsAuth(clientId, clientSecret); } public AsyncSeedOauthClientCredentialsWithVariablesClientBuilder url(String url) { @@ -162,19 +136,7 @@ protected void setEnvironment(ClientOptions.Builder builder) { * } * } */ - protected void setAuthentication(ClientOptions.Builder builder) { - if (this.clientId != null && this.clientSecret != null) { - ClientOptions.Builder authClientOptionsBuilder = - ClientOptions.builder().environment(this.environment); - if (this.rootVariable != null) { - authClientOptionsBuilder.rootVariable(this.rootVariable); - } - AuthClient authClient = new AuthClient(authClientOptionsBuilder.build()); - OAuthTokenSupplier oAuthTokenSupplier = new OAuthTokenSupplier( - this.clientId, this.clientSecret, this.audience, this.grantType, this.scope, authClient); - builder.addHeader("Authorization", oAuthTokenSupplier); - } - } + protected void setAuthentication(ClientOptions.Builder builder) {} /** * Override this method to configure API variables defined in the specification. @@ -264,4 +226,62 @@ public AsyncSeedOauthClientCredentialsWithVariablesClient build() { validateConfiguration(); return new AsyncSeedOauthClientCredentialsWithVariablesClient(buildClientOptions()); } + + public static final class _TokenAuth extends AsyncSeedOauthClientCredentialsWithVariablesClientBuilder { + private final String token; + + _TokenAuth(String token) { + this.token = token; + } + + @Override + protected void setAuthentication(ClientOptions.Builder builder) { + builder.addHeader("Authorization", "Bearer " + this.token); + } + } + + public static final class _CredentialsAuth extends AsyncSeedOauthClientCredentialsWithVariablesClientBuilder { + private final String clientId; + + private final String clientSecret; + + private String audience = null; + + private String grantType = null; + + private String scope = null; + + _CredentialsAuth(String clientId, String clientSecret) { + this.clientId = clientId; + this.clientSecret = clientSecret; + } + + public _CredentialsAuth audience(String audience) { + this.audience = audience; + return this; + } + + public _CredentialsAuth grantType(String grantType) { + this.grantType = grantType; + return this; + } + + public _CredentialsAuth scope(String scope) { + this.scope = scope; + return this; + } + + @Override + protected void setAuthentication(ClientOptions.Builder builder) { + ClientOptions.Builder authClientOptionsBuilder = + ClientOptions.builder().environment(this.environment); + if (this.rootVariable != null) { + authClientOptionsBuilder.rootVariable(this.rootVariable); + } + AuthClient authClient = new AuthClient(authClientOptionsBuilder.build()); + OAuthTokenSupplier oAuthTokenSupplier = new OAuthTokenSupplier( + this.clientId, this.clientSecret, this.audience, this.grantType, this.scope, authClient); + builder.addHeader("Authorization", oAuthTokenSupplier); + } + } } diff --git a/seed/java-sdk/oauth-client-credentials-with-variables/src/main/java/com/seed/oauthClientCredentialsWithVariables/SeedOauthClientCredentialsWithVariablesClient.java b/seed/java-sdk/oauth-client-credentials-with-variables/src/main/java/com/seed/oauthClientCredentialsWithVariables/SeedOauthClientCredentialsWithVariablesClient.java index 7933bfa76001..b43695f9b8c7 100644 --- a/seed/java-sdk/oauth-client-credentials-with-variables/src/main/java/com/seed/oauthClientCredentialsWithVariables/SeedOauthClientCredentialsWithVariablesClient.java +++ b/seed/java-sdk/oauth-client-credentials-with-variables/src/main/java/com/seed/oauthClientCredentialsWithVariables/SeedOauthClientCredentialsWithVariablesClient.java @@ -54,7 +54,23 @@ public SimpleClient simple() { return this.simpleClient.get(); } - public static SeedOauthClientCredentialsWithVariablesClientBuilder builder() { - return new SeedOauthClientCredentialsWithVariablesClientBuilder(); + /** + * Creates a client builder using a pre-generated access token. + * @param token The access token to use for authentication + * @return A builder configured for token authentication + */ + public static SeedOauthClientCredentialsWithVariablesClientBuilder._TokenAuth withToken(String token) { + return SeedOauthClientCredentialsWithVariablesClientBuilder.withToken(token); + } + + /** + * Creates a client builder using OAuth client credentials. + * @param clientId The OAuth client ID + * @param clientSecret The OAuth client secret + * @return A builder configured for OAuth authentication + */ + public static SeedOauthClientCredentialsWithVariablesClientBuilder._CredentialsAuth withCredentials( + String clientId, String clientSecret) { + return SeedOauthClientCredentialsWithVariablesClientBuilder.withCredentials(clientId, clientSecret); } } diff --git a/seed/java-sdk/oauth-client-credentials-with-variables/src/main/java/com/seed/oauthClientCredentialsWithVariables/SeedOauthClientCredentialsWithVariablesClientBuilder.java b/seed/java-sdk/oauth-client-credentials-with-variables/src/main/java/com/seed/oauthClientCredentialsWithVariables/SeedOauthClientCredentialsWithVariablesClientBuilder.java index 565433688702..612eaa32ce21 100644 --- a/seed/java-sdk/oauth-client-credentials-with-variables/src/main/java/com/seed/oauthClientCredentialsWithVariables/SeedOauthClientCredentialsWithVariablesClientBuilder.java +++ b/seed/java-sdk/oauth-client-credentials-with-variables/src/main/java/com/seed/oauthClientCredentialsWithVariables/SeedOauthClientCredentialsWithVariablesClientBuilder.java @@ -19,60 +19,34 @@ public class SeedOauthClientCredentialsWithVariablesClientBuilder { private final Map customHeaders = new HashMap<>(); - private String clientId = null; - - private String clientSecret = null; - - private String audience = null; - - private String grantType = null; - - private String scope = null; - - private Environment environment; + protected Environment environment; private OkHttpClient httpClient; private String rootVariable; /** - * Sets clientId - */ - public SeedOauthClientCredentialsWithVariablesClientBuilder clientId(String clientId) { - this.clientId = clientId; - return this; - } - - /** - * Sets clientSecret - */ - public SeedOauthClientCredentialsWithVariablesClientBuilder clientSecret(String clientSecret) { - this.clientSecret = clientSecret; - return this; - } - - /** - * Sets audience - */ - public SeedOauthClientCredentialsWithVariablesClientBuilder audience(String audience) { - this.audience = audience; - return this; - } - - /** - * Sets grantType + * Creates a builder that uses a pre-generated access token for authentication. + * Use this when you already have a valid access token and want to bypass + * the OAuth client credentials flow. + * + * @param token The access token to use for Authorization header + * @return A builder configured for token authentication */ - public SeedOauthClientCredentialsWithVariablesClientBuilder grantType(String grantType) { - this.grantType = grantType; - return this; + public static _TokenAuth withToken(String token) { + return new _TokenAuth(token); } /** - * Sets scope + * Creates a builder that uses OAuth client credentials for authentication. + * The builder will automatically handle token acquisition and refresh. + * + * @param clientId The OAuth client ID + * @param clientSecret The OAuth client secret + * @return A builder configured for OAuth client credentials authentication */ - public SeedOauthClientCredentialsWithVariablesClientBuilder scope(String scope) { - this.scope = scope; - return this; + public static _CredentialsAuth withCredentials(String clientId, String clientSecret) { + return new _CredentialsAuth(clientId, clientSecret); } public SeedOauthClientCredentialsWithVariablesClientBuilder url(String url) { @@ -162,19 +136,7 @@ protected void setEnvironment(ClientOptions.Builder builder) { * } * } */ - protected void setAuthentication(ClientOptions.Builder builder) { - if (this.clientId != null && this.clientSecret != null) { - ClientOptions.Builder authClientOptionsBuilder = - ClientOptions.builder().environment(this.environment); - if (this.rootVariable != null) { - authClientOptionsBuilder.rootVariable(this.rootVariable); - } - AuthClient authClient = new AuthClient(authClientOptionsBuilder.build()); - OAuthTokenSupplier oAuthTokenSupplier = new OAuthTokenSupplier( - this.clientId, this.clientSecret, this.audience, this.grantType, this.scope, authClient); - builder.addHeader("Authorization", oAuthTokenSupplier); - } - } + protected void setAuthentication(ClientOptions.Builder builder) {} /** * Override this method to configure API variables defined in the specification. @@ -264,4 +226,62 @@ public SeedOauthClientCredentialsWithVariablesClient build() { validateConfiguration(); return new SeedOauthClientCredentialsWithVariablesClient(buildClientOptions()); } + + public static final class _TokenAuth extends SeedOauthClientCredentialsWithVariablesClientBuilder { + private final String token; + + _TokenAuth(String token) { + this.token = token; + } + + @Override + protected void setAuthentication(ClientOptions.Builder builder) { + builder.addHeader("Authorization", "Bearer " + this.token); + } + } + + public static final class _CredentialsAuth extends SeedOauthClientCredentialsWithVariablesClientBuilder { + private final String clientId; + + private final String clientSecret; + + private String audience = null; + + private String grantType = null; + + private String scope = null; + + _CredentialsAuth(String clientId, String clientSecret) { + this.clientId = clientId; + this.clientSecret = clientSecret; + } + + public _CredentialsAuth audience(String audience) { + this.audience = audience; + return this; + } + + public _CredentialsAuth grantType(String grantType) { + this.grantType = grantType; + return this; + } + + public _CredentialsAuth scope(String scope) { + this.scope = scope; + return this; + } + + @Override + protected void setAuthentication(ClientOptions.Builder builder) { + ClientOptions.Builder authClientOptionsBuilder = + ClientOptions.builder().environment(this.environment); + if (this.rootVariable != null) { + authClientOptionsBuilder.rootVariable(this.rootVariable); + } + AuthClient authClient = new AuthClient(authClientOptionsBuilder.build()); + OAuthTokenSupplier oAuthTokenSupplier = new OAuthTokenSupplier( + this.clientId, this.clientSecret, this.audience, this.grantType, this.scope, authClient); + builder.addHeader("Authorization", oAuthTokenSupplier); + } + } } diff --git a/seed/java-sdk/oauth-client-credentials/oauth-client-credentials/README.md b/seed/java-sdk/oauth-client-credentials/oauth-client-credentials/README.md index 1c44022e185f..4b46712b02ea 100644 --- a/seed/java-sdk/oauth-client-credentials/oauth-client-credentials/README.md +++ b/seed/java-sdk/oauth-client-credentials/oauth-client-credentials/README.md @@ -77,6 +77,29 @@ public class Example { } } ``` +## Authentication + +This SDK supports two authentication methods: + +### Option 1: Direct Bearer Token + +If you already have a valid access token, you can use it directly: + +```java +SeedOauthClientCredentialsClient client = SeedOauthClientCredentialsClient.withToken("your-access-token") + .url("https://api.example.com") + .build(); +``` + +### Option 2: OAuth Client Credentials + +The SDK can automatically handle token acquisition and refresh: + +```java +SeedOauthClientCredentialsClient client = SeedOauthClientCredentialsClient.withCredentials("client-id", "client-secret") + .url("https://api.example.com") + .build(); +``` ## Base Url @@ -152,7 +175,6 @@ SeedOauthClientCredentialsClient client = SeedOauthClientCredentialsClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.oauthClientCredentials.SeedOauthClientCredentialsClient; import com.seed.oauthClientCredentials.core.RequestOptions; @@ -160,7 +182,7 @@ import com.seed.oauthClientCredentials.core.RequestOptions; // Client level SeedOauthClientCredentialsClient client = SeedOauthClientCredentialsClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -168,7 +190,7 @@ client.auth().getTokenWithClientCredentials( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/oauth-client-credentials/oauth-client-credentials/src/main/java/com/seed/oauthClientCredentials/AsyncSeedOauthClientCredentialsClient.java b/seed/java-sdk/oauth-client-credentials/oauth-client-credentials/src/main/java/com/seed/oauthClientCredentials/AsyncSeedOauthClientCredentialsClient.java index a9e9b7d91dae..f9f377eac451 100644 --- a/seed/java-sdk/oauth-client-credentials/oauth-client-credentials/src/main/java/com/seed/oauthClientCredentials/AsyncSeedOauthClientCredentialsClient.java +++ b/seed/java-sdk/oauth-client-credentials/oauth-client-credentials/src/main/java/com/seed/oauthClientCredentials/AsyncSeedOauthClientCredentialsClient.java @@ -46,7 +46,23 @@ public AsyncSimpleClient simple() { return this.simpleClient.get(); } - public static AsyncSeedOauthClientCredentialsClientBuilder builder() { - return new AsyncSeedOauthClientCredentialsClientBuilder(); + /** + * Creates a client builder using a pre-generated access token. + * @param token The access token to use for authentication + * @return A builder configured for token authentication + */ + public static AsyncSeedOauthClientCredentialsClientBuilder._TokenAuth withToken(String token) { + return AsyncSeedOauthClientCredentialsClientBuilder.withToken(token); + } + + /** + * Creates a client builder using OAuth client credentials. + * @param clientId The OAuth client ID + * @param clientSecret The OAuth client secret + * @return A builder configured for OAuth authentication + */ + public static AsyncSeedOauthClientCredentialsClientBuilder._CredentialsAuth withCredentials( + String clientId, String clientSecret) { + return AsyncSeedOauthClientCredentialsClientBuilder.withCredentials(clientId, clientSecret); } } diff --git a/seed/java-sdk/oauth-client-credentials/oauth-client-credentials/src/main/java/com/seed/oauthClientCredentials/AsyncSeedOauthClientCredentialsClientBuilder.java b/seed/java-sdk/oauth-client-credentials/oauth-client-credentials/src/main/java/com/seed/oauthClientCredentials/AsyncSeedOauthClientCredentialsClientBuilder.java index 570898df1e2e..8978b10154c9 100644 --- a/seed/java-sdk/oauth-client-credentials/oauth-client-credentials/src/main/java/com/seed/oauthClientCredentials/AsyncSeedOauthClientCredentialsClientBuilder.java +++ b/seed/java-sdk/oauth-client-credentials/oauth-client-credentials/src/main/java/com/seed/oauthClientCredentials/AsyncSeedOauthClientCredentialsClientBuilder.java @@ -19,58 +19,32 @@ public class AsyncSeedOauthClientCredentialsClientBuilder { private final Map customHeaders = new HashMap<>(); - private String clientId = null; - - private String clientSecret = null; - - private String audience = null; - - private String grantType = null; - - private String scope = null; - - private Environment environment; + protected Environment environment; private OkHttpClient httpClient; /** - * Sets clientId - */ - public AsyncSeedOauthClientCredentialsClientBuilder clientId(String clientId) { - this.clientId = clientId; - return this; - } - - /** - * Sets clientSecret - */ - public AsyncSeedOauthClientCredentialsClientBuilder clientSecret(String clientSecret) { - this.clientSecret = clientSecret; - return this; - } - - /** - * Sets audience + * Creates a builder that uses a pre-generated access token for authentication. + * Use this when you already have a valid access token and want to bypass + * the OAuth client credentials flow. + * + * @param token The access token to use for Authorization header + * @return A builder configured for token authentication */ - public AsyncSeedOauthClientCredentialsClientBuilder audience(String audience) { - this.audience = audience; - return this; + public static _TokenAuth withToken(String token) { + return new _TokenAuth(token); } /** - * Sets grantType + * Creates a builder that uses OAuth client credentials for authentication. + * The builder will automatically handle token acquisition and refresh. + * + * @param clientId The OAuth client ID + * @param clientSecret The OAuth client secret + * @return A builder configured for OAuth client credentials authentication */ - public AsyncSeedOauthClientCredentialsClientBuilder grantType(String grantType) { - this.grantType = grantType; - return this; - } - - /** - * Sets scope - */ - public AsyncSeedOauthClientCredentialsClientBuilder scope(String scope) { - this.scope = scope; - return this; + public static _CredentialsAuth withCredentials(String clientId, String clientSecret) { + return new _CredentialsAuth(clientId, clientSecret); } public AsyncSeedOauthClientCredentialsClientBuilder url(String url) { @@ -154,16 +128,7 @@ protected void setEnvironment(ClientOptions.Builder builder) { * } * } */ - protected void setAuthentication(ClientOptions.Builder builder) { - if (this.clientId != null && this.clientSecret != null) { - ClientOptions.Builder authClientOptionsBuilder = - ClientOptions.builder().environment(this.environment); - AuthClient authClient = new AuthClient(authClientOptionsBuilder.build()); - OAuthTokenSupplier oAuthTokenSupplier = new OAuthTokenSupplier( - this.clientId, this.clientSecret, this.audience, this.grantType, this.scope, authClient); - builder.addHeader("Authorization", oAuthTokenSupplier); - } - } + protected void setAuthentication(ClientOptions.Builder builder) {} /** * Sets the request timeout configuration. @@ -241,4 +206,59 @@ public AsyncSeedOauthClientCredentialsClient build() { validateConfiguration(); return new AsyncSeedOauthClientCredentialsClient(buildClientOptions()); } + + public static final class _TokenAuth extends AsyncSeedOauthClientCredentialsClientBuilder { + private final String token; + + _TokenAuth(String token) { + this.token = token; + } + + @Override + protected void setAuthentication(ClientOptions.Builder builder) { + builder.addHeader("Authorization", "Bearer " + this.token); + } + } + + public static final class _CredentialsAuth extends AsyncSeedOauthClientCredentialsClientBuilder { + private final String clientId; + + private final String clientSecret; + + private String audience = null; + + private String grantType = null; + + private String scope = null; + + _CredentialsAuth(String clientId, String clientSecret) { + this.clientId = clientId; + this.clientSecret = clientSecret; + } + + public _CredentialsAuth audience(String audience) { + this.audience = audience; + return this; + } + + public _CredentialsAuth grantType(String grantType) { + this.grantType = grantType; + return this; + } + + public _CredentialsAuth scope(String scope) { + this.scope = scope; + return this; + } + + @Override + protected void setAuthentication(ClientOptions.Builder builder) { + ClientOptions.Builder authClientOptionsBuilder = + ClientOptions.builder().environment(this.environment); + AuthClient authClient = new AuthClient(authClientOptionsBuilder.build()); + OAuthTokenSupplier oAuthTokenSupplier = new OAuthTokenSupplier( + this.clientId, this.clientSecret, this.audience, this.grantType, this.scope, authClient); + builder.addHeader("Authorization", oAuthTokenSupplier); + } + } } diff --git a/seed/java-sdk/oauth-client-credentials/oauth-client-credentials/src/main/java/com/seed/oauthClientCredentials/SeedOauthClientCredentialsClient.java b/seed/java-sdk/oauth-client-credentials/oauth-client-credentials/src/main/java/com/seed/oauthClientCredentials/SeedOauthClientCredentialsClient.java index 3889ea39460b..67b12bf426bd 100644 --- a/seed/java-sdk/oauth-client-credentials/oauth-client-credentials/src/main/java/com/seed/oauthClientCredentials/SeedOauthClientCredentialsClient.java +++ b/seed/java-sdk/oauth-client-credentials/oauth-client-credentials/src/main/java/com/seed/oauthClientCredentials/SeedOauthClientCredentialsClient.java @@ -46,7 +46,23 @@ public SimpleClient simple() { return this.simpleClient.get(); } - public static SeedOauthClientCredentialsClientBuilder builder() { - return new SeedOauthClientCredentialsClientBuilder(); + /** + * Creates a client builder using a pre-generated access token. + * @param token The access token to use for authentication + * @return A builder configured for token authentication + */ + public static SeedOauthClientCredentialsClientBuilder._TokenAuth withToken(String token) { + return SeedOauthClientCredentialsClientBuilder.withToken(token); + } + + /** + * Creates a client builder using OAuth client credentials. + * @param clientId The OAuth client ID + * @param clientSecret The OAuth client secret + * @return A builder configured for OAuth authentication + */ + public static SeedOauthClientCredentialsClientBuilder._CredentialsAuth withCredentials( + String clientId, String clientSecret) { + return SeedOauthClientCredentialsClientBuilder.withCredentials(clientId, clientSecret); } } diff --git a/seed/java-sdk/oauth-client-credentials/oauth-client-credentials/src/main/java/com/seed/oauthClientCredentials/SeedOauthClientCredentialsClientBuilder.java b/seed/java-sdk/oauth-client-credentials/oauth-client-credentials/src/main/java/com/seed/oauthClientCredentials/SeedOauthClientCredentialsClientBuilder.java index f435aaf77d81..4cfd31292e9f 100644 --- a/seed/java-sdk/oauth-client-credentials/oauth-client-credentials/src/main/java/com/seed/oauthClientCredentials/SeedOauthClientCredentialsClientBuilder.java +++ b/seed/java-sdk/oauth-client-credentials/oauth-client-credentials/src/main/java/com/seed/oauthClientCredentials/SeedOauthClientCredentialsClientBuilder.java @@ -19,58 +19,32 @@ public class SeedOauthClientCredentialsClientBuilder { private final Map customHeaders = new HashMap<>(); - private String clientId = null; - - private String clientSecret = null; - - private String audience = null; - - private String grantType = null; - - private String scope = null; - - private Environment environment; + protected Environment environment; private OkHttpClient httpClient; /** - * Sets clientId - */ - public SeedOauthClientCredentialsClientBuilder clientId(String clientId) { - this.clientId = clientId; - return this; - } - - /** - * Sets clientSecret - */ - public SeedOauthClientCredentialsClientBuilder clientSecret(String clientSecret) { - this.clientSecret = clientSecret; - return this; - } - - /** - * Sets audience + * Creates a builder that uses a pre-generated access token for authentication. + * Use this when you already have a valid access token and want to bypass + * the OAuth client credentials flow. + * + * @param token The access token to use for Authorization header + * @return A builder configured for token authentication */ - public SeedOauthClientCredentialsClientBuilder audience(String audience) { - this.audience = audience; - return this; + public static _TokenAuth withToken(String token) { + return new _TokenAuth(token); } /** - * Sets grantType + * Creates a builder that uses OAuth client credentials for authentication. + * The builder will automatically handle token acquisition and refresh. + * + * @param clientId The OAuth client ID + * @param clientSecret The OAuth client secret + * @return A builder configured for OAuth client credentials authentication */ - public SeedOauthClientCredentialsClientBuilder grantType(String grantType) { - this.grantType = grantType; - return this; - } - - /** - * Sets scope - */ - public SeedOauthClientCredentialsClientBuilder scope(String scope) { - this.scope = scope; - return this; + public static _CredentialsAuth withCredentials(String clientId, String clientSecret) { + return new _CredentialsAuth(clientId, clientSecret); } public SeedOauthClientCredentialsClientBuilder url(String url) { @@ -154,16 +128,7 @@ protected void setEnvironment(ClientOptions.Builder builder) { * } * } */ - protected void setAuthentication(ClientOptions.Builder builder) { - if (this.clientId != null && this.clientSecret != null) { - ClientOptions.Builder authClientOptionsBuilder = - ClientOptions.builder().environment(this.environment); - AuthClient authClient = new AuthClient(authClientOptionsBuilder.build()); - OAuthTokenSupplier oAuthTokenSupplier = new OAuthTokenSupplier( - this.clientId, this.clientSecret, this.audience, this.grantType, this.scope, authClient); - builder.addHeader("Authorization", oAuthTokenSupplier); - } - } + protected void setAuthentication(ClientOptions.Builder builder) {} /** * Sets the request timeout configuration. @@ -241,4 +206,59 @@ public SeedOauthClientCredentialsClient build() { validateConfiguration(); return new SeedOauthClientCredentialsClient(buildClientOptions()); } + + public static final class _TokenAuth extends SeedOauthClientCredentialsClientBuilder { + private final String token; + + _TokenAuth(String token) { + this.token = token; + } + + @Override + protected void setAuthentication(ClientOptions.Builder builder) { + builder.addHeader("Authorization", "Bearer " + this.token); + } + } + + public static final class _CredentialsAuth extends SeedOauthClientCredentialsClientBuilder { + private final String clientId; + + private final String clientSecret; + + private String audience = null; + + private String grantType = null; + + private String scope = null; + + _CredentialsAuth(String clientId, String clientSecret) { + this.clientId = clientId; + this.clientSecret = clientSecret; + } + + public _CredentialsAuth audience(String audience) { + this.audience = audience; + return this; + } + + public _CredentialsAuth grantType(String grantType) { + this.grantType = grantType; + return this; + } + + public _CredentialsAuth scope(String scope) { + this.scope = scope; + return this; + } + + @Override + protected void setAuthentication(ClientOptions.Builder builder) { + ClientOptions.Builder authClientOptionsBuilder = + ClientOptions.builder().environment(this.environment); + AuthClient authClient = new AuthClient(authClientOptionsBuilder.build()); + OAuthTokenSupplier oAuthTokenSupplier = new OAuthTokenSupplier( + this.clientId, this.clientSecret, this.audience, this.grantType, this.scope, authClient); + builder.addHeader("Authorization", oAuthTokenSupplier); + } + } } diff --git a/seed/java-sdk/optional/wire-tests/README.md b/seed/java-sdk/optional/wire-tests/README.md index 3451a903e90d..c80797802f63 100644 --- a/seed/java-sdk/optional/wire-tests/README.md +++ b/seed/java-sdk/optional/wire-tests/README.md @@ -152,7 +152,6 @@ SeedObjectsWithImportsClient client = SeedObjectsWithImportsClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.objectsWithImports.SeedObjectsWithImportsClient; import com.seed.objectsWithImports.core.RequestOptions; @@ -160,7 +159,7 @@ import com.seed.objectsWithImports.core.RequestOptions; // Client level SeedObjectsWithImportsClient client = SeedObjectsWithImportsClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -168,7 +167,7 @@ client.optional().sendOptionalBody( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/package-yml/README.md b/seed/java-sdk/package-yml/README.md index c1b87c3f7a18..e457a7bd495b 100644 --- a/seed/java-sdk/package-yml/README.md +++ b/seed/java-sdk/package-yml/README.md @@ -150,7 +150,6 @@ SeedPackageYmlClient client = SeedPackageYmlClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.packageYml.SeedPackageYmlClient; import com.seed.packageYml.core.RequestOptions; @@ -158,7 +157,7 @@ import com.seed.packageYml.core.RequestOptions; // Client level SeedPackageYmlClient client = SeedPackageYmlClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -166,7 +165,7 @@ client.echo( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/pagination/default/README.md b/seed/java-sdk/pagination/default/README.md index f387eb18e8f9..5e4fbb888dc2 100644 --- a/seed/java-sdk/pagination/default/README.md +++ b/seed/java-sdk/pagination/default/README.md @@ -211,7 +211,6 @@ SeedPaginationClient client = SeedPaginationClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.pagination.SeedPaginationClient; import com.seed.pagination.core.RequestOptions; @@ -219,7 +218,7 @@ import com.seed.pagination.core.RequestOptions; // Client level SeedPaginationClient client = SeedPaginationClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -227,7 +226,7 @@ client.complex().search( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/pagination/default/reference.md b/seed/java-sdk/pagination/default/reference.md index ad2a85139792..9ecc989d5666 100644 --- a/seed/java-sdk/pagination/default/reference.md +++ b/seed/java-sdk/pagination/default/reference.md @@ -1478,6 +1478,51 @@ client.users().listWithGlobalConfig( + + + + +
client.users.listWithOptionalData() -> SyncPagingIterable<User> +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```java +client.users().listWithOptionalData( + ListUsersOptionalDataRequest + .builder() + .page(1) + .build() +); +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**page:** `Optional` — Defaults to first page + +
+
+
+
+ +
diff --git a/seed/java-sdk/pagination/default/snippet.json b/seed/java-sdk/pagination/default/snippet.json index 0377e1c04da6..84685e82fb4c 100644 --- a/seed/java-sdk/pagination/default/snippet.json +++ b/seed/java-sdk/pagination/default/snippet.json @@ -248,7 +248,7 @@ } }, { - "example_identifier": "20792607", + "example_identifier": "3c830777", "id": { "method": "GET", "path": "/users", @@ -337,6 +337,45 @@ "sync_client": "package com.example.usage;\n\nimport com.seed.pagination.SeedPaginationClient;\nimport com.seed.pagination.resources.users.requests.ListWithGlobalConfigRequest;\n\npublic class Example {\n public static void main(String[] args) {\n SeedPaginationClient client = SeedPaginationClient\n .builder()\n .token(\"\")\n .build();\n\n client.users().listWithGlobalConfig(\n ListWithGlobalConfigRequest\n .builder()\n .offset(1)\n .build()\n );\n }\n}\n", "async_client": "package com.example.usage;\n\nimport com.seed.pagination.SeedPaginationClient;\nimport com.seed.pagination.resources.users.requests.ListWithGlobalConfigRequest;\n\npublic class Example {\n public static void main(String[] args) {\n SeedPaginationClient client = SeedPaginationClient\n .builder()\n .token(\"\")\n .build();\n\n client.users().listWithGlobalConfig(\n ListWithGlobalConfigRequest\n .builder()\n .offset(1)\n .build()\n );\n }\n}\n" } + }, + { + "example_identifier": "WithData", + "id": { + "method": "GET", + "path": "/users/optional-data", + "identifier_override": "endpoint_users.listWithOptionalData" + }, + "snippet": { + "type": "java", + "sync_client": "package com.example.usage;\n\nimport com.seed.pagination.SeedPaginationClient;\nimport com.seed.pagination.resources.users.requests.ListUsersOptionalDataRequest;\n\npublic class Example {\n public static void main(String[] args) {\n SeedPaginationClient client = SeedPaginationClient\n .builder()\n .token(\"\")\n .build();\n\n client.users().listWithOptionalData(\n ListUsersOptionalDataRequest\n .builder()\n .page(1)\n .build()\n );\n }\n}\n", + "async_client": "package com.example.usage;\n\nimport com.seed.pagination.SeedPaginationClient;\nimport com.seed.pagination.resources.users.requests.ListUsersOptionalDataRequest;\n\npublic class Example {\n public static void main(String[] args) {\n SeedPaginationClient client = SeedPaginationClient\n .builder()\n .token(\"\")\n .build();\n\n client.users().listWithOptionalData(\n ListUsersOptionalDataRequest\n .builder()\n .page(1)\n .build()\n );\n }\n}\n" + } + }, + { + "example_identifier": "WithoutData", + "id": { + "method": "GET", + "path": "/users/optional-data", + "identifier_override": "endpoint_users.listWithOptionalData" + }, + "snippet": { + "type": "java", + "sync_client": "package com.example.usage;\n\nimport com.seed.pagination.SeedPaginationClient;\nimport com.seed.pagination.resources.users.requests.ListUsersOptionalDataRequest;\n\npublic class Example {\n public static void main(String[] args) {\n SeedPaginationClient client = SeedPaginationClient\n .builder()\n .token(\"\")\n .build();\n\n client.users().listWithOptionalData(\n ListUsersOptionalDataRequest\n .builder()\n .page(1)\n .build()\n );\n }\n}\n", + "async_client": "package com.example.usage;\n\nimport com.seed.pagination.SeedPaginationClient;\nimport com.seed.pagination.resources.users.requests.ListUsersOptionalDataRequest;\n\npublic class Example {\n public static void main(String[] args) {\n SeedPaginationClient client = SeedPaginationClient\n .builder()\n .token(\"\")\n .build();\n\n client.users().listWithOptionalData(\n ListUsersOptionalDataRequest\n .builder()\n .page(1)\n .build()\n );\n }\n}\n" + } + }, + { + "example_identifier": "6b3bf976", + "id": { + "method": "GET", + "path": "/users/optional-data", + "identifier_override": "endpoint_users.listWithOptionalData" + }, + "snippet": { + "type": "java", + "sync_client": "package com.example.usage;\n\nimport com.seed.pagination.SeedPaginationClient;\nimport com.seed.pagination.resources.users.requests.ListUsersOptionalDataRequest;\n\npublic class Example {\n public static void main(String[] args) {\n SeedPaginationClient client = SeedPaginationClient\n .builder()\n .token(\"\")\n .build();\n\n client.users().listWithOptionalData(\n ListUsersOptionalDataRequest\n .builder()\n .page(1)\n .build()\n );\n }\n}\n", + "async_client": "package com.example.usage;\n\nimport com.seed.pagination.SeedPaginationClient;\nimport com.seed.pagination.resources.users.requests.ListUsersOptionalDataRequest;\n\npublic class Example {\n public static void main(String[] args) {\n SeedPaginationClient client = SeedPaginationClient\n .builder()\n .token(\"\")\n .build();\n\n client.users().listWithOptionalData(\n ListUsersOptionalDataRequest\n .builder()\n .page(1)\n .build()\n );\n }\n}\n" + } } ], "types": {} diff --git a/seed/java-sdk/pagination/default/src/main/java/com/seed/pagination/resources/users/AsyncRawUsersClient.java b/seed/java-sdk/pagination/default/src/main/java/com/seed/pagination/resources/users/AsyncRawUsersClient.java index d6ae43d532ea..79e647ef77c5 100644 --- a/seed/java-sdk/pagination/default/src/main/java/com/seed/pagination/resources/users/AsyncRawUsersClient.java +++ b/seed/java-sdk/pagination/default/src/main/java/com/seed/pagination/resources/users/AsyncRawUsersClient.java @@ -25,11 +25,13 @@ import com.seed.pagination.resources.users.requests.ListUsersMixedTypeCursorPaginationRequest; import com.seed.pagination.resources.users.requests.ListUsersOffsetPaginationRequest; import com.seed.pagination.resources.users.requests.ListUsersOffsetStepPaginationRequest; +import com.seed.pagination.resources.users.requests.ListUsersOptionalDataRequest; import com.seed.pagination.resources.users.requests.ListWithGlobalConfigRequest; import com.seed.pagination.resources.users.requests.ListWithOffsetPaginationHasNextPageRequest; import com.seed.pagination.resources.users.types.ListUsersExtendedOptionalListResponse; import com.seed.pagination.resources.users.types.ListUsersExtendedResponse; import com.seed.pagination.resources.users.types.ListUsersMixedTypePaginationResponse; +import com.seed.pagination.resources.users.types.ListUsersOptionalDataPaginationResponse; import com.seed.pagination.resources.users.types.ListUsersPaginationResponse; import com.seed.pagination.resources.users.types.NextPage; import com.seed.pagination.resources.users.types.Page; @@ -1141,4 +1143,81 @@ public void onFailure(@NotNull Call call, @NotNull IOException e) { }); return future; } + + public CompletableFuture>> listWithOptionalData() { + return listWithOptionalData(ListUsersOptionalDataRequest.builder().build()); + } + + public CompletableFuture>> listWithOptionalData( + ListUsersOptionalDataRequest request) { + return listWithOptionalData(request, null); + } + + public CompletableFuture>> listWithOptionalData( + ListUsersOptionalDataRequest request, RequestOptions requestOptions) { + HttpUrl.Builder httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("users") + .addPathSegments("optional-data"); + if (request.getPage().isPresent()) { + QueryStringMapper.addQueryParameter( + httpUrl, "page", request.getPage().get(), false); + } + Request.Builder _requestBuilder = new Request.Builder() + .url(httpUrl.build()) + .method("GET", null) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .addHeader("Accept", "application/json"); + Request okhttpRequest = _requestBuilder.build(); + OkHttpClient client = clientOptions.httpClient(); + if (requestOptions != null && requestOptions.getTimeout().isPresent()) { + client = clientOptions.httpClientWithTimeout(requestOptions); + } + CompletableFuture>> future = new CompletableFuture<>(); + client.newCall(okhttpRequest).enqueue(new Callback() { + @Override + public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { + try (ResponseBody responseBody = response.body()) { + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + if (response.isSuccessful()) { + ListUsersOptionalDataPaginationResponse parsedResponse = ObjectMappers.JSON_MAPPER.readValue( + responseBodyString, ListUsersOptionalDataPaginationResponse.class); + int newPageNumber = request.getPage() + .map((Integer page) -> page + 1) + .orElse(1); + ListUsersOptionalDataRequest nextRequest = ListUsersOptionalDataRequest.builder() + .from(request) + .page(newPageNumber) + .build(); + List result = parsedResponse.getData().orElse(Collections.emptyList()); + future.complete(new SeedPaginationHttpResponse<>( + new SyncPagingIterable(true, result, parsedResponse, () -> { + try { + return listWithOptionalData(nextRequest, requestOptions) + .get() + .body(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + }), + response)); + return; + } + Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); + future.completeExceptionally(new SeedPaginationApiException( + "Error with status code " + response.code(), response.code(), errorBody, response)); + return; + } catch (IOException e) { + future.completeExceptionally( + new SeedPaginationException("Network error executing HTTP request", e)); + } + } + + @Override + public void onFailure(@NotNull Call call, @NotNull IOException e) { + future.completeExceptionally(new SeedPaginationException("Network error executing HTTP request", e)); + } + }); + return future; + } } diff --git a/seed/java-sdk/pagination/default/src/main/java/com/seed/pagination/resources/users/AsyncUsersClient.java b/seed/java-sdk/pagination/default/src/main/java/com/seed/pagination/resources/users/AsyncUsersClient.java index 4e627e1ca7be..6995edd03d59 100644 --- a/seed/java-sdk/pagination/default/src/main/java/com/seed/pagination/resources/users/AsyncUsersClient.java +++ b/seed/java-sdk/pagination/default/src/main/java/com/seed/pagination/resources/users/AsyncUsersClient.java @@ -17,6 +17,7 @@ import com.seed.pagination.resources.users.requests.ListUsersMixedTypeCursorPaginationRequest; import com.seed.pagination.resources.users.requests.ListUsersOffsetPaginationRequest; import com.seed.pagination.resources.users.requests.ListUsersOffsetStepPaginationRequest; +import com.seed.pagination.resources.users.requests.ListUsersOptionalDataRequest; import com.seed.pagination.resources.users.requests.ListWithGlobalConfigRequest; import com.seed.pagination.resources.users.requests.ListWithOffsetPaginationHasNextPageRequest; import com.seed.pagination.resources.users.types.User; @@ -233,4 +234,17 @@ public CompletableFuture> listWithGlobalConfig( ListWithGlobalConfigRequest request, RequestOptions requestOptions) { return this.rawClient.listWithGlobalConfig(request, requestOptions).thenApply(response -> response.body()); } + + public CompletableFuture> listWithOptionalData() { + return this.rawClient.listWithOptionalData().thenApply(response -> response.body()); + } + + public CompletableFuture> listWithOptionalData(ListUsersOptionalDataRequest request) { + return this.rawClient.listWithOptionalData(request).thenApply(response -> response.body()); + } + + public CompletableFuture> listWithOptionalData( + ListUsersOptionalDataRequest request, RequestOptions requestOptions) { + return this.rawClient.listWithOptionalData(request, requestOptions).thenApply(response -> response.body()); + } } diff --git a/seed/java-sdk/pagination/default/src/main/java/com/seed/pagination/resources/users/RawUsersClient.java b/seed/java-sdk/pagination/default/src/main/java/com/seed/pagination/resources/users/RawUsersClient.java index ed813ea75098..fa7ab391d77a 100644 --- a/seed/java-sdk/pagination/default/src/main/java/com/seed/pagination/resources/users/RawUsersClient.java +++ b/seed/java-sdk/pagination/default/src/main/java/com/seed/pagination/resources/users/RawUsersClient.java @@ -25,11 +25,13 @@ import com.seed.pagination.resources.users.requests.ListUsersMixedTypeCursorPaginationRequest; import com.seed.pagination.resources.users.requests.ListUsersOffsetPaginationRequest; import com.seed.pagination.resources.users.requests.ListUsersOffsetStepPaginationRequest; +import com.seed.pagination.resources.users.requests.ListUsersOptionalDataRequest; import com.seed.pagination.resources.users.requests.ListWithGlobalConfigRequest; import com.seed.pagination.resources.users.requests.ListWithOffsetPaginationHasNextPageRequest; import com.seed.pagination.resources.users.types.ListUsersExtendedOptionalListResponse; import com.seed.pagination.resources.users.types.ListUsersExtendedResponse; import com.seed.pagination.resources.users.types.ListUsersMixedTypePaginationResponse; +import com.seed.pagination.resources.users.types.ListUsersOptionalDataPaginationResponse; import com.seed.pagination.resources.users.types.ListUsersPaginationResponse; import com.seed.pagination.resources.users.types.NextPage; import com.seed.pagination.resources.users.types.Page; @@ -871,4 +873,60 @@ public SeedPaginationHttpResponse> listWithGlobalConf throw new SeedPaginationException("Network error executing HTTP request", e); } } + + public SeedPaginationHttpResponse> listWithOptionalData() { + return listWithOptionalData(ListUsersOptionalDataRequest.builder().build()); + } + + public SeedPaginationHttpResponse> listWithOptionalData( + ListUsersOptionalDataRequest request) { + return listWithOptionalData(request, null); + } + + public SeedPaginationHttpResponse> listWithOptionalData( + ListUsersOptionalDataRequest request, RequestOptions requestOptions) { + HttpUrl.Builder httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("users") + .addPathSegments("optional-data"); + if (request.getPage().isPresent()) { + QueryStringMapper.addQueryParameter( + httpUrl, "page", request.getPage().get(), false); + } + Request.Builder _requestBuilder = new Request.Builder() + .url(httpUrl.build()) + .method("GET", null) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .addHeader("Accept", "application/json"); + Request okhttpRequest = _requestBuilder.build(); + OkHttpClient client = clientOptions.httpClient(); + if (requestOptions != null && requestOptions.getTimeout().isPresent()) { + client = clientOptions.httpClientWithTimeout(requestOptions); + } + try (Response response = client.newCall(okhttpRequest).execute()) { + ResponseBody responseBody = response.body(); + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + if (response.isSuccessful()) { + ListUsersOptionalDataPaginationResponse parsedResponse = ObjectMappers.JSON_MAPPER.readValue( + responseBodyString, ListUsersOptionalDataPaginationResponse.class); + int newPageNumber = + request.getPage().map((Integer page) -> page + 1).orElse(1); + ListUsersOptionalDataRequest nextRequest = ListUsersOptionalDataRequest.builder() + .from(request) + .page(newPageNumber) + .build(); + List result = parsedResponse.getData().orElse(Collections.emptyList()); + return new SeedPaginationHttpResponse<>( + new SyncPagingIterable( + true, result, parsedResponse, () -> listWithOptionalData(nextRequest, requestOptions) + .body()), + response); + } + Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); + throw new SeedPaginationApiException( + "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (IOException e) { + throw new SeedPaginationException("Network error executing HTTP request", e); + } + } } diff --git a/seed/java-sdk/pagination/default/src/main/java/com/seed/pagination/resources/users/UsersClient.java b/seed/java-sdk/pagination/default/src/main/java/com/seed/pagination/resources/users/UsersClient.java index 6eefeabad972..1f6b69e7e5a7 100644 --- a/seed/java-sdk/pagination/default/src/main/java/com/seed/pagination/resources/users/UsersClient.java +++ b/seed/java-sdk/pagination/default/src/main/java/com/seed/pagination/resources/users/UsersClient.java @@ -17,6 +17,7 @@ import com.seed.pagination.resources.users.requests.ListUsersMixedTypeCursorPaginationRequest; import com.seed.pagination.resources.users.requests.ListUsersOffsetPaginationRequest; import com.seed.pagination.resources.users.requests.ListUsersOffsetStepPaginationRequest; +import com.seed.pagination.resources.users.requests.ListUsersOptionalDataRequest; import com.seed.pagination.resources.users.requests.ListWithGlobalConfigRequest; import com.seed.pagination.resources.users.requests.ListWithOffsetPaginationHasNextPageRequest; import com.seed.pagination.resources.users.types.User; @@ -225,4 +226,17 @@ public SyncPagingIterable listWithGlobalConfig( ListWithGlobalConfigRequest request, RequestOptions requestOptions) { return this.rawClient.listWithGlobalConfig(request, requestOptions).body(); } + + public SyncPagingIterable listWithOptionalData() { + return this.rawClient.listWithOptionalData().body(); + } + + public SyncPagingIterable listWithOptionalData(ListUsersOptionalDataRequest request) { + return this.rawClient.listWithOptionalData(request).body(); + } + + public SyncPagingIterable listWithOptionalData( + ListUsersOptionalDataRequest request, RequestOptions requestOptions) { + return this.rawClient.listWithOptionalData(request, requestOptions).body(); + } } diff --git a/seed/java-sdk/pagination/default/src/main/java/com/seed/pagination/resources/users/requests/ListUsersOptionalDataRequest.java b/seed/java-sdk/pagination/default/src/main/java/com/seed/pagination/resources/users/requests/ListUsersOptionalDataRequest.java new file mode 100644 index 000000000000..46af9d580e71 --- /dev/null +++ b/seed/java-sdk/pagination/default/src/main/java/com/seed/pagination/resources/users/requests/ListUsersOptionalDataRequest.java @@ -0,0 +1,101 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.pagination.resources.users.requests; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.seed.pagination.core.ObjectMappers; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = ListUsersOptionalDataRequest.Builder.class) +public final class ListUsersOptionalDataRequest { + private final Optional page; + + private final Map additionalProperties; + + private ListUsersOptionalDataRequest(Optional page, Map additionalProperties) { + this.page = page; + this.additionalProperties = additionalProperties; + } + + /** + * @return Defaults to first page + */ + @JsonProperty("page") + public Optional getPage() { + return page; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof ListUsersOptionalDataRequest && equalTo((ListUsersOptionalDataRequest) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(ListUsersOptionalDataRequest other) { + return page.equals(other.page); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.page); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder { + private Optional page = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + public Builder from(ListUsersOptionalDataRequest other) { + page(other.getPage()); + return this; + } + + /** + *

Defaults to first page

+ */ + @JsonSetter(value = "page", nulls = Nulls.SKIP) + public Builder page(Optional page) { + this.page = page; + return this; + } + + public Builder page(Integer page) { + this.page = Optional.ofNullable(page); + return this; + } + + public ListUsersOptionalDataRequest build() { + return new ListUsersOptionalDataRequest(page, additionalProperties); + } + } +} diff --git a/seed/java-sdk/pagination/default/src/main/java/com/seed/pagination/resources/users/types/ListUsersOptionalDataPaginationResponse.java b/seed/java-sdk/pagination/default/src/main/java/com/seed/pagination/resources/users/types/ListUsersOptionalDataPaginationResponse.java new file mode 100644 index 000000000000..7dfc9f69986c --- /dev/null +++ b/seed/java-sdk/pagination/default/src/main/java/com/seed/pagination/resources/users/types/ListUsersOptionalDataPaginationResponse.java @@ -0,0 +1,209 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.pagination.resources.users.types; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.seed.pagination.core.ObjectMappers; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = ListUsersOptionalDataPaginationResponse.Builder.class) +public final class ListUsersOptionalDataPaginationResponse { + private final Optional hasNextPage; + + private final Optional page; + + private final int totalCount; + + private final Optional> data; + + private final Map additionalProperties; + + private ListUsersOptionalDataPaginationResponse( + Optional hasNextPage, + Optional page, + int totalCount, + Optional> data, + Map additionalProperties) { + this.hasNextPage = hasNextPage; + this.page = page; + this.totalCount = totalCount; + this.data = data; + this.additionalProperties = additionalProperties; + } + + @JsonProperty("hasNextPage") + public Optional getHasNextPage() { + return hasNextPage; + } + + @JsonProperty("page") + public Optional getPage() { + return page; + } + + /** + * @return The totall number of /users + */ + @JsonProperty("total_count") + public int getTotalCount() { + return totalCount; + } + + @JsonProperty("data") + public Optional> getData() { + return data; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof ListUsersOptionalDataPaginationResponse + && equalTo((ListUsersOptionalDataPaginationResponse) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(ListUsersOptionalDataPaginationResponse other) { + return hasNextPage.equals(other.hasNextPage) + && page.equals(other.page) + && totalCount == other.totalCount + && data.equals(other.data); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.hasNextPage, this.page, this.totalCount, this.data); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static TotalCountStage builder() { + return new Builder(); + } + + public interface TotalCountStage { + /** + *

The totall number of /users

+ */ + _FinalStage totalCount(int totalCount); + + Builder from(ListUsersOptionalDataPaginationResponse other); + } + + public interface _FinalStage { + ListUsersOptionalDataPaginationResponse build(); + + _FinalStage hasNextPage(Optional hasNextPage); + + _FinalStage hasNextPage(Boolean hasNextPage); + + _FinalStage page(Optional page); + + _FinalStage page(Page page); + + _FinalStage data(Optional> data); + + _FinalStage data(List data); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder implements TotalCountStage, _FinalStage { + private int totalCount; + + private Optional> data = Optional.empty(); + + private Optional page = Optional.empty(); + + private Optional hasNextPage = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + @java.lang.Override + public Builder from(ListUsersOptionalDataPaginationResponse other) { + hasNextPage(other.getHasNextPage()); + page(other.getPage()); + totalCount(other.getTotalCount()); + data(other.getData()); + return this; + } + + /** + *

The totall number of /users

+ *

The totall number of /users

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + @JsonSetter("total_count") + public _FinalStage totalCount(int totalCount) { + this.totalCount = totalCount; + return this; + } + + @java.lang.Override + public _FinalStage data(List data) { + this.data = Optional.ofNullable(data); + return this; + } + + @java.lang.Override + @JsonSetter(value = "data", nulls = Nulls.SKIP) + public _FinalStage data(Optional> data) { + this.data = data; + return this; + } + + @java.lang.Override + public _FinalStage page(Page page) { + this.page = Optional.ofNullable(page); + return this; + } + + @java.lang.Override + @JsonSetter(value = "page", nulls = Nulls.SKIP) + public _FinalStage page(Optional page) { + this.page = page; + return this; + } + + @java.lang.Override + public _FinalStage hasNextPage(Boolean hasNextPage) { + this.hasNextPage = Optional.ofNullable(hasNextPage); + return this; + } + + @java.lang.Override + @JsonSetter(value = "hasNextPage", nulls = Nulls.SKIP) + public _FinalStage hasNextPage(Optional hasNextPage) { + this.hasNextPage = hasNextPage; + return this; + } + + @java.lang.Override + public ListUsersOptionalDataPaginationResponse build() { + return new ListUsersOptionalDataPaginationResponse( + hasNextPage, page, totalCount, data, additionalProperties); + } + } +} diff --git a/seed/java-sdk/pagination/default/src/main/java/com/snippets/Example26.java b/seed/java-sdk/pagination/default/src/main/java/com/snippets/Example26.java new file mode 100644 index 000000000000..692bf3855d25 --- /dev/null +++ b/seed/java-sdk/pagination/default/src/main/java/com/snippets/Example26.java @@ -0,0 +1,17 @@ +package com.snippets; + +import com.seed.pagination.SeedPaginationClient; +import com.seed.pagination.resources.users.requests.ListUsersOptionalDataRequest; + +public class Example26 { + public static void main(String[] args) { + SeedPaginationClient client = SeedPaginationClient.builder() + .token("") + .url("https://api.fern.com") + .build(); + + client.users() + .listWithOptionalData( + ListUsersOptionalDataRequest.builder().page(1).build()); + } +} diff --git a/seed/java-sdk/pagination/default/src/main/java/com/snippets/Example27.java b/seed/java-sdk/pagination/default/src/main/java/com/snippets/Example27.java new file mode 100644 index 000000000000..5166b82a7072 --- /dev/null +++ b/seed/java-sdk/pagination/default/src/main/java/com/snippets/Example27.java @@ -0,0 +1,17 @@ +package com.snippets; + +import com.seed.pagination.SeedPaginationClient; +import com.seed.pagination.resources.users.requests.ListUsersOptionalDataRequest; + +public class Example27 { + public static void main(String[] args) { + SeedPaginationClient client = SeedPaginationClient.builder() + .token("") + .url("https://api.fern.com") + .build(); + + client.users() + .listWithOptionalData( + ListUsersOptionalDataRequest.builder().page(1).build()); + } +} diff --git a/seed/java-sdk/pagination/default/src/main/java/com/snippets/Example28.java b/seed/java-sdk/pagination/default/src/main/java/com/snippets/Example28.java new file mode 100644 index 000000000000..efa380e1f2b4 --- /dev/null +++ b/seed/java-sdk/pagination/default/src/main/java/com/snippets/Example28.java @@ -0,0 +1,17 @@ +package com.snippets; + +import com.seed.pagination.SeedPaginationClient; +import com.seed.pagination.resources.users.requests.ListUsersOptionalDataRequest; + +public class Example28 { + public static void main(String[] args) { + SeedPaginationClient client = SeedPaginationClient.builder() + .token("") + .url("https://api.fern.com") + .build(); + + client.users() + .listWithOptionalData( + ListUsersOptionalDataRequest.builder().page(1).build()); + } +} diff --git a/seed/java-sdk/pagination/default/src/test/java/com/seed/pagination/UsersWireTest.java b/seed/java-sdk/pagination/default/src/test/java/com/seed/pagination/UsersWireTest.java index e0128009e89e..0f8f019897ec 100644 --- a/seed/java-sdk/pagination/default/src/test/java/com/seed/pagination/UsersWireTest.java +++ b/seed/java-sdk/pagination/default/src/test/java/com/seed/pagination/UsersWireTest.java @@ -11,6 +11,7 @@ import com.seed.pagination.resources.users.requests.ListUsersExtendedRequest; import com.seed.pagination.resources.users.requests.ListUsersMixedTypeCursorPaginationRequest; import com.seed.pagination.resources.users.requests.ListUsersOffsetStepPaginationRequest; +import com.seed.pagination.resources.users.requests.ListUsersOptionalDataRequest; import com.seed.pagination.resources.users.requests.ListWithGlobalConfigRequest; import com.seed.pagination.resources.users.types.Order; import com.seed.pagination.resources.users.types.User; @@ -379,6 +380,26 @@ public void testListWithGlobalConfig() throws Exception { // The SDK correctly parses the response into a SyncPagingIterable } + @Test + public void testListWithOptionalData() throws Exception { + server.enqueue( + new MockResponse() + .setResponseCode(200) + .setBody( + "{\"hasNextPage\":true,\"page\":{\"page\":1,\"next\":{\"page\":2,\"starting_after\":\"next_cursor\"},\"per_page\":10,\"total_page\":5},\"total_count\":50,\"data\":[{\"name\":\"Alice\",\"id\":1},{\"name\":\"Bob\",\"id\":2}]}")); + SyncPagingIterable response = client.users() + .listWithOptionalData( + ListUsersOptionalDataRequest.builder().page(1).build()); + RecordedRequest request = server.takeRequest(); + Assertions.assertNotNull(request); + Assertions.assertEquals("GET", request.getMethod()); + + // Validate response body + Assertions.assertNotNull(response, "Response should not be null"); + // Pagination response validated via MockWebServer + // The SDK correctly parses the response into a SyncPagingIterable + } + /** * Compares two JsonNodes with numeric equivalence and null safety. * For objects, checks that all fields in 'expected' exist in 'actual' with matching values. diff --git a/seed/java-sdk/path-parameters/README.md b/seed/java-sdk/path-parameters/README.md index 236c3ec0f72b..40c50878ec6f 100644 --- a/seed/java-sdk/path-parameters/README.md +++ b/seed/java-sdk/path-parameters/README.md @@ -153,7 +153,6 @@ SeedPathParametersClient client = SeedPathParametersClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.pathParameters.SeedPathParametersClient; import com.seed.pathParameters.core.RequestOptions; @@ -161,7 +160,7 @@ import com.seed.pathParameters.core.RequestOptions; // Client level SeedPathParametersClient client = SeedPathParametersClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -169,7 +168,7 @@ client.user().createUser( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/plain-text/README.md b/seed/java-sdk/plain-text/README.md index d280915130f4..57a2e23db678 100644 --- a/seed/java-sdk/plain-text/README.md +++ b/seed/java-sdk/plain-text/README.md @@ -142,7 +142,6 @@ SeedPlainTextClient client = SeedPlainTextClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.plainText.SeedPlainTextClient; import com.seed.plainText.core.RequestOptions; @@ -150,7 +149,7 @@ import com.seed.plainText.core.RequestOptions; // Client level SeedPlainTextClient client = SeedPlainTextClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -158,7 +157,7 @@ client.service().getText( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/property-access/README.md b/seed/java-sdk/property-access/README.md index 6f4369fbeb04..d15b3646d873 100644 --- a/seed/java-sdk/property-access/README.md +++ b/seed/java-sdk/property-access/README.md @@ -165,7 +165,6 @@ SeedPropertyAccessClient client = SeedPropertyAccessClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.propertyAccess.SeedPropertyAccessClient; import com.seed.propertyAccess.core.RequestOptions; @@ -173,7 +172,7 @@ import com.seed.propertyAccess.core.RequestOptions; // Client level SeedPropertyAccessClient client = SeedPropertyAccessClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -181,7 +180,7 @@ client.createUser( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/public-object/README.md b/seed/java-sdk/public-object/README.md index 6242c17d0422..b24dfcf9ad92 100644 --- a/seed/java-sdk/public-object/README.md +++ b/seed/java-sdk/public-object/README.md @@ -138,7 +138,6 @@ SeedPublicObjectClient client = SeedPublicObjectClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.publicObject.SeedPublicObjectClient; import com.seed.publicObject.core.RequestOptions; @@ -146,7 +145,7 @@ import com.seed.publicObject.core.RequestOptions; // Client level SeedPublicObjectClient client = SeedPublicObjectClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -154,7 +153,7 @@ client.service().get( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/query-parameters-openapi-as-objects/README.md b/seed/java-sdk/query-parameters-openapi-as-objects/README.md index c664a82f4aa7..7627e1833677 100644 --- a/seed/java-sdk/query-parameters-openapi-as-objects/README.md +++ b/seed/java-sdk/query-parameters-openapi-as-objects/README.md @@ -261,7 +261,6 @@ SeedApiClient client = SeedApiClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.api.SeedApiClient; import com.seed.api.core.RequestOptions; @@ -269,7 +268,7 @@ import com.seed.api.core.RequestOptions; // Client level SeedApiClient client = SeedApiClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -277,7 +276,7 @@ client.search( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/query-parameters-openapi/README.md b/seed/java-sdk/query-parameters-openapi/README.md index 40157ff8f2b7..a666cc3eea6f 100644 --- a/seed/java-sdk/query-parameters-openapi/README.md +++ b/seed/java-sdk/query-parameters-openapi/README.md @@ -261,7 +261,6 @@ SeedApiClient client = SeedApiClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.api.SeedApiClient; import com.seed.api.core.RequestOptions; @@ -269,7 +268,7 @@ import com.seed.api.core.RequestOptions; // Client level SeedApiClient client = SeedApiClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -277,7 +276,7 @@ client.search( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/query-parameters/README.md b/seed/java-sdk/query-parameters/README.md index 6cf58443c2d3..ce5dc34dd08e 100644 --- a/seed/java-sdk/query-parameters/README.md +++ b/seed/java-sdk/query-parameters/README.md @@ -230,7 +230,6 @@ SeedQueryParametersClient client = SeedQueryParametersClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.queryParameters.SeedQueryParametersClient; import com.seed.queryParameters.core.RequestOptions; @@ -238,7 +237,7 @@ import com.seed.queryParameters.core.RequestOptions; // Client level SeedQueryParametersClient client = SeedQueryParametersClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -246,7 +245,7 @@ client.user().getUsername( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/request-parameters/README.md b/seed/java-sdk/request-parameters/README.md index 62a60197b8c6..5cb0b5626910 100644 --- a/seed/java-sdk/request-parameters/README.md +++ b/seed/java-sdk/request-parameters/README.md @@ -154,7 +154,6 @@ SeedRequestParametersClient client = SeedRequestParametersClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.requestParameters.SeedRequestParametersClient; import com.seed.requestParameters.core.RequestOptions; @@ -162,7 +161,7 @@ import com.seed.requestParameters.core.RequestOptions; // Client level SeedRequestParametersClient client = SeedRequestParametersClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -170,7 +169,7 @@ client.user().createUsername( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/required-nullable/README.md b/seed/java-sdk/required-nullable/README.md index b8780fbc09d3..976c8813a4e5 100644 --- a/seed/java-sdk/required-nullable/README.md +++ b/seed/java-sdk/required-nullable/README.md @@ -151,7 +151,6 @@ SeedApiClient client = SeedApiClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.api.SeedApiClient; import com.seed.api.core.RequestOptions; @@ -159,7 +158,7 @@ import com.seed.api.core.RequestOptions; // Client level SeedApiClient client = SeedApiClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -167,7 +166,7 @@ client.getFoo( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/reserved-keywords/README.md b/seed/java-sdk/reserved-keywords/README.md index f6e14bf62a6c..3ea65b9a26b5 100644 --- a/seed/java-sdk/reserved-keywords/README.md +++ b/seed/java-sdk/reserved-keywords/README.md @@ -148,7 +148,6 @@ SeedNurseryApiClient client = SeedNurseryApiClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.nurseryApi.SeedNurseryApiClient; import com.seed.nurseryApi.core.RequestOptions; @@ -156,7 +155,7 @@ import com.seed.nurseryApi.core.RequestOptions; // Client level SeedNurseryApiClient client = SeedNurseryApiClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -164,7 +163,7 @@ client.package_().test( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/response-property/README.md b/seed/java-sdk/response-property/README.md index fb40a4fae57d..1b1fdaeb2a59 100644 --- a/seed/java-sdk/response-property/README.md +++ b/seed/java-sdk/response-property/README.md @@ -142,7 +142,6 @@ SeedResponsePropertyClient client = SeedResponsePropertyClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.responseProperty.SeedResponsePropertyClient; import com.seed.responseProperty.core.RequestOptions; @@ -150,7 +149,7 @@ import com.seed.responseProperty.core.RequestOptions; // Client level SeedResponsePropertyClient client = SeedResponsePropertyClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -158,7 +157,7 @@ client.service().getMovie( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/server-sent-event-examples/README.md b/seed/java-sdk/server-sent-event-examples/README.md index c40d9a924445..035118dfbea3 100644 --- a/seed/java-sdk/server-sent-event-examples/README.md +++ b/seed/java-sdk/server-sent-event-examples/README.md @@ -148,7 +148,6 @@ SeedServerSentEventsClient client = SeedServerSentEventsClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.serverSentEvents.SeedServerSentEventsClient; import com.seed.serverSentEvents.core.RequestOptions; @@ -156,7 +155,7 @@ import com.seed.serverSentEvents.core.RequestOptions; // Client level SeedServerSentEventsClient client = SeedServerSentEventsClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -164,7 +163,7 @@ client.completions().stream( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/server-sent-events/README.md b/seed/java-sdk/server-sent-events/README.md index b0c372dc85d5..4e472aefe4b0 100644 --- a/seed/java-sdk/server-sent-events/README.md +++ b/seed/java-sdk/server-sent-events/README.md @@ -148,7 +148,6 @@ SeedServerSentEventsClient client = SeedServerSentEventsClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.serverSentEvents.SeedServerSentEventsClient; import com.seed.serverSentEvents.core.RequestOptions; @@ -156,7 +155,7 @@ import com.seed.serverSentEvents.core.RequestOptions; // Client level SeedServerSentEventsClient client = SeedServerSentEventsClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -164,7 +163,7 @@ client.completions().stream( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/simple-api/README.md b/seed/java-sdk/simple-api/README.md index 7a118ddcff7d..377675e0c3d7 100644 --- a/seed/java-sdk/simple-api/README.md +++ b/seed/java-sdk/simple-api/README.md @@ -158,7 +158,6 @@ SeedSimpleApiClient client = SeedSimpleApiClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.simpleApi.SeedSimpleApiClient; import com.seed.simpleApi.core.RequestOptions; @@ -166,7 +165,7 @@ import com.seed.simpleApi.core.RequestOptions; // Client level SeedSimpleApiClient client = SeedSimpleApiClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -174,7 +173,7 @@ client.user().get( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/simple-fhir/README.md b/seed/java-sdk/simple-fhir/README.md index 826a70a57c48..d7933327102e 100644 --- a/seed/java-sdk/simple-fhir/README.md +++ b/seed/java-sdk/simple-fhir/README.md @@ -142,7 +142,6 @@ SeedApiClient client = SeedApiClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.api.SeedApiClient; import com.seed.api.core.RequestOptions; @@ -150,7 +149,7 @@ import com.seed.api.core.RequestOptions; // Client level SeedApiClient client = SeedApiClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -158,7 +157,7 @@ client.getAccount( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/single-url-environment-default/README.md b/seed/java-sdk/single-url-environment-default/README.md index 4b1e03584e2a..92809854093a 100644 --- a/seed/java-sdk/single-url-environment-default/README.md +++ b/seed/java-sdk/single-url-environment-default/README.md @@ -158,7 +158,6 @@ SeedSingleUrlEnvironmentDefaultClient client = SeedSingleUrlEnvironmentDefaultCl ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.singleUrlEnvironmentDefault.SeedSingleUrlEnvironmentDefaultClient; import com.seed.singleUrlEnvironmentDefault.core.RequestOptions; @@ -166,7 +165,7 @@ import com.seed.singleUrlEnvironmentDefault.core.RequestOptions; // Client level SeedSingleUrlEnvironmentDefaultClient client = SeedSingleUrlEnvironmentDefaultClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -174,7 +173,7 @@ client.dummy().getDummy( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/single-url-environment-no-default/README.md b/seed/java-sdk/single-url-environment-no-default/README.md index 7fee5a388bf3..67c193ff33f7 100644 --- a/seed/java-sdk/single-url-environment-no-default/README.md +++ b/seed/java-sdk/single-url-environment-no-default/README.md @@ -158,7 +158,6 @@ SeedSingleUrlEnvironmentNoDefaultClient client = SeedSingleUrlEnvironmentNoDefau ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.singleUrlEnvironmentNoDefault.SeedSingleUrlEnvironmentNoDefaultClient; import com.seed.singleUrlEnvironmentNoDefault.core.RequestOptions; @@ -166,7 +165,7 @@ import com.seed.singleUrlEnvironmentNoDefault.core.RequestOptions; // Client level SeedSingleUrlEnvironmentNoDefaultClient client = SeedSingleUrlEnvironmentNoDefaultClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -174,7 +173,7 @@ client.dummy().getDummy( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/streaming/streaming/README.md b/seed/java-sdk/streaming/streaming/README.md index 40bae0cd52b7..b0d09093614c 100644 --- a/seed/java-sdk/streaming/streaming/README.md +++ b/seed/java-sdk/streaming/streaming/README.md @@ -148,7 +148,6 @@ SeedStreamingClient client = SeedStreamingClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.streaming.SeedStreamingClient; import com.seed.streaming.core.RequestOptions; @@ -156,7 +155,7 @@ import com.seed.streaming.core.RequestOptions; // Client level SeedStreamingClient client = SeedStreamingClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -164,7 +163,7 @@ client.dummy().generateStream( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/trace/README.md b/seed/java-sdk/trace/README.md index c44548399016..a8bf666342bb 100644 --- a/seed/java-sdk/trace/README.md +++ b/seed/java-sdk/trace/README.md @@ -163,7 +163,6 @@ SeedTraceClient client = SeedTraceClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.trace.SeedTraceClient; import com.seed.trace.core.RequestOptions; @@ -171,7 +170,7 @@ import com.seed.trace.core.RequestOptions; // Client level SeedTraceClient client = SeedTraceClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -179,7 +178,7 @@ client.admin().updateTestSubmissionStatus( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/undiscriminated-union-with-response-property/README.md b/seed/java-sdk/undiscriminated-union-with-response-property/README.md index b269a810558d..40c69207e8d5 100644 --- a/seed/java-sdk/undiscriminated-union-with-response-property/README.md +++ b/seed/java-sdk/undiscriminated-union-with-response-property/README.md @@ -142,7 +142,6 @@ SeedUndiscriminatedUnionWithResponsePropertyClient client = SeedUndiscriminatedU ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.undiscriminatedUnionWithResponseProperty.SeedUndiscriminatedUnionWithResponsePropertyClient; import com.seed.undiscriminatedUnionWithResponseProperty.core.RequestOptions; @@ -150,7 +149,7 @@ import com.seed.undiscriminatedUnionWithResponseProperty.core.RequestOptions; // Client level SeedUndiscriminatedUnionWithResponsePropertyClient client = SeedUndiscriminatedUnionWithResponsePropertyClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -158,7 +157,7 @@ client.getUnion( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/undiscriminated-unions/README.md b/seed/java-sdk/undiscriminated-unions/README.md index 61bdc27f29c5..497e159cfc63 100644 --- a/seed/java-sdk/undiscriminated-unions/README.md +++ b/seed/java-sdk/undiscriminated-unions/README.md @@ -145,7 +145,6 @@ SeedUndiscriminatedUnionsClient client = SeedUndiscriminatedUnionsClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.undiscriminatedUnions.SeedUndiscriminatedUnionsClient; import com.seed.undiscriminatedUnions.core.RequestOptions; @@ -153,7 +152,7 @@ import com.seed.undiscriminatedUnions.core.RequestOptions; // Client level SeedUndiscriminatedUnionsClient client = SeedUndiscriminatedUnionsClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -161,7 +160,7 @@ client.union().get( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/unions/default/README.md b/seed/java-sdk/unions/default/README.md index 09c491ee5e60..6d6975516ed8 100644 --- a/seed/java-sdk/unions/default/README.md +++ b/seed/java-sdk/unions/default/README.md @@ -142,7 +142,6 @@ SeedUnionsClient client = SeedUnionsClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.unions.SeedUnionsClient; import com.seed.unions.core.RequestOptions; @@ -150,7 +149,7 @@ import com.seed.unions.core.RequestOptions; // Client level SeedUnionsClient client = SeedUnionsClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -158,7 +157,7 @@ client.bigunion().get( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/unknown/README.md b/seed/java-sdk/unknown/README.md index db25c11e6860..352e5688d402 100644 --- a/seed/java-sdk/unknown/README.md +++ b/seed/java-sdk/unknown/README.md @@ -145,7 +145,6 @@ SeedUnknownAsAnyClient client = SeedUnknownAsAnyClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.unknownAsAny.SeedUnknownAsAnyClient; import com.seed.unknownAsAny.core.RequestOptions; @@ -153,7 +152,7 @@ import com.seed.unknownAsAny.core.RequestOptions; // Client level SeedUnknownAsAnyClient client = SeedUnknownAsAnyClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -161,7 +160,7 @@ client.unknown().post( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/url-form-encoded/README.md b/seed/java-sdk/url-form-encoded/README.md index d189384b333d..4bc2e5456128 100644 --- a/seed/java-sdk/url-form-encoded/README.md +++ b/seed/java-sdk/url-form-encoded/README.md @@ -149,7 +149,6 @@ SeedApiClient client = SeedApiClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.api.SeedApiClient; import com.seed.api.core.RequestOptions; @@ -157,7 +156,7 @@ import com.seed.api.core.RequestOptions; // Client level SeedApiClient client = SeedApiClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -165,7 +164,7 @@ client.submitFormData( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/validation/README.md b/seed/java-sdk/validation/README.md index 925fcc0b3c44..f0664cccbee8 100644 --- a/seed/java-sdk/validation/README.md +++ b/seed/java-sdk/validation/README.md @@ -152,7 +152,6 @@ SeedValidationClient client = SeedValidationClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.validation.SeedValidationClient; import com.seed.validation.core.RequestOptions; @@ -160,7 +159,7 @@ import com.seed.validation.core.RequestOptions; // Client level SeedValidationClient client = SeedValidationClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -168,7 +167,7 @@ client.create( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/variables/README.md b/seed/java-sdk/variables/README.md index e83430806bde..b33f5560bf0f 100644 --- a/seed/java-sdk/variables/README.md +++ b/seed/java-sdk/variables/README.md @@ -143,7 +143,6 @@ SeedVariablesClient client = SeedVariablesClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.variables.SeedVariablesClient; import com.seed.variables.core.RequestOptions; @@ -151,7 +150,7 @@ import com.seed.variables.core.RequestOptions; // Client level SeedVariablesClient client = SeedVariablesClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -159,7 +158,7 @@ client.service().post( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/version-no-default/README.md b/seed/java-sdk/version-no-default/README.md index ee7bf6422c44..27311025d2ab 100644 --- a/seed/java-sdk/version-no-default/README.md +++ b/seed/java-sdk/version-no-default/README.md @@ -142,7 +142,6 @@ SeedVersionClient client = SeedVersionClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.version.SeedVersionClient; import com.seed.version.core.RequestOptions; @@ -150,7 +149,7 @@ import com.seed.version.core.RequestOptions; // Client level SeedVersionClient client = SeedVersionClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -158,7 +157,7 @@ client.user().getUser( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/version/forward-compatible-enums/README.md b/seed/java-sdk/version/forward-compatible-enums/README.md index 0ba46b31d555..ab6bd1c3e328 100644 --- a/seed/java-sdk/version/forward-compatible-enums/README.md +++ b/seed/java-sdk/version/forward-compatible-enums/README.md @@ -142,7 +142,6 @@ SeedVersionClient client = SeedVersionClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.version.SeedVersionClient; import com.seed.version.core.RequestOptions; @@ -150,7 +149,7 @@ import com.seed.version.core.RequestOptions; // Client level SeedVersionClient client = SeedVersionClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -158,7 +157,7 @@ client.user().getUser( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/version/no-custom-config/README.md b/seed/java-sdk/version/no-custom-config/README.md index 0ba46b31d555..ab6bd1c3e328 100644 --- a/seed/java-sdk/version/no-custom-config/README.md +++ b/seed/java-sdk/version/no-custom-config/README.md @@ -142,7 +142,6 @@ SeedVersionClient client = SeedVersionClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.version.SeedVersionClient; import com.seed.version.core.RequestOptions; @@ -150,7 +149,7 @@ import com.seed.version.core.RequestOptions; // Client level SeedVersionClient client = SeedVersionClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -158,7 +157,7 @@ client.user().getUser( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-sdk/websocket-inferred-auth/README.md b/seed/java-sdk/websocket-inferred-auth/README.md index 1b56267f8058..f4131a129b1d 100644 --- a/seed/java-sdk/websocket-inferred-auth/README.md +++ b/seed/java-sdk/websocket-inferred-auth/README.md @@ -180,7 +180,6 @@ SeedWebsocketAuthClient client = SeedWebsocketAuthClient ### Timeouts The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. - ```java import com.seed.websocketAuth.SeedWebsocketAuthClient; import com.seed.websocketAuth.core.RequestOptions; @@ -188,7 +187,7 @@ import com.seed.websocketAuth.core.RequestOptions; // Client level SeedWebsocketAuthClient client = SeedWebsocketAuthClient .builder() - .timeout(10) + .timeout(60) .build(); // Request level @@ -196,7 +195,7 @@ client.auth().getTokenWithClientCredentials( ..., RequestOptions .builder() - .timeout(10) + .timeout(60) .build() ); ``` diff --git a/seed/java-spring/inferred-auth-implicit-api-key/snippet.json b/seed/java-spring/inferred-auth-implicit-api-key/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/java-spring/inferred-auth-implicit-api-key/src/main/java/core/APIException.java b/seed/java-spring/inferred-auth-implicit-api-key/src/main/java/core/APIException.java new file mode 100644 index 000000000000..27289cf9b2e4 --- /dev/null +++ b/seed/java-spring/inferred-auth-implicit-api-key/src/main/java/core/APIException.java @@ -0,0 +1,10 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package core; + +import java.lang.Exception; + +public class APIException extends Exception { +} diff --git a/seed/java-spring/inferred-auth-implicit-api-key/src/main/java/core/DateTimeDeserializer.java b/seed/java-spring/inferred-auth-implicit-api-key/src/main/java/core/DateTimeDeserializer.java new file mode 100644 index 000000000000..3d3174aec001 --- /dev/null +++ b/seed/java-spring/inferred-auth-implicit-api-key/src/main/java/core/DateTimeDeserializer.java @@ -0,0 +1,56 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package core; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.module.SimpleModule; +import java.io.IOException; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalQueries; + +/** + * Custom deserializer that handles converting ISO8601 dates into {@link OffsetDateTime} objects. + */ +class DateTimeDeserializer extends JsonDeserializer { + private static final SimpleModule MODULE; + + static { + MODULE = new SimpleModule().addDeserializer(OffsetDateTime.class, new DateTimeDeserializer()); + } + + /** + * Gets a module wrapping this deserializer as an adapter for the Jackson ObjectMapper. + * + * @return A {@link SimpleModule} to be plugged onto Jackson ObjectMapper. + */ + public static SimpleModule getModule() { + return MODULE; + } + + @Override + public OffsetDateTime deserialize(JsonParser parser, DeserializationContext context) throws IOException { + JsonToken token = parser.currentToken(); + if (token == JsonToken.VALUE_NUMBER_INT) { + return OffsetDateTime.ofInstant(Instant.ofEpochSecond(parser.getValueAsLong()), ZoneOffset.UTC); + } else { + TemporalAccessor temporal = DateTimeFormatter.ISO_DATE_TIME.parseBest( + parser.getValueAsString(), OffsetDateTime::from, LocalDateTime::from); + + if (temporal.query(TemporalQueries.offset()) == null) { + return LocalDateTime.from(temporal).atOffset(ZoneOffset.UTC); + } else { + return OffsetDateTime.from(temporal); + } + } + } +} \ No newline at end of file diff --git a/seed/java-spring/inferred-auth-implicit-api-key/src/main/java/core/ObjectMappers.java b/seed/java-spring/inferred-auth-implicit-api-key/src/main/java/core/ObjectMappers.java new file mode 100644 index 000000000000..7208607e674e --- /dev/null +++ b/seed/java-spring/inferred-auth-implicit-api-key/src/main/java/core/ObjectMappers.java @@ -0,0 +1,51 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package core; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import java.io.IOException; +import java.lang.Integer; +import java.lang.Object; +import java.lang.String; + +public final class ObjectMappers { + public static final ObjectMapper JSON_MAPPER = JsonMapper.builder() + .addModule(new Jdk8Module()) + .addModule(new JavaTimeModule()) + .addModule(DateTimeDeserializer.getModule()) + .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .build(); + + private ObjectMappers() { + } + + public static String stringify(Object o) { + try { + return JSON_MAPPER.setSerializationInclusion(JsonInclude.Include.ALWAYS) + .writerWithDefaultPrettyPrinter() + .writeValueAsString(o); + } + catch (IOException e) { + return o.getClass().getName() + "@" + Integer.toHexString(o.hashCode()); + } + } + + public static Object parseErrorBody(String responseBodyString) { + try { + return JSON_MAPPER.readValue(responseBodyString, Object.class); + } + catch (JsonProcessingException ignored) { + return responseBodyString; + } + } + } diff --git a/seed/java-spring/inferred-auth-implicit-api-key/src/main/java/core/OptionalNullable.java b/seed/java-spring/inferred-auth-implicit-api-key/src/main/java/core/OptionalNullable.java new file mode 100644 index 000000000000..15415ffefc09 --- /dev/null +++ b/seed/java-spring/inferred-auth-implicit-api-key/src/main/java/core/OptionalNullable.java @@ -0,0 +1,182 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package core; + +import static java.util.Objects.requireNonNull; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import java.util.Objects; +import java.util.Optional; + +/** + * An optional wrapper type that supports null values, distinguishing between: + * - ABSENT: field not present (e.g., not included in request) + * - NULL: field explicitly set to null + * - PRESENT: field has a non-null value + * + * This is useful for partial updates, JSON Merge Patch (RFC 7396), and any API + * that needs to differentiate between "not specified" and "set to null". + * + * Use this for optional> types where null is a valid value. + * For regular optional types (where null is not valid), use Optional instead. + */ +public final class OptionalNullable { + private final State state; + + private final T value; + + private OptionalNullable(State state, T value) { + this.state = state; + this.value = value; + } + + /** + * Creates an absent OptionalNullable (field not present). + */ + public static OptionalNullable absent() { + return new OptionalNullable<>(State.ABSENT, null); + } + + /** + * Creates a null OptionalNullable (field explicitly set to null). + */ + public static OptionalNullable ofNull() { + return new OptionalNullable<>(State.NULL, null); + } + + /** + * Creates an OptionalNullable with a value. + */ + public static OptionalNullable of(T value) { + requireNonNull(value, "Use ofNull() for null values"); + return new OptionalNullable<>(State.PRESENT, value); + } + + /** + * Creates an OptionalNullable from a nullable value. + */ + @JsonCreator + public static OptionalNullable ofNullable(T value) { + return value == null ? ofNull() : of(value); + } + + /** + * Returns true if the field was absent from the request. + */ + public boolean isAbsent() { + return state == State.ABSENT; + } + + /** + * Returns true if the field was explicitly set to null. + */ + public boolean isNull() { + return state == State.NULL; + } + + /** + * Returns true if the field has a value. + */ + public boolean isPresent() { + return state == State.PRESENT; + } + + /** + * Returns true if the field was present in the request (either null or with a value). + */ + public boolean wasSpecified() { + return state != State.ABSENT; + } + + /** + * Gets the value if present, throws if absent or null. + */ + public T get() { + if (state != State.PRESENT) { + throw new IllegalStateException("Cannot get value from " + state + " OptionalNullable"); + } + return value; + } + + /** + * Gets the value if present or explicitly null, throws if absent. + * This is useful for update operations where null is a valid value to set. + */ + public T getValueOrNull() { + if (state == State.ABSENT) { + throw new IllegalStateException("No value set"); + } + return value; + } + + /** + * Gets the value if present, returns null if explicitly set to null, or returns the provided default if absent. + */ + public T orElse(T defaultValue) { + if (state == State.PRESENT) { + return value; + } + if (state == State.NULL) { + return null; + } + return defaultValue; + } + + /** + * Converts to an Optional, returning empty for both absent and null states. + */ + public Optional toOptional() { + return state == State.PRESENT ? Optional.of(value) : Optional.empty(); + } + + /** + * For JSON serialization - serialize the actual value or null. + * Absent values should be handled by @JsonInclude(JsonInclude.Include.CUSTOM) + */ + @JsonValue + public Object getJsonValue() { + if (state == State.ABSENT) { + // Should not be serialized - handled by custom inclusion + throw new IllegalStateException("Absent values should not be serialized"); + } + return state == State.NULL ? null : value; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + OptionalNullable that = (OptionalNullable) o; + return state == that.state && Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(state, value); + } + + @Override + public String toString() { + switch (state) { + case ABSENT: return "OptionalNullable.absent()"; + case NULL: return "OptionalNullable.ofNull()"; + case PRESENT: return "OptionalNullable.of(" + value + ")"; + default: throw new IllegalStateException("Unknown state: " + state); + } + } + + private enum State { + ABSENT, + + NULL, + + PRESENT + } +} diff --git a/seed/java-spring/inferred-auth-implicit-api-key/src/main/java/resources/auth/AuthService.java b/seed/java-spring/inferred-auth-implicit-api-key/src/main/java/resources/auth/AuthService.java new file mode 100644 index 000000000000..64b018fdcd75 --- /dev/null +++ b/seed/java-spring/inferred-auth-implicit-api-key/src/main/java/resources/auth/AuthService.java @@ -0,0 +1,22 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package resources.auth; + +import java.lang.String; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import resources.auth.types.TokenResponse; + +@RequestMapping( + path = "/" +) +public interface AuthService { + @PostMapping( + value = "/token", + produces = "application/json" + ) + TokenResponse getToken(@RequestHeader("X-Api-Key") String apiKey); +} diff --git a/seed/java-spring/inferred-auth-implicit-api-key/src/main/java/resources/auth/types/TokenResponse.java b/seed/java-spring/inferred-auth-implicit-api-key/src/main/java/resources/auth/types/TokenResponse.java new file mode 100644 index 000000000000..7086351330d5 --- /dev/null +++ b/seed/java-spring/inferred-auth-implicit-api-key/src/main/java/resources/auth/types/TokenResponse.java @@ -0,0 +1,173 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package resources.auth.types; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import core.ObjectMappers; +import java.lang.Object; +import java.lang.String; +import java.util.Objects; +import java.util.Optional; +import org.jetbrains.annotations.NotNull; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize( + builder = TokenResponse.Builder.class +) +public final class TokenResponse { + private final String accessToken; + + private final String tokenType; + + private final int expiresIn; + + private final Optional scope; + + private TokenResponse(String accessToken, String tokenType, int expiresIn, + Optional scope) { + this.accessToken = accessToken; + this.tokenType = tokenType; + this.expiresIn = expiresIn; + this.scope = scope; + } + + @JsonProperty("access_token") + public String getAccessToken() { + return accessToken; + } + + @JsonProperty("token_type") + public String getTokenType() { + return tokenType; + } + + @JsonProperty("expires_in") + public int getExpiresIn() { + return expiresIn; + } + + @JsonProperty("scope") + public Optional getScope() { + return scope; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof TokenResponse && equalTo((TokenResponse) other); + } + + private boolean equalTo(TokenResponse other) { + return accessToken.equals(other.accessToken) && tokenType.equals(other.tokenType) && expiresIn == other.expiresIn && scope.equals(other.scope); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.accessToken, this.tokenType, this.expiresIn, this.scope); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static AccessTokenStage builder() { + return new Builder(); + } + + public interface AccessTokenStage { + TokenTypeStage accessToken(@NotNull String accessToken); + + Builder from(TokenResponse other); + } + + public interface TokenTypeStage { + ExpiresInStage tokenType(@NotNull String tokenType); + } + + public interface ExpiresInStage { + _FinalStage expiresIn(int expiresIn); + } + + public interface _FinalStage { + TokenResponse build(); + + _FinalStage scope(Optional scope); + + _FinalStage scope(String scope); + } + + @JsonIgnoreProperties( + ignoreUnknown = true + ) + public static final class Builder implements AccessTokenStage, TokenTypeStage, ExpiresInStage, _FinalStage { + private String accessToken; + + private String tokenType; + + private int expiresIn; + + private Optional scope = Optional.empty(); + + private Builder() { + } + + @java.lang.Override + public Builder from(TokenResponse other) { + accessToken(other.getAccessToken()); + tokenType(other.getTokenType()); + expiresIn(other.getExpiresIn()); + scope(other.getScope()); + return this; + } + + @java.lang.Override + @JsonSetter("access_token") + public TokenTypeStage accessToken(@NotNull String accessToken) { + this.accessToken = Objects.requireNonNull(accessToken, "accessToken must not be null"); + return this; + } + + @java.lang.Override + @JsonSetter("token_type") + public ExpiresInStage tokenType(@NotNull String tokenType) { + this.tokenType = Objects.requireNonNull(tokenType, "tokenType must not be null"); + return this; + } + + @java.lang.Override + @JsonSetter("expires_in") + public _FinalStage expiresIn(int expiresIn) { + this.expiresIn = expiresIn; + return this; + } + + @java.lang.Override + public _FinalStage scope(String scope) { + this.scope = Optional.ofNullable(scope); + return this; + } + + @java.lang.Override + @JsonSetter( + value = "scope", + nulls = Nulls.SKIP + ) + public _FinalStage scope(Optional scope) { + this.scope = scope; + return this; + } + + @java.lang.Override + public TokenResponse build() { + return new TokenResponse(accessToken, tokenType, expiresIn, scope); + } + } +} diff --git a/seed/java-spring/inferred-auth-implicit-api-key/src/main/java/resources/nested/api/ApiService.java b/seed/java-spring/inferred-auth-implicit-api-key/src/main/java/resources/nested/api/ApiService.java new file mode 100644 index 000000000000..226b0de342c7 --- /dev/null +++ b/seed/java-spring/inferred-auth-implicit-api-key/src/main/java/resources/nested/api/ApiService.java @@ -0,0 +1,20 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package resources.nested.api; + +import java.security.Principal; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +@RequestMapping( + path = "/nested" +) +public interface ApiService { + @GetMapping( + value = "/get-something", + produces = "application/json" + ) + void getSomething(Principal principal); +} diff --git a/seed/java-spring/inferred-auth-implicit-api-key/src/main/java/resources/nestednoauth/api/ApiService.java b/seed/java-spring/inferred-auth-implicit-api-key/src/main/java/resources/nestednoauth/api/ApiService.java new file mode 100644 index 000000000000..53b0956b33dd --- /dev/null +++ b/seed/java-spring/inferred-auth-implicit-api-key/src/main/java/resources/nestednoauth/api/ApiService.java @@ -0,0 +1,19 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package resources.nestednoauth.api; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +@RequestMapping( + path = "/nested-no-auth" +) +public interface ApiService { + @GetMapping( + value = "/get-something", + produces = "application/json" + ) + void getSomething(); +} diff --git a/seed/java-spring/inferred-auth-implicit-api-key/src/main/java/resources/simple/SimpleService.java b/seed/java-spring/inferred-auth-implicit-api-key/src/main/java/resources/simple/SimpleService.java new file mode 100644 index 000000000000..497f755bb457 --- /dev/null +++ b/seed/java-spring/inferred-auth-implicit-api-key/src/main/java/resources/simple/SimpleService.java @@ -0,0 +1,20 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package resources.simple; + +import java.security.Principal; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +@RequestMapping( + path = "/" +) +public interface SimpleService { + @GetMapping( + value = "/get-something", + produces = "application/json" + ) + void getSomething(Principal principal); +} diff --git a/seed/java-spring/inferred-auth-implicit-reference/snippet.json b/seed/java-spring/inferred-auth-implicit-reference/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/java-spring/inferred-auth-implicit-reference/src/main/java/core/APIException.java b/seed/java-spring/inferred-auth-implicit-reference/src/main/java/core/APIException.java new file mode 100644 index 000000000000..27289cf9b2e4 --- /dev/null +++ b/seed/java-spring/inferred-auth-implicit-reference/src/main/java/core/APIException.java @@ -0,0 +1,10 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package core; + +import java.lang.Exception; + +public class APIException extends Exception { +} diff --git a/seed/java-spring/inferred-auth-implicit-reference/src/main/java/core/DateTimeDeserializer.java b/seed/java-spring/inferred-auth-implicit-reference/src/main/java/core/DateTimeDeserializer.java new file mode 100644 index 000000000000..3d3174aec001 --- /dev/null +++ b/seed/java-spring/inferred-auth-implicit-reference/src/main/java/core/DateTimeDeserializer.java @@ -0,0 +1,56 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package core; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.module.SimpleModule; +import java.io.IOException; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalQueries; + +/** + * Custom deserializer that handles converting ISO8601 dates into {@link OffsetDateTime} objects. + */ +class DateTimeDeserializer extends JsonDeserializer { + private static final SimpleModule MODULE; + + static { + MODULE = new SimpleModule().addDeserializer(OffsetDateTime.class, new DateTimeDeserializer()); + } + + /** + * Gets a module wrapping this deserializer as an adapter for the Jackson ObjectMapper. + * + * @return A {@link SimpleModule} to be plugged onto Jackson ObjectMapper. + */ + public static SimpleModule getModule() { + return MODULE; + } + + @Override + public OffsetDateTime deserialize(JsonParser parser, DeserializationContext context) throws IOException { + JsonToken token = parser.currentToken(); + if (token == JsonToken.VALUE_NUMBER_INT) { + return OffsetDateTime.ofInstant(Instant.ofEpochSecond(parser.getValueAsLong()), ZoneOffset.UTC); + } else { + TemporalAccessor temporal = DateTimeFormatter.ISO_DATE_TIME.parseBest( + parser.getValueAsString(), OffsetDateTime::from, LocalDateTime::from); + + if (temporal.query(TemporalQueries.offset()) == null) { + return LocalDateTime.from(temporal).atOffset(ZoneOffset.UTC); + } else { + return OffsetDateTime.from(temporal); + } + } + } +} \ No newline at end of file diff --git a/seed/java-spring/inferred-auth-implicit-reference/src/main/java/core/ObjectMappers.java b/seed/java-spring/inferred-auth-implicit-reference/src/main/java/core/ObjectMappers.java new file mode 100644 index 000000000000..7208607e674e --- /dev/null +++ b/seed/java-spring/inferred-auth-implicit-reference/src/main/java/core/ObjectMappers.java @@ -0,0 +1,51 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package core; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import java.io.IOException; +import java.lang.Integer; +import java.lang.Object; +import java.lang.String; + +public final class ObjectMappers { + public static final ObjectMapper JSON_MAPPER = JsonMapper.builder() + .addModule(new Jdk8Module()) + .addModule(new JavaTimeModule()) + .addModule(DateTimeDeserializer.getModule()) + .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .build(); + + private ObjectMappers() { + } + + public static String stringify(Object o) { + try { + return JSON_MAPPER.setSerializationInclusion(JsonInclude.Include.ALWAYS) + .writerWithDefaultPrettyPrinter() + .writeValueAsString(o); + } + catch (IOException e) { + return o.getClass().getName() + "@" + Integer.toHexString(o.hashCode()); + } + } + + public static Object parseErrorBody(String responseBodyString) { + try { + return JSON_MAPPER.readValue(responseBodyString, Object.class); + } + catch (JsonProcessingException ignored) { + return responseBodyString; + } + } + } diff --git a/seed/java-spring/inferred-auth-implicit-reference/src/main/java/core/OptionalNullable.java b/seed/java-spring/inferred-auth-implicit-reference/src/main/java/core/OptionalNullable.java new file mode 100644 index 000000000000..15415ffefc09 --- /dev/null +++ b/seed/java-spring/inferred-auth-implicit-reference/src/main/java/core/OptionalNullable.java @@ -0,0 +1,182 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package core; + +import static java.util.Objects.requireNonNull; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import java.util.Objects; +import java.util.Optional; + +/** + * An optional wrapper type that supports null values, distinguishing between: + * - ABSENT: field not present (e.g., not included in request) + * - NULL: field explicitly set to null + * - PRESENT: field has a non-null value + * + * This is useful for partial updates, JSON Merge Patch (RFC 7396), and any API + * that needs to differentiate between "not specified" and "set to null". + * + * Use this for optional> types where null is a valid value. + * For regular optional types (where null is not valid), use Optional instead. + */ +public final class OptionalNullable { + private final State state; + + private final T value; + + private OptionalNullable(State state, T value) { + this.state = state; + this.value = value; + } + + /** + * Creates an absent OptionalNullable (field not present). + */ + public static OptionalNullable absent() { + return new OptionalNullable<>(State.ABSENT, null); + } + + /** + * Creates a null OptionalNullable (field explicitly set to null). + */ + public static OptionalNullable ofNull() { + return new OptionalNullable<>(State.NULL, null); + } + + /** + * Creates an OptionalNullable with a value. + */ + public static OptionalNullable of(T value) { + requireNonNull(value, "Use ofNull() for null values"); + return new OptionalNullable<>(State.PRESENT, value); + } + + /** + * Creates an OptionalNullable from a nullable value. + */ + @JsonCreator + public static OptionalNullable ofNullable(T value) { + return value == null ? ofNull() : of(value); + } + + /** + * Returns true if the field was absent from the request. + */ + public boolean isAbsent() { + return state == State.ABSENT; + } + + /** + * Returns true if the field was explicitly set to null. + */ + public boolean isNull() { + return state == State.NULL; + } + + /** + * Returns true if the field has a value. + */ + public boolean isPresent() { + return state == State.PRESENT; + } + + /** + * Returns true if the field was present in the request (either null or with a value). + */ + public boolean wasSpecified() { + return state != State.ABSENT; + } + + /** + * Gets the value if present, throws if absent or null. + */ + public T get() { + if (state != State.PRESENT) { + throw new IllegalStateException("Cannot get value from " + state + " OptionalNullable"); + } + return value; + } + + /** + * Gets the value if present or explicitly null, throws if absent. + * This is useful for update operations where null is a valid value to set. + */ + public T getValueOrNull() { + if (state == State.ABSENT) { + throw new IllegalStateException("No value set"); + } + return value; + } + + /** + * Gets the value if present, returns null if explicitly set to null, or returns the provided default if absent. + */ + public T orElse(T defaultValue) { + if (state == State.PRESENT) { + return value; + } + if (state == State.NULL) { + return null; + } + return defaultValue; + } + + /** + * Converts to an Optional, returning empty for both absent and null states. + */ + public Optional toOptional() { + return state == State.PRESENT ? Optional.of(value) : Optional.empty(); + } + + /** + * For JSON serialization - serialize the actual value or null. + * Absent values should be handled by @JsonInclude(JsonInclude.Include.CUSTOM) + */ + @JsonValue + public Object getJsonValue() { + if (state == State.ABSENT) { + // Should not be serialized - handled by custom inclusion + throw new IllegalStateException("Absent values should not be serialized"); + } + return state == State.NULL ? null : value; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + OptionalNullable that = (OptionalNullable) o; + return state == that.state && Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(state, value); + } + + @Override + public String toString() { + switch (state) { + case ABSENT: return "OptionalNullable.absent()"; + case NULL: return "OptionalNullable.ofNull()"; + case PRESENT: return "OptionalNullable.of(" + value + ")"; + default: throw new IllegalStateException("Unknown state: " + state); + } + } + + private enum State { + ABSENT, + + NULL, + + PRESENT + } +} diff --git a/seed/java-spring/inferred-auth-implicit-reference/src/main/java/resources/auth/AuthService.java b/seed/java-spring/inferred-auth-implicit-reference/src/main/java/resources/auth/AuthService.java new file mode 100644 index 000000000000..5f4226485c9b --- /dev/null +++ b/seed/java-spring/inferred-auth-implicit-reference/src/main/java/resources/auth/AuthService.java @@ -0,0 +1,31 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package resources.auth; + +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import resources.auth.types.GetTokenRequest; +import resources.auth.types.RefreshTokenRequest; +import resources.auth.types.TokenResponse; + +@RequestMapping( + path = "/" +) +public interface AuthService { + @PostMapping( + value = "/token", + produces = "application/json", + consumes = "application/json" + ) + TokenResponse getTokenWithClientCredentials(@RequestBody GetTokenRequest body); + + @PostMapping( + value = "/token/refresh", + produces = "application/json", + consumes = "application/json" + ) + TokenResponse refreshToken(@RequestBody RefreshTokenRequest body); +} diff --git a/seed/java-spring/inferred-auth-implicit-reference/src/main/java/resources/auth/types/GetTokenRequest.java b/seed/java-spring/inferred-auth-implicit-reference/src/main/java/resources/auth/types/GetTokenRequest.java new file mode 100644 index 000000000000..f42315888f19 --- /dev/null +++ b/seed/java-spring/inferred-auth-implicit-reference/src/main/java/resources/auth/types/GetTokenRequest.java @@ -0,0 +1,160 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package resources.auth.types; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import core.ObjectMappers; +import java.lang.Object; +import java.lang.String; +import java.util.Objects; +import java.util.Optional; +import org.jetbrains.annotations.NotNull; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize( + builder = GetTokenRequest.Builder.class +) +public final class GetTokenRequest { + private final String clientId; + + private final String clientSecret; + + private final Optional scope; + + private GetTokenRequest(String clientId, String clientSecret, Optional scope) { + this.clientId = clientId; + this.clientSecret = clientSecret; + this.scope = scope; + } + + @JsonProperty("client_id") + public String getClientId() { + return clientId; + } + + @JsonProperty("client_secret") + public String getClientSecret() { + return clientSecret; + } + + @JsonProperty("audience") + public String getAudience() { + return "https://api.example.com"; + } + + @JsonProperty("grant_type") + public String getGrantType() { + return "client_credentials"; + } + + @JsonProperty("scope") + public Optional getScope() { + return scope; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof GetTokenRequest && equalTo((GetTokenRequest) other); + } + + private boolean equalTo(GetTokenRequest other) { + return clientId.equals(other.clientId) && clientSecret.equals(other.clientSecret) && scope.equals(other.scope); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.clientId, this.clientSecret, this.scope); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static ClientIdStage builder() { + return new Builder(); + } + + public interface ClientIdStage { + ClientSecretStage clientId(@NotNull String clientId); + + Builder from(GetTokenRequest other); + } + + public interface ClientSecretStage { + _FinalStage clientSecret(@NotNull String clientSecret); + } + + public interface _FinalStage { + GetTokenRequest build(); + + _FinalStage scope(Optional scope); + + _FinalStage scope(String scope); + } + + @JsonIgnoreProperties( + ignoreUnknown = true + ) + public static final class Builder implements ClientIdStage, ClientSecretStage, _FinalStage { + private String clientId; + + private String clientSecret; + + private Optional scope = Optional.empty(); + + private Builder() { + } + + @java.lang.Override + public Builder from(GetTokenRequest other) { + clientId(other.getClientId()); + clientSecret(other.getClientSecret()); + scope(other.getScope()); + return this; + } + + @java.lang.Override + @JsonSetter("client_id") + public ClientSecretStage clientId(@NotNull String clientId) { + this.clientId = Objects.requireNonNull(clientId, "clientId must not be null"); + return this; + } + + @java.lang.Override + @JsonSetter("client_secret") + public _FinalStage clientSecret(@NotNull String clientSecret) { + this.clientSecret = Objects.requireNonNull(clientSecret, "clientSecret must not be null"); + return this; + } + + @java.lang.Override + public _FinalStage scope(String scope) { + this.scope = Optional.ofNullable(scope); + return this; + } + + @java.lang.Override + @JsonSetter( + value = "scope", + nulls = Nulls.SKIP + ) + public _FinalStage scope(Optional scope) { + this.scope = scope; + return this; + } + + @java.lang.Override + public GetTokenRequest build() { + return new GetTokenRequest(clientId, clientSecret, scope); + } + } +} diff --git a/seed/java-spring/inferred-auth-implicit-reference/src/main/java/resources/auth/types/RefreshTokenRequest.java b/seed/java-spring/inferred-auth-implicit-reference/src/main/java/resources/auth/types/RefreshTokenRequest.java new file mode 100644 index 000000000000..39170501086f --- /dev/null +++ b/seed/java-spring/inferred-auth-implicit-reference/src/main/java/resources/auth/types/RefreshTokenRequest.java @@ -0,0 +1,183 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package resources.auth.types; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import core.ObjectMappers; +import java.lang.Object; +import java.lang.String; +import java.util.Objects; +import java.util.Optional; +import org.jetbrains.annotations.NotNull; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize( + builder = RefreshTokenRequest.Builder.class +) +public final class RefreshTokenRequest { + private final String clientId; + + private final String clientSecret; + + private final String refreshToken; + + private final Optional scope; + + private RefreshTokenRequest(String clientId, String clientSecret, String refreshToken, + Optional scope) { + this.clientId = clientId; + this.clientSecret = clientSecret; + this.refreshToken = refreshToken; + this.scope = scope; + } + + @JsonProperty("client_id") + public String getClientId() { + return clientId; + } + + @JsonProperty("client_secret") + public String getClientSecret() { + return clientSecret; + } + + @JsonProperty("refresh_token") + public String getRefreshToken() { + return refreshToken; + } + + @JsonProperty("audience") + public String getAudience() { + return "https://api.example.com"; + } + + @JsonProperty("grant_type") + public String getGrantType() { + return "refresh_token"; + } + + @JsonProperty("scope") + public Optional getScope() { + return scope; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof RefreshTokenRequest && equalTo((RefreshTokenRequest) other); + } + + private boolean equalTo(RefreshTokenRequest other) { + return clientId.equals(other.clientId) && clientSecret.equals(other.clientSecret) && refreshToken.equals(other.refreshToken) && scope.equals(other.scope); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.clientId, this.clientSecret, this.refreshToken, this.scope); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static ClientIdStage builder() { + return new Builder(); + } + + public interface ClientIdStage { + ClientSecretStage clientId(@NotNull String clientId); + + Builder from(RefreshTokenRequest other); + } + + public interface ClientSecretStage { + RefreshTokenStage clientSecret(@NotNull String clientSecret); + } + + public interface RefreshTokenStage { + _FinalStage refreshToken(@NotNull String refreshToken); + } + + public interface _FinalStage { + RefreshTokenRequest build(); + + _FinalStage scope(Optional scope); + + _FinalStage scope(String scope); + } + + @JsonIgnoreProperties( + ignoreUnknown = true + ) + public static final class Builder implements ClientIdStage, ClientSecretStage, RefreshTokenStage, _FinalStage { + private String clientId; + + private String clientSecret; + + private String refreshToken; + + private Optional scope = Optional.empty(); + + private Builder() { + } + + @java.lang.Override + public Builder from(RefreshTokenRequest other) { + clientId(other.getClientId()); + clientSecret(other.getClientSecret()); + refreshToken(other.getRefreshToken()); + scope(other.getScope()); + return this; + } + + @java.lang.Override + @JsonSetter("client_id") + public ClientSecretStage clientId(@NotNull String clientId) { + this.clientId = Objects.requireNonNull(clientId, "clientId must not be null"); + return this; + } + + @java.lang.Override + @JsonSetter("client_secret") + public RefreshTokenStage clientSecret(@NotNull String clientSecret) { + this.clientSecret = Objects.requireNonNull(clientSecret, "clientSecret must not be null"); + return this; + } + + @java.lang.Override + @JsonSetter("refresh_token") + public _FinalStage refreshToken(@NotNull String refreshToken) { + this.refreshToken = Objects.requireNonNull(refreshToken, "refreshToken must not be null"); + return this; + } + + @java.lang.Override + public _FinalStage scope(String scope) { + this.scope = Optional.ofNullable(scope); + return this; + } + + @java.lang.Override + @JsonSetter( + value = "scope", + nulls = Nulls.SKIP + ) + public _FinalStage scope(Optional scope) { + this.scope = scope; + return this; + } + + @java.lang.Override + public RefreshTokenRequest build() { + return new RefreshTokenRequest(clientId, clientSecret, refreshToken, scope); + } + } +} diff --git a/seed/java-spring/inferred-auth-implicit-reference/src/main/java/resources/auth/types/TokenResponse.java b/seed/java-spring/inferred-auth-implicit-reference/src/main/java/resources/auth/types/TokenResponse.java new file mode 100644 index 000000000000..8f337f6338d2 --- /dev/null +++ b/seed/java-spring/inferred-auth-implicit-reference/src/main/java/resources/auth/types/TokenResponse.java @@ -0,0 +1,150 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package resources.auth.types; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import core.ObjectMappers; +import java.lang.Object; +import java.lang.String; +import java.util.Objects; +import java.util.Optional; +import org.jetbrains.annotations.NotNull; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize( + builder = TokenResponse.Builder.class +) +public final class TokenResponse { + private final String accessToken; + + private final int expiresIn; + + private final Optional refreshToken; + + private TokenResponse(String accessToken, int expiresIn, Optional refreshToken) { + this.accessToken = accessToken; + this.expiresIn = expiresIn; + this.refreshToken = refreshToken; + } + + @JsonProperty("access_token") + public String getAccessToken() { + return accessToken; + } + + @JsonProperty("expires_in") + public int getExpiresIn() { + return expiresIn; + } + + @JsonProperty("refresh_token") + public Optional getRefreshToken() { + return refreshToken; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof TokenResponse && equalTo((TokenResponse) other); + } + + private boolean equalTo(TokenResponse other) { + return accessToken.equals(other.accessToken) && expiresIn == other.expiresIn && refreshToken.equals(other.refreshToken); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.accessToken, this.expiresIn, this.refreshToken); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static AccessTokenStage builder() { + return new Builder(); + } + + public interface AccessTokenStage { + ExpiresInStage accessToken(@NotNull String accessToken); + + Builder from(TokenResponse other); + } + + public interface ExpiresInStage { + _FinalStage expiresIn(int expiresIn); + } + + public interface _FinalStage { + TokenResponse build(); + + _FinalStage refreshToken(Optional refreshToken); + + _FinalStage refreshToken(String refreshToken); + } + + @JsonIgnoreProperties( + ignoreUnknown = true + ) + public static final class Builder implements AccessTokenStage, ExpiresInStage, _FinalStage { + private String accessToken; + + private int expiresIn; + + private Optional refreshToken = Optional.empty(); + + private Builder() { + } + + @java.lang.Override + public Builder from(TokenResponse other) { + accessToken(other.getAccessToken()); + expiresIn(other.getExpiresIn()); + refreshToken(other.getRefreshToken()); + return this; + } + + @java.lang.Override + @JsonSetter("access_token") + public ExpiresInStage accessToken(@NotNull String accessToken) { + this.accessToken = Objects.requireNonNull(accessToken, "accessToken must not be null"); + return this; + } + + @java.lang.Override + @JsonSetter("expires_in") + public _FinalStage expiresIn(int expiresIn) { + this.expiresIn = expiresIn; + return this; + } + + @java.lang.Override + public _FinalStage refreshToken(String refreshToken) { + this.refreshToken = Optional.ofNullable(refreshToken); + return this; + } + + @java.lang.Override + @JsonSetter( + value = "refresh_token", + nulls = Nulls.SKIP + ) + public _FinalStage refreshToken(Optional refreshToken) { + this.refreshToken = refreshToken; + return this; + } + + @java.lang.Override + public TokenResponse build() { + return new TokenResponse(accessToken, expiresIn, refreshToken); + } + } +} diff --git a/seed/java-spring/inferred-auth-implicit-reference/src/main/java/resources/nested/api/ApiService.java b/seed/java-spring/inferred-auth-implicit-reference/src/main/java/resources/nested/api/ApiService.java new file mode 100644 index 000000000000..226b0de342c7 --- /dev/null +++ b/seed/java-spring/inferred-auth-implicit-reference/src/main/java/resources/nested/api/ApiService.java @@ -0,0 +1,20 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package resources.nested.api; + +import java.security.Principal; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +@RequestMapping( + path = "/nested" +) +public interface ApiService { + @GetMapping( + value = "/get-something", + produces = "application/json" + ) + void getSomething(Principal principal); +} diff --git a/seed/java-spring/inferred-auth-implicit-reference/src/main/java/resources/nestednoauth/api/ApiService.java b/seed/java-spring/inferred-auth-implicit-reference/src/main/java/resources/nestednoauth/api/ApiService.java new file mode 100644 index 000000000000..53b0956b33dd --- /dev/null +++ b/seed/java-spring/inferred-auth-implicit-reference/src/main/java/resources/nestednoauth/api/ApiService.java @@ -0,0 +1,19 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package resources.nestednoauth.api; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +@RequestMapping( + path = "/nested-no-auth" +) +public interface ApiService { + @GetMapping( + value = "/get-something", + produces = "application/json" + ) + void getSomething(); +} diff --git a/seed/java-spring/inferred-auth-implicit-reference/src/main/java/resources/simple/SimpleService.java b/seed/java-spring/inferred-auth-implicit-reference/src/main/java/resources/simple/SimpleService.java new file mode 100644 index 000000000000..497f755bb457 --- /dev/null +++ b/seed/java-spring/inferred-auth-implicit-reference/src/main/java/resources/simple/SimpleService.java @@ -0,0 +1,20 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package resources.simple; + +import java.security.Principal; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +@RequestMapping( + path = "/" +) +public interface SimpleService { + @GetMapping( + value = "/get-something", + produces = "application/json" + ) + void getSomething(Principal principal); +} diff --git a/seed/java-spring/java-default-timeout/snippet.json b/seed/java-spring/java-default-timeout/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/java-spring/java-default-timeout/src/main/java/RootService.java b/seed/java-spring/java-default-timeout/src/main/java/RootService.java new file mode 100644 index 000000000000..7ac56ba30e0c --- /dev/null +++ b/seed/java-spring/java-default-timeout/src/main/java/RootService.java @@ -0,0 +1,18 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import types.User; + +@RequestMapping( + path = "/" +) +public interface RootService { + @GetMapping( + value = "/user", + produces = "application/json" + ) + User getUser(); +} diff --git a/seed/java-spring/java-default-timeout/src/main/java/core/APIException.java b/seed/java-spring/java-default-timeout/src/main/java/core/APIException.java new file mode 100644 index 000000000000..27289cf9b2e4 --- /dev/null +++ b/seed/java-spring/java-default-timeout/src/main/java/core/APIException.java @@ -0,0 +1,10 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package core; + +import java.lang.Exception; + +public class APIException extends Exception { +} diff --git a/seed/java-spring/java-default-timeout/src/main/java/core/DateTimeDeserializer.java b/seed/java-spring/java-default-timeout/src/main/java/core/DateTimeDeserializer.java new file mode 100644 index 000000000000..3d3174aec001 --- /dev/null +++ b/seed/java-spring/java-default-timeout/src/main/java/core/DateTimeDeserializer.java @@ -0,0 +1,56 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package core; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.module.SimpleModule; +import java.io.IOException; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalQueries; + +/** + * Custom deserializer that handles converting ISO8601 dates into {@link OffsetDateTime} objects. + */ +class DateTimeDeserializer extends JsonDeserializer { + private static final SimpleModule MODULE; + + static { + MODULE = new SimpleModule().addDeserializer(OffsetDateTime.class, new DateTimeDeserializer()); + } + + /** + * Gets a module wrapping this deserializer as an adapter for the Jackson ObjectMapper. + * + * @return A {@link SimpleModule} to be plugged onto Jackson ObjectMapper. + */ + public static SimpleModule getModule() { + return MODULE; + } + + @Override + public OffsetDateTime deserialize(JsonParser parser, DeserializationContext context) throws IOException { + JsonToken token = parser.currentToken(); + if (token == JsonToken.VALUE_NUMBER_INT) { + return OffsetDateTime.ofInstant(Instant.ofEpochSecond(parser.getValueAsLong()), ZoneOffset.UTC); + } else { + TemporalAccessor temporal = DateTimeFormatter.ISO_DATE_TIME.parseBest( + parser.getValueAsString(), OffsetDateTime::from, LocalDateTime::from); + + if (temporal.query(TemporalQueries.offset()) == null) { + return LocalDateTime.from(temporal).atOffset(ZoneOffset.UTC); + } else { + return OffsetDateTime.from(temporal); + } + } + } +} \ No newline at end of file diff --git a/seed/java-spring/java-default-timeout/src/main/java/core/ObjectMappers.java b/seed/java-spring/java-default-timeout/src/main/java/core/ObjectMappers.java new file mode 100644 index 000000000000..7208607e674e --- /dev/null +++ b/seed/java-spring/java-default-timeout/src/main/java/core/ObjectMappers.java @@ -0,0 +1,51 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package core; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import java.io.IOException; +import java.lang.Integer; +import java.lang.Object; +import java.lang.String; + +public final class ObjectMappers { + public static final ObjectMapper JSON_MAPPER = JsonMapper.builder() + .addModule(new Jdk8Module()) + .addModule(new JavaTimeModule()) + .addModule(DateTimeDeserializer.getModule()) + .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .build(); + + private ObjectMappers() { + } + + public static String stringify(Object o) { + try { + return JSON_MAPPER.setSerializationInclusion(JsonInclude.Include.ALWAYS) + .writerWithDefaultPrettyPrinter() + .writeValueAsString(o); + } + catch (IOException e) { + return o.getClass().getName() + "@" + Integer.toHexString(o.hashCode()); + } + } + + public static Object parseErrorBody(String responseBodyString) { + try { + return JSON_MAPPER.readValue(responseBodyString, Object.class); + } + catch (JsonProcessingException ignored) { + return responseBodyString; + } + } + } diff --git a/seed/java-spring/java-default-timeout/src/main/java/core/OptionalNullable.java b/seed/java-spring/java-default-timeout/src/main/java/core/OptionalNullable.java new file mode 100644 index 000000000000..15415ffefc09 --- /dev/null +++ b/seed/java-spring/java-default-timeout/src/main/java/core/OptionalNullable.java @@ -0,0 +1,182 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package core; + +import static java.util.Objects.requireNonNull; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import java.util.Objects; +import java.util.Optional; + +/** + * An optional wrapper type that supports null values, distinguishing between: + * - ABSENT: field not present (e.g., not included in request) + * - NULL: field explicitly set to null + * - PRESENT: field has a non-null value + * + * This is useful for partial updates, JSON Merge Patch (RFC 7396), and any API + * that needs to differentiate between "not specified" and "set to null". + * + * Use this for optional> types where null is a valid value. + * For regular optional types (where null is not valid), use Optional instead. + */ +public final class OptionalNullable { + private final State state; + + private final T value; + + private OptionalNullable(State state, T value) { + this.state = state; + this.value = value; + } + + /** + * Creates an absent OptionalNullable (field not present). + */ + public static OptionalNullable absent() { + return new OptionalNullable<>(State.ABSENT, null); + } + + /** + * Creates a null OptionalNullable (field explicitly set to null). + */ + public static OptionalNullable ofNull() { + return new OptionalNullable<>(State.NULL, null); + } + + /** + * Creates an OptionalNullable with a value. + */ + public static OptionalNullable of(T value) { + requireNonNull(value, "Use ofNull() for null values"); + return new OptionalNullable<>(State.PRESENT, value); + } + + /** + * Creates an OptionalNullable from a nullable value. + */ + @JsonCreator + public static OptionalNullable ofNullable(T value) { + return value == null ? ofNull() : of(value); + } + + /** + * Returns true if the field was absent from the request. + */ + public boolean isAbsent() { + return state == State.ABSENT; + } + + /** + * Returns true if the field was explicitly set to null. + */ + public boolean isNull() { + return state == State.NULL; + } + + /** + * Returns true if the field has a value. + */ + public boolean isPresent() { + return state == State.PRESENT; + } + + /** + * Returns true if the field was present in the request (either null or with a value). + */ + public boolean wasSpecified() { + return state != State.ABSENT; + } + + /** + * Gets the value if present, throws if absent or null. + */ + public T get() { + if (state != State.PRESENT) { + throw new IllegalStateException("Cannot get value from " + state + " OptionalNullable"); + } + return value; + } + + /** + * Gets the value if present or explicitly null, throws if absent. + * This is useful for update operations where null is a valid value to set. + */ + public T getValueOrNull() { + if (state == State.ABSENT) { + throw new IllegalStateException("No value set"); + } + return value; + } + + /** + * Gets the value if present, returns null if explicitly set to null, or returns the provided default if absent. + */ + public T orElse(T defaultValue) { + if (state == State.PRESENT) { + return value; + } + if (state == State.NULL) { + return null; + } + return defaultValue; + } + + /** + * Converts to an Optional, returning empty for both absent and null states. + */ + public Optional toOptional() { + return state == State.PRESENT ? Optional.of(value) : Optional.empty(); + } + + /** + * For JSON serialization - serialize the actual value or null. + * Absent values should be handled by @JsonInclude(JsonInclude.Include.CUSTOM) + */ + @JsonValue + public Object getJsonValue() { + if (state == State.ABSENT) { + // Should not be serialized - handled by custom inclusion + throw new IllegalStateException("Absent values should not be serialized"); + } + return state == State.NULL ? null : value; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + OptionalNullable that = (OptionalNullable) o; + return state == that.state && Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(state, value); + } + + @Override + public String toString() { + switch (state) { + case ABSENT: return "OptionalNullable.absent()"; + case NULL: return "OptionalNullable.ofNull()"; + case PRESENT: return "OptionalNullable.of(" + value + ")"; + default: throw new IllegalStateException("Unknown state: " + state); + } + } + + private enum State { + ABSENT, + + NULL, + + PRESENT + } +} diff --git a/seed/java-spring/java-default-timeout/src/main/java/types/User.java b/seed/java-spring/java-default-timeout/src/main/java/types/User.java new file mode 100644 index 000000000000..efff4cbbd731 --- /dev/null +++ b/seed/java-spring/java-default-timeout/src/main/java/types/User.java @@ -0,0 +1,117 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package types; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import core.ObjectMappers; +import java.lang.Object; +import java.lang.String; +import java.util.Objects; +import org.jetbrains.annotations.NotNull; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize( + builder = User.Builder.class +) +public final class User { + private final String id; + + private final String name; + + private User(String id, String name) { + this.id = id; + this.name = name; + } + + @JsonProperty("id") + public String getId() { + return id; + } + + @JsonProperty("name") + public String getName() { + return name; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof User && equalTo((User) other); + } + + private boolean equalTo(User other) { + return id.equals(other.id) && name.equals(other.name); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.id, this.name); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static IdStage builder() { + return new Builder(); + } + + public interface IdStage { + NameStage id(@NotNull String id); + + Builder from(User other); + } + + public interface NameStage { + _FinalStage name(@NotNull String name); + } + + public interface _FinalStage { + User build(); + } + + @JsonIgnoreProperties( + ignoreUnknown = true + ) + public static final class Builder implements IdStage, NameStage, _FinalStage { + private String id; + + private String name; + + private Builder() { + } + + @java.lang.Override + public Builder from(User other) { + id(other.getId()); + name(other.getName()); + return this; + } + + @java.lang.Override + @JsonSetter("id") + public NameStage id(@NotNull String id) { + this.id = Objects.requireNonNull(id, "id must not be null"); + return this; + } + + @java.lang.Override + @JsonSetter("name") + public _FinalStage name(@NotNull String name) { + this.name = Objects.requireNonNull(name, "name must not be null"); + return this; + } + + @java.lang.Override + public User build() { + return new User(id, name); + } + } +} diff --git a/seed/java-spring/java-optional-nullable-query-params/snippet.json b/seed/java-spring/java-optional-nullable-query-params/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/java-spring/java-optional-nullable-query-params/src/main/java/RootService.java b/seed/java-spring/java-optional-nullable-query-params/src/main/java/RootService.java new file mode 100644 index 000000000000..82daf9606b5b --- /dev/null +++ b/seed/java-spring/java-optional-nullable-query-params/src/main/java/RootService.java @@ -0,0 +1,30 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import java.lang.Boolean; +import java.lang.Integer; +import java.lang.String; +import java.util.Optional; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import types.SearchResponse; +import types.SortOrder; + +@RequestMapping( + path = "/api" +) +public interface RootService { + @GetMapping( + value = "/search", + produces = "application/json" + ) + SearchResponse search(@RequestParam("query") Optional query, + @RequestParam("limit") Optional limit, + @RequestParam("includeArchived") Optional includeArchived, + @RequestParam("sortOrder") Optional sortOrder, + @RequestParam("optionalWithoutDefault") Optional optionalWithoutDefault, + @RequestParam("regularOptional") Optional regularOptional, + @RequestParam("regularOptionalNoDefault") Optional regularOptionalNoDefault); +} diff --git a/seed/java-spring/java-optional-nullable-query-params/src/main/java/core/APIException.java b/seed/java-spring/java-optional-nullable-query-params/src/main/java/core/APIException.java new file mode 100644 index 000000000000..27289cf9b2e4 --- /dev/null +++ b/seed/java-spring/java-optional-nullable-query-params/src/main/java/core/APIException.java @@ -0,0 +1,10 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package core; + +import java.lang.Exception; + +public class APIException extends Exception { +} diff --git a/seed/java-spring/java-optional-nullable-query-params/src/main/java/core/DateTimeDeserializer.java b/seed/java-spring/java-optional-nullable-query-params/src/main/java/core/DateTimeDeserializer.java new file mode 100644 index 000000000000..3d3174aec001 --- /dev/null +++ b/seed/java-spring/java-optional-nullable-query-params/src/main/java/core/DateTimeDeserializer.java @@ -0,0 +1,56 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package core; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.module.SimpleModule; +import java.io.IOException; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalQueries; + +/** + * Custom deserializer that handles converting ISO8601 dates into {@link OffsetDateTime} objects. + */ +class DateTimeDeserializer extends JsonDeserializer { + private static final SimpleModule MODULE; + + static { + MODULE = new SimpleModule().addDeserializer(OffsetDateTime.class, new DateTimeDeserializer()); + } + + /** + * Gets a module wrapping this deserializer as an adapter for the Jackson ObjectMapper. + * + * @return A {@link SimpleModule} to be plugged onto Jackson ObjectMapper. + */ + public static SimpleModule getModule() { + return MODULE; + } + + @Override + public OffsetDateTime deserialize(JsonParser parser, DeserializationContext context) throws IOException { + JsonToken token = parser.currentToken(); + if (token == JsonToken.VALUE_NUMBER_INT) { + return OffsetDateTime.ofInstant(Instant.ofEpochSecond(parser.getValueAsLong()), ZoneOffset.UTC); + } else { + TemporalAccessor temporal = DateTimeFormatter.ISO_DATE_TIME.parseBest( + parser.getValueAsString(), OffsetDateTime::from, LocalDateTime::from); + + if (temporal.query(TemporalQueries.offset()) == null) { + return LocalDateTime.from(temporal).atOffset(ZoneOffset.UTC); + } else { + return OffsetDateTime.from(temporal); + } + } + } +} \ No newline at end of file diff --git a/seed/java-spring/java-optional-nullable-query-params/src/main/java/core/ObjectMappers.java b/seed/java-spring/java-optional-nullable-query-params/src/main/java/core/ObjectMappers.java new file mode 100644 index 000000000000..7208607e674e --- /dev/null +++ b/seed/java-spring/java-optional-nullable-query-params/src/main/java/core/ObjectMappers.java @@ -0,0 +1,51 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package core; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import java.io.IOException; +import java.lang.Integer; +import java.lang.Object; +import java.lang.String; + +public final class ObjectMappers { + public static final ObjectMapper JSON_MAPPER = JsonMapper.builder() + .addModule(new Jdk8Module()) + .addModule(new JavaTimeModule()) + .addModule(DateTimeDeserializer.getModule()) + .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .build(); + + private ObjectMappers() { + } + + public static String stringify(Object o) { + try { + return JSON_MAPPER.setSerializationInclusion(JsonInclude.Include.ALWAYS) + .writerWithDefaultPrettyPrinter() + .writeValueAsString(o); + } + catch (IOException e) { + return o.getClass().getName() + "@" + Integer.toHexString(o.hashCode()); + } + } + + public static Object parseErrorBody(String responseBodyString) { + try { + return JSON_MAPPER.readValue(responseBodyString, Object.class); + } + catch (JsonProcessingException ignored) { + return responseBodyString; + } + } + } diff --git a/seed/java-spring/java-optional-nullable-query-params/src/main/java/core/OptionalNullable.java b/seed/java-spring/java-optional-nullable-query-params/src/main/java/core/OptionalNullable.java new file mode 100644 index 000000000000..15415ffefc09 --- /dev/null +++ b/seed/java-spring/java-optional-nullable-query-params/src/main/java/core/OptionalNullable.java @@ -0,0 +1,182 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package core; + +import static java.util.Objects.requireNonNull; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import java.util.Objects; +import java.util.Optional; + +/** + * An optional wrapper type that supports null values, distinguishing between: + * - ABSENT: field not present (e.g., not included in request) + * - NULL: field explicitly set to null + * - PRESENT: field has a non-null value + * + * This is useful for partial updates, JSON Merge Patch (RFC 7396), and any API + * that needs to differentiate between "not specified" and "set to null". + * + * Use this for optional> types where null is a valid value. + * For regular optional types (where null is not valid), use Optional instead. + */ +public final class OptionalNullable { + private final State state; + + private final T value; + + private OptionalNullable(State state, T value) { + this.state = state; + this.value = value; + } + + /** + * Creates an absent OptionalNullable (field not present). + */ + public static OptionalNullable absent() { + return new OptionalNullable<>(State.ABSENT, null); + } + + /** + * Creates a null OptionalNullable (field explicitly set to null). + */ + public static OptionalNullable ofNull() { + return new OptionalNullable<>(State.NULL, null); + } + + /** + * Creates an OptionalNullable with a value. + */ + public static OptionalNullable of(T value) { + requireNonNull(value, "Use ofNull() for null values"); + return new OptionalNullable<>(State.PRESENT, value); + } + + /** + * Creates an OptionalNullable from a nullable value. + */ + @JsonCreator + public static OptionalNullable ofNullable(T value) { + return value == null ? ofNull() : of(value); + } + + /** + * Returns true if the field was absent from the request. + */ + public boolean isAbsent() { + return state == State.ABSENT; + } + + /** + * Returns true if the field was explicitly set to null. + */ + public boolean isNull() { + return state == State.NULL; + } + + /** + * Returns true if the field has a value. + */ + public boolean isPresent() { + return state == State.PRESENT; + } + + /** + * Returns true if the field was present in the request (either null or with a value). + */ + public boolean wasSpecified() { + return state != State.ABSENT; + } + + /** + * Gets the value if present, throws if absent or null. + */ + public T get() { + if (state != State.PRESENT) { + throw new IllegalStateException("Cannot get value from " + state + " OptionalNullable"); + } + return value; + } + + /** + * Gets the value if present or explicitly null, throws if absent. + * This is useful for update operations where null is a valid value to set. + */ + public T getValueOrNull() { + if (state == State.ABSENT) { + throw new IllegalStateException("No value set"); + } + return value; + } + + /** + * Gets the value if present, returns null if explicitly set to null, or returns the provided default if absent. + */ + public T orElse(T defaultValue) { + if (state == State.PRESENT) { + return value; + } + if (state == State.NULL) { + return null; + } + return defaultValue; + } + + /** + * Converts to an Optional, returning empty for both absent and null states. + */ + public Optional toOptional() { + return state == State.PRESENT ? Optional.of(value) : Optional.empty(); + } + + /** + * For JSON serialization - serialize the actual value or null. + * Absent values should be handled by @JsonInclude(JsonInclude.Include.CUSTOM) + */ + @JsonValue + public Object getJsonValue() { + if (state == State.ABSENT) { + // Should not be serialized - handled by custom inclusion + throw new IllegalStateException("Absent values should not be serialized"); + } + return state == State.NULL ? null : value; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + OptionalNullable that = (OptionalNullable) o; + return state == that.state && Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(state, value); + } + + @Override + public String toString() { + switch (state) { + case ABSENT: return "OptionalNullable.absent()"; + case NULL: return "OptionalNullable.ofNull()"; + case PRESENT: return "OptionalNullable.of(" + value + ")"; + default: throw new IllegalStateException("Unknown state: " + state); + } + } + + private enum State { + ABSENT, + + NULL, + + PRESENT + } +} diff --git a/seed/java-spring/java-optional-nullable-query-params/src/main/java/types/SearchResponse.java b/seed/java-spring/java-optional-nullable-query-params/src/main/java/types/SearchResponse.java new file mode 100644 index 000000000000..7bfbf7e3eca6 --- /dev/null +++ b/seed/java-spring/java-optional-nullable-query-params/src/main/java/types/SearchResponse.java @@ -0,0 +1,269 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package types; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import core.ObjectMappers; +import java.lang.Boolean; +import java.lang.Integer; +import java.lang.Object; +import java.lang.String; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize( + builder = SearchResponse.Builder.class +) +public final class SearchResponse { + private final List results; + + private final int totalCount; + + private final Optional query; + + private final Optional limit; + + private final Optional includeArchived; + + private final Optional sortOrder; + + private SearchResponse(List results, int totalCount, Optional query, + Optional limit, Optional includeArchived, Optional sortOrder) { + this.results = results; + this.totalCount = totalCount; + this.query = query; + this.limit = limit; + this.includeArchived = includeArchived; + this.sortOrder = sortOrder; + } + + @JsonProperty("results") + public List getResults() { + return results; + } + + @JsonProperty("totalCount") + public int getTotalCount() { + return totalCount; + } + + @JsonProperty("query") + public Optional getQuery() { + return query; + } + + @JsonProperty("limit") + public Optional getLimit() { + return limit; + } + + @JsonProperty("includeArchived") + public Optional getIncludeArchived() { + return includeArchived; + } + + @JsonProperty("sortOrder") + public Optional getSortOrder() { + return sortOrder; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof SearchResponse && equalTo((SearchResponse) other); + } + + private boolean equalTo(SearchResponse other) { + return results.equals(other.results) && totalCount == other.totalCount && query.equals(other.query) && limit.equals(other.limit) && includeArchived.equals(other.includeArchived) && sortOrder.equals(other.sortOrder); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.results, this.totalCount, this.query, this.limit, this.includeArchived, this.sortOrder); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static TotalCountStage builder() { + return new Builder(); + } + + public interface TotalCountStage { + _FinalStage totalCount(int totalCount); + + Builder from(SearchResponse other); + } + + public interface _FinalStage { + SearchResponse build(); + + _FinalStage results(List results); + + _FinalStage addResults(String results); + + _FinalStage addAllResults(List results); + + _FinalStage query(Optional query); + + _FinalStage query(String query); + + _FinalStage limit(Optional limit); + + _FinalStage limit(Integer limit); + + _FinalStage includeArchived(Optional includeArchived); + + _FinalStage includeArchived(Boolean includeArchived); + + _FinalStage sortOrder(Optional sortOrder); + + _FinalStage sortOrder(SortOrder sortOrder); + } + + @JsonIgnoreProperties( + ignoreUnknown = true + ) + public static final class Builder implements TotalCountStage, _FinalStage { + private int totalCount; + + private Optional sortOrder = Optional.empty(); + + private Optional includeArchived = Optional.empty(); + + private Optional limit = Optional.empty(); + + private Optional query = Optional.empty(); + + private List results = new ArrayList<>(); + + private Builder() { + } + + @java.lang.Override + public Builder from(SearchResponse other) { + results(other.getResults()); + totalCount(other.getTotalCount()); + query(other.getQuery()); + limit(other.getLimit()); + includeArchived(other.getIncludeArchived()); + sortOrder(other.getSortOrder()); + return this; + } + + @java.lang.Override + @JsonSetter("totalCount") + public _FinalStage totalCount(int totalCount) { + this.totalCount = totalCount; + return this; + } + + @java.lang.Override + public _FinalStage sortOrder(SortOrder sortOrder) { + this.sortOrder = Optional.ofNullable(sortOrder); + return this; + } + + @java.lang.Override + @JsonSetter( + value = "sortOrder", + nulls = Nulls.SKIP + ) + public _FinalStage sortOrder(Optional sortOrder) { + this.sortOrder = sortOrder; + return this; + } + + @java.lang.Override + public _FinalStage includeArchived(Boolean includeArchived) { + this.includeArchived = Optional.ofNullable(includeArchived); + return this; + } + + @java.lang.Override + @JsonSetter( + value = "includeArchived", + nulls = Nulls.SKIP + ) + public _FinalStage includeArchived(Optional includeArchived) { + this.includeArchived = includeArchived; + return this; + } + + @java.lang.Override + public _FinalStage limit(Integer limit) { + this.limit = Optional.ofNullable(limit); + return this; + } + + @java.lang.Override + @JsonSetter( + value = "limit", + nulls = Nulls.SKIP + ) + public _FinalStage limit(Optional limit) { + this.limit = limit; + return this; + } + + @java.lang.Override + public _FinalStage query(String query) { + this.query = Optional.ofNullable(query); + return this; + } + + @java.lang.Override + @JsonSetter( + value = "query", + nulls = Nulls.SKIP + ) + public _FinalStage query(Optional query) { + this.query = query; + return this; + } + + @java.lang.Override + public _FinalStage addAllResults(List results) { + if (results != null) { + this.results.addAll(results); + } + return this; + } + + @java.lang.Override + public _FinalStage addResults(String results) { + this.results.add(results); + return this; + } + + @java.lang.Override + @JsonSetter( + value = "results", + nulls = Nulls.SKIP + ) + public _FinalStage results(List results) { + this.results.clear(); + if (results != null) { + this.results.addAll(results); + } + return this; + } + + @java.lang.Override + public SearchResponse build() { + return new SearchResponse(results, totalCount, query, limit, includeArchived, sortOrder); + } + } +} diff --git a/seed/java-spring/java-optional-nullable-query-params/src/main/java/types/SortOrder.java b/seed/java-spring/java-optional-nullable-query-params/src/main/java/types/SortOrder.java new file mode 100644 index 000000000000..1555f60346ae --- /dev/null +++ b/seed/java-spring/java-optional-nullable-query-params/src/main/java/types/SortOrder.java @@ -0,0 +1,26 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package types; + +import com.fasterxml.jackson.annotation.JsonValue; +import java.lang.String; + +public enum SortOrder { + ASC("ASC"), + + DESC("DESC"); + + private final String value; + + SortOrder(String value) { + this.value = value; + } + + @JsonValue + @java.lang.Override + public String toString() { + return this.value; + } +} diff --git a/seed/java-spring/java-with-property-conflict/snippet.json b/seed/java-spring/java-with-property-conflict/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/java-spring/java-with-property-conflict/src/main/java/core/APIException.java b/seed/java-spring/java-with-property-conflict/src/main/java/core/APIException.java new file mode 100644 index 000000000000..27289cf9b2e4 --- /dev/null +++ b/seed/java-spring/java-with-property-conflict/src/main/java/core/APIException.java @@ -0,0 +1,10 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package core; + +import java.lang.Exception; + +public class APIException extends Exception { +} diff --git a/seed/java-spring/java-with-property-conflict/src/main/java/core/DateTimeDeserializer.java b/seed/java-spring/java-with-property-conflict/src/main/java/core/DateTimeDeserializer.java new file mode 100644 index 000000000000..3d3174aec001 --- /dev/null +++ b/seed/java-spring/java-with-property-conflict/src/main/java/core/DateTimeDeserializer.java @@ -0,0 +1,56 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package core; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.module.SimpleModule; +import java.io.IOException; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalQueries; + +/** + * Custom deserializer that handles converting ISO8601 dates into {@link OffsetDateTime} objects. + */ +class DateTimeDeserializer extends JsonDeserializer { + private static final SimpleModule MODULE; + + static { + MODULE = new SimpleModule().addDeserializer(OffsetDateTime.class, new DateTimeDeserializer()); + } + + /** + * Gets a module wrapping this deserializer as an adapter for the Jackson ObjectMapper. + * + * @return A {@link SimpleModule} to be plugged onto Jackson ObjectMapper. + */ + public static SimpleModule getModule() { + return MODULE; + } + + @Override + public OffsetDateTime deserialize(JsonParser parser, DeserializationContext context) throws IOException { + JsonToken token = parser.currentToken(); + if (token == JsonToken.VALUE_NUMBER_INT) { + return OffsetDateTime.ofInstant(Instant.ofEpochSecond(parser.getValueAsLong()), ZoneOffset.UTC); + } else { + TemporalAccessor temporal = DateTimeFormatter.ISO_DATE_TIME.parseBest( + parser.getValueAsString(), OffsetDateTime::from, LocalDateTime::from); + + if (temporal.query(TemporalQueries.offset()) == null) { + return LocalDateTime.from(temporal).atOffset(ZoneOffset.UTC); + } else { + return OffsetDateTime.from(temporal); + } + } + } +} \ No newline at end of file diff --git a/seed/java-spring/java-with-property-conflict/src/main/java/core/ObjectMappers.java b/seed/java-spring/java-with-property-conflict/src/main/java/core/ObjectMappers.java new file mode 100644 index 000000000000..7208607e674e --- /dev/null +++ b/seed/java-spring/java-with-property-conflict/src/main/java/core/ObjectMappers.java @@ -0,0 +1,51 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package core; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import java.io.IOException; +import java.lang.Integer; +import java.lang.Object; +import java.lang.String; + +public final class ObjectMappers { + public static final ObjectMapper JSON_MAPPER = JsonMapper.builder() + .addModule(new Jdk8Module()) + .addModule(new JavaTimeModule()) + .addModule(DateTimeDeserializer.getModule()) + .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .build(); + + private ObjectMappers() { + } + + public static String stringify(Object o) { + try { + return JSON_MAPPER.setSerializationInclusion(JsonInclude.Include.ALWAYS) + .writerWithDefaultPrettyPrinter() + .writeValueAsString(o); + } + catch (IOException e) { + return o.getClass().getName() + "@" + Integer.toHexString(o.hashCode()); + } + } + + public static Object parseErrorBody(String responseBodyString) { + try { + return JSON_MAPPER.readValue(responseBodyString, Object.class); + } + catch (JsonProcessingException ignored) { + return responseBodyString; + } + } + } diff --git a/seed/java-spring/java-with-property-conflict/src/main/java/core/OptionalNullable.java b/seed/java-spring/java-with-property-conflict/src/main/java/core/OptionalNullable.java new file mode 100644 index 000000000000..15415ffefc09 --- /dev/null +++ b/seed/java-spring/java-with-property-conflict/src/main/java/core/OptionalNullable.java @@ -0,0 +1,182 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package core; + +import static java.util.Objects.requireNonNull; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import java.util.Objects; +import java.util.Optional; + +/** + * An optional wrapper type that supports null values, distinguishing between: + * - ABSENT: field not present (e.g., not included in request) + * - NULL: field explicitly set to null + * - PRESENT: field has a non-null value + * + * This is useful for partial updates, JSON Merge Patch (RFC 7396), and any API + * that needs to differentiate between "not specified" and "set to null". + * + * Use this for optional> types where null is a valid value. + * For regular optional types (where null is not valid), use Optional instead. + */ +public final class OptionalNullable { + private final State state; + + private final T value; + + private OptionalNullable(State state, T value) { + this.state = state; + this.value = value; + } + + /** + * Creates an absent OptionalNullable (field not present). + */ + public static OptionalNullable absent() { + return new OptionalNullable<>(State.ABSENT, null); + } + + /** + * Creates a null OptionalNullable (field explicitly set to null). + */ + public static OptionalNullable ofNull() { + return new OptionalNullable<>(State.NULL, null); + } + + /** + * Creates an OptionalNullable with a value. + */ + public static OptionalNullable of(T value) { + requireNonNull(value, "Use ofNull() for null values"); + return new OptionalNullable<>(State.PRESENT, value); + } + + /** + * Creates an OptionalNullable from a nullable value. + */ + @JsonCreator + public static OptionalNullable ofNullable(T value) { + return value == null ? ofNull() : of(value); + } + + /** + * Returns true if the field was absent from the request. + */ + public boolean isAbsent() { + return state == State.ABSENT; + } + + /** + * Returns true if the field was explicitly set to null. + */ + public boolean isNull() { + return state == State.NULL; + } + + /** + * Returns true if the field has a value. + */ + public boolean isPresent() { + return state == State.PRESENT; + } + + /** + * Returns true if the field was present in the request (either null or with a value). + */ + public boolean wasSpecified() { + return state != State.ABSENT; + } + + /** + * Gets the value if present, throws if absent or null. + */ + public T get() { + if (state != State.PRESENT) { + throw new IllegalStateException("Cannot get value from " + state + " OptionalNullable"); + } + return value; + } + + /** + * Gets the value if present or explicitly null, throws if absent. + * This is useful for update operations where null is a valid value to set. + */ + public T getValueOrNull() { + if (state == State.ABSENT) { + throw new IllegalStateException("No value set"); + } + return value; + } + + /** + * Gets the value if present, returns null if explicitly set to null, or returns the provided default if absent. + */ + public T orElse(T defaultValue) { + if (state == State.PRESENT) { + return value; + } + if (state == State.NULL) { + return null; + } + return defaultValue; + } + + /** + * Converts to an Optional, returning empty for both absent and null states. + */ + public Optional toOptional() { + return state == State.PRESENT ? Optional.of(value) : Optional.empty(); + } + + /** + * For JSON serialization - serialize the actual value or null. + * Absent values should be handled by @JsonInclude(JsonInclude.Include.CUSTOM) + */ + @JsonValue + public Object getJsonValue() { + if (state == State.ABSENT) { + // Should not be serialized - handled by custom inclusion + throw new IllegalStateException("Absent values should not be serialized"); + } + return state == State.NULL ? null : value; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + OptionalNullable that = (OptionalNullable) o; + return state == that.state && Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(state, value); + } + + @Override + public String toString() { + switch (state) { + case ABSENT: return "OptionalNullable.absent()"; + case NULL: return "OptionalNullable.ofNull()"; + case PRESENT: return "OptionalNullable.of(" + value + ")"; + default: throw new IllegalStateException("Unknown state: " + state); + } + } + + private enum State { + ABSENT, + + NULL, + + PRESENT + } +} diff --git a/seed/java-spring/java-with-property-conflict/src/main/java/types/ConfigurableProp.java b/seed/java-spring/java-with-property-conflict/src/main/java/types/ConfigurableProp.java new file mode 100644 index 000000000000..d14c5c50f1ce --- /dev/null +++ b/seed/java-spring/java-with-property-conflict/src/main/java/types/ConfigurableProp.java @@ -0,0 +1,244 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package types; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import core.ObjectMappers; +import java.lang.Boolean; +import java.lang.Object; +import java.lang.String; +import java.util.Objects; +import java.util.Optional; +import org.jetbrains.annotations.NotNull; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize( + builder = ConfigurableProp.Builder.class +) +public final class ConfigurableProp { + private final String name; + + private final Optional label; + + private final Optional withLabel; + + private final Optional description; + + private ConfigurableProp(String name, Optional label, Optional withLabel, + Optional description) { + this.name = name; + this.label = label; + this.withLabel = withLabel; + this.description = description; + } + + /** + * @return The name of the prop + */ + @JsonProperty("name") + public String getName() { + return name; + } + + /** + * @return The display label + */ + @JsonProperty("label") + public Optional getLabel() { + return label; + } + + /** + * @return If true, requires label-value format + */ + @JsonProperty("withLabel") + public Optional getWithLabel() { + return withLabel; + } + + /** + * @return A description of the prop + */ + @JsonProperty("description") + public Optional getDescription() { + return description; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof ConfigurableProp && equalTo((ConfigurableProp) other); + } + + private boolean equalTo(ConfigurableProp other) { + return name.equals(other.name) && label.equals(other.label) && withLabel.equals(other.withLabel) && description.equals(other.description); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.name, this.label, this.withLabel, this.description); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static NameStage builder() { + return new Builder(); + } + + public interface NameStage { + /** + *

The name of the prop

+ */ + _FinalStage name(@NotNull String name); + + Builder from(ConfigurableProp other); + } + + public interface _FinalStage { + ConfigurableProp build(); + + /** + *

The display label

+ */ + _FinalStage label(Optional label); + + _FinalStage label(String label); + + /** + *

If true, requires label-value format

+ */ + _FinalStage withLabel(Optional withLabel); + + _FinalStage withLabel(Boolean withLabel); + + /** + *

A description of the prop

+ */ + _FinalStage description(Optional description); + + _FinalStage description(String description); + } + + @JsonIgnoreProperties( + ignoreUnknown = true + ) + public static final class Builder implements NameStage, _FinalStage { + private String name; + + private Optional description = Optional.empty(); + + private Optional withLabel = Optional.empty(); + + private Optional label = Optional.empty(); + + private Builder() { + } + + @java.lang.Override + public Builder from(ConfigurableProp other) { + name(other.getName()); + label(other.getLabel()); + withLabel(other.getWithLabel()); + description(other.getDescription()); + return this; + } + + /** + *

The name of the prop

+ *

The name of the prop

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + @JsonSetter("name") + public _FinalStage name(@NotNull String name) { + this.name = Objects.requireNonNull(name, "name must not be null"); + return this; + } + + /** + *

A description of the prop

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + public _FinalStage description(String description) { + this.description = Optional.ofNullable(description); + return this; + } + + /** + *

A description of the prop

+ */ + @java.lang.Override + @JsonSetter( + value = "description", + nulls = Nulls.SKIP + ) + public _FinalStage description(Optional description) { + this.description = description; + return this; + } + + /** + *

If true, requires label-value format

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + @JsonIgnore + public _FinalStage withLabel(Boolean withLabel) { + this.withLabel = Optional.ofNullable(withLabel); + return this; + } + + /** + *

If true, requires label-value format

+ */ + @java.lang.Override + @JsonSetter( + value = "withLabel", + nulls = Nulls.SKIP + ) + public _FinalStage withLabel(Optional withLabel) { + this.withLabel = withLabel; + return this; + } + + /** + *

The display label

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + public _FinalStage label(String label) { + this.label = Optional.ofNullable(label); + return this; + } + + /** + *

The display label

+ */ + @java.lang.Override + @JsonSetter( + value = "label", + nulls = Nulls.SKIP + ) + public _FinalStage label(Optional label) { + this.label = label; + return this; + } + + @java.lang.Override + public ConfigurableProp build() { + return new ConfigurableProp(name, label, withLabel, description); + } + } +} diff --git a/seed/java-spring/nullable-allof-extends/snippet.json b/seed/java-spring/nullable-allof-extends/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/java-spring/nullable-allof-extends/src/main/java/RootService.java b/seed/java-spring/nullable-allof-extends/src/main/java/RootService.java new file mode 100644 index 000000000000..6236347a406e --- /dev/null +++ b/seed/java-spring/nullable-allof-extends/src/main/java/RootService.java @@ -0,0 +1,27 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import types.RootObject; + +@RequestMapping( + path = "/" +) +public interface RootService { + @GetMapping( + value = "/test", + produces = "application/json" + ) + RootObject getTest(); + + @PostMapping( + value = "/test", + produces = "application/json", + consumes = "application/json" + ) + RootObject createTest(@RequestBody RootObject body); +} diff --git a/seed/java-spring/nullable-allof-extends/src/main/java/core/APIException.java b/seed/java-spring/nullable-allof-extends/src/main/java/core/APIException.java new file mode 100644 index 000000000000..27289cf9b2e4 --- /dev/null +++ b/seed/java-spring/nullable-allof-extends/src/main/java/core/APIException.java @@ -0,0 +1,10 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package core; + +import java.lang.Exception; + +public class APIException extends Exception { +} diff --git a/seed/java-spring/nullable-allof-extends/src/main/java/core/DateTimeDeserializer.java b/seed/java-spring/nullable-allof-extends/src/main/java/core/DateTimeDeserializer.java new file mode 100644 index 000000000000..3d3174aec001 --- /dev/null +++ b/seed/java-spring/nullable-allof-extends/src/main/java/core/DateTimeDeserializer.java @@ -0,0 +1,56 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package core; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.module.SimpleModule; +import java.io.IOException; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalQueries; + +/** + * Custom deserializer that handles converting ISO8601 dates into {@link OffsetDateTime} objects. + */ +class DateTimeDeserializer extends JsonDeserializer { + private static final SimpleModule MODULE; + + static { + MODULE = new SimpleModule().addDeserializer(OffsetDateTime.class, new DateTimeDeserializer()); + } + + /** + * Gets a module wrapping this deserializer as an adapter for the Jackson ObjectMapper. + * + * @return A {@link SimpleModule} to be plugged onto Jackson ObjectMapper. + */ + public static SimpleModule getModule() { + return MODULE; + } + + @Override + public OffsetDateTime deserialize(JsonParser parser, DeserializationContext context) throws IOException { + JsonToken token = parser.currentToken(); + if (token == JsonToken.VALUE_NUMBER_INT) { + return OffsetDateTime.ofInstant(Instant.ofEpochSecond(parser.getValueAsLong()), ZoneOffset.UTC); + } else { + TemporalAccessor temporal = DateTimeFormatter.ISO_DATE_TIME.parseBest( + parser.getValueAsString(), OffsetDateTime::from, LocalDateTime::from); + + if (temporal.query(TemporalQueries.offset()) == null) { + return LocalDateTime.from(temporal).atOffset(ZoneOffset.UTC); + } else { + return OffsetDateTime.from(temporal); + } + } + } +} \ No newline at end of file diff --git a/seed/java-spring/nullable-allof-extends/src/main/java/core/ObjectMappers.java b/seed/java-spring/nullable-allof-extends/src/main/java/core/ObjectMappers.java new file mode 100644 index 000000000000..7208607e674e --- /dev/null +++ b/seed/java-spring/nullable-allof-extends/src/main/java/core/ObjectMappers.java @@ -0,0 +1,51 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package core; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import java.io.IOException; +import java.lang.Integer; +import java.lang.Object; +import java.lang.String; + +public final class ObjectMappers { + public static final ObjectMapper JSON_MAPPER = JsonMapper.builder() + .addModule(new Jdk8Module()) + .addModule(new JavaTimeModule()) + .addModule(DateTimeDeserializer.getModule()) + .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .build(); + + private ObjectMappers() { + } + + public static String stringify(Object o) { + try { + return JSON_MAPPER.setSerializationInclusion(JsonInclude.Include.ALWAYS) + .writerWithDefaultPrettyPrinter() + .writeValueAsString(o); + } + catch (IOException e) { + return o.getClass().getName() + "@" + Integer.toHexString(o.hashCode()); + } + } + + public static Object parseErrorBody(String responseBodyString) { + try { + return JSON_MAPPER.readValue(responseBodyString, Object.class); + } + catch (JsonProcessingException ignored) { + return responseBodyString; + } + } + } diff --git a/seed/java-spring/nullable-allof-extends/src/main/java/core/OptionalNullable.java b/seed/java-spring/nullable-allof-extends/src/main/java/core/OptionalNullable.java new file mode 100644 index 000000000000..15415ffefc09 --- /dev/null +++ b/seed/java-spring/nullable-allof-extends/src/main/java/core/OptionalNullable.java @@ -0,0 +1,182 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package core; + +import static java.util.Objects.requireNonNull; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import java.util.Objects; +import java.util.Optional; + +/** + * An optional wrapper type that supports null values, distinguishing between: + * - ABSENT: field not present (e.g., not included in request) + * - NULL: field explicitly set to null + * - PRESENT: field has a non-null value + * + * This is useful for partial updates, JSON Merge Patch (RFC 7396), and any API + * that needs to differentiate between "not specified" and "set to null". + * + * Use this for optional> types where null is a valid value. + * For regular optional types (where null is not valid), use Optional instead. + */ +public final class OptionalNullable { + private final State state; + + private final T value; + + private OptionalNullable(State state, T value) { + this.state = state; + this.value = value; + } + + /** + * Creates an absent OptionalNullable (field not present). + */ + public static OptionalNullable absent() { + return new OptionalNullable<>(State.ABSENT, null); + } + + /** + * Creates a null OptionalNullable (field explicitly set to null). + */ + public static OptionalNullable ofNull() { + return new OptionalNullable<>(State.NULL, null); + } + + /** + * Creates an OptionalNullable with a value. + */ + public static OptionalNullable of(T value) { + requireNonNull(value, "Use ofNull() for null values"); + return new OptionalNullable<>(State.PRESENT, value); + } + + /** + * Creates an OptionalNullable from a nullable value. + */ + @JsonCreator + public static OptionalNullable ofNullable(T value) { + return value == null ? ofNull() : of(value); + } + + /** + * Returns true if the field was absent from the request. + */ + public boolean isAbsent() { + return state == State.ABSENT; + } + + /** + * Returns true if the field was explicitly set to null. + */ + public boolean isNull() { + return state == State.NULL; + } + + /** + * Returns true if the field has a value. + */ + public boolean isPresent() { + return state == State.PRESENT; + } + + /** + * Returns true if the field was present in the request (either null or with a value). + */ + public boolean wasSpecified() { + return state != State.ABSENT; + } + + /** + * Gets the value if present, throws if absent or null. + */ + public T get() { + if (state != State.PRESENT) { + throw new IllegalStateException("Cannot get value from " + state + " OptionalNullable"); + } + return value; + } + + /** + * Gets the value if present or explicitly null, throws if absent. + * This is useful for update operations where null is a valid value to set. + */ + public T getValueOrNull() { + if (state == State.ABSENT) { + throw new IllegalStateException("No value set"); + } + return value; + } + + /** + * Gets the value if present, returns null if explicitly set to null, or returns the provided default if absent. + */ + public T orElse(T defaultValue) { + if (state == State.PRESENT) { + return value; + } + if (state == State.NULL) { + return null; + } + return defaultValue; + } + + /** + * Converts to an Optional, returning empty for both absent and null states. + */ + public Optional toOptional() { + return state == State.PRESENT ? Optional.of(value) : Optional.empty(); + } + + /** + * For JSON serialization - serialize the actual value or null. + * Absent values should be handled by @JsonInclude(JsonInclude.Include.CUSTOM) + */ + @JsonValue + public Object getJsonValue() { + if (state == State.ABSENT) { + // Should not be serialized - handled by custom inclusion + throw new IllegalStateException("Absent values should not be serialized"); + } + return state == State.NULL ? null : value; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + OptionalNullable that = (OptionalNullable) o; + return state == that.state && Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(state, value); + } + + @Override + public String toString() { + switch (state) { + case ABSENT: return "OptionalNullable.absent()"; + case NULL: return "OptionalNullable.ofNull()"; + case PRESENT: return "OptionalNullable.of(" + value + ")"; + default: throw new IllegalStateException("Unknown state: " + state); + } + } + + private enum State { + ABSENT, + + NULL, + + PRESENT + } +} diff --git a/seed/java-spring/nullable-allof-extends/src/main/java/types/INormalObject.java b/seed/java-spring/nullable-allof-extends/src/main/java/types/INormalObject.java new file mode 100644 index 000000000000..37f5b6cd086d --- /dev/null +++ b/seed/java-spring/nullable-allof-extends/src/main/java/types/INormalObject.java @@ -0,0 +1,12 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package types; + +import java.lang.String; +import java.util.Optional; + +public interface INormalObject { + Optional getNormalField(); +} diff --git a/seed/java-spring/nullable-allof-extends/src/main/java/types/INullableObject.java b/seed/java-spring/nullable-allof-extends/src/main/java/types/INullableObject.java new file mode 100644 index 000000000000..0fdeceb80c3b --- /dev/null +++ b/seed/java-spring/nullable-allof-extends/src/main/java/types/INullableObject.java @@ -0,0 +1,12 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package types; + +import java.lang.String; +import java.util.Optional; + +public interface INullableObject { + Optional getNullableField(); +} diff --git a/seed/java-spring/nullable-allof-extends/src/main/java/types/NormalObject.java b/seed/java-spring/nullable-allof-extends/src/main/java/types/NormalObject.java new file mode 100644 index 000000000000..16f4ced8f3aa --- /dev/null +++ b/seed/java-spring/nullable-allof-extends/src/main/java/types/NormalObject.java @@ -0,0 +1,92 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package types; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import core.ObjectMappers; +import java.lang.Object; +import java.lang.String; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize( + builder = NormalObject.Builder.class +) +public final class NormalObject implements INormalObject { + private final Optional normalField; + + private NormalObject(Optional normalField) { + this.normalField = normalField; + } + + @JsonProperty("normalField") + @java.lang.Override + public Optional getNormalField() { + return normalField; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof NormalObject && equalTo((NormalObject) other); + } + + private boolean equalTo(NormalObject other) { + return normalField.equals(other.normalField); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.normalField); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties( + ignoreUnknown = true + ) + public static final class Builder { + private Optional normalField = Optional.empty(); + + private Builder() { + } + + public Builder from(NormalObject other) { + normalField(other.getNormalField()); + return this; + } + + @JsonSetter( + value = "normalField", + nulls = Nulls.SKIP + ) + public Builder normalField(Optional normalField) { + this.normalField = normalField; + return this; + } + + public Builder normalField(String normalField) { + this.normalField = Optional.ofNullable(normalField); + return this; + } + + public NormalObject build() { + return new NormalObject(normalField); + } + } +} diff --git a/seed/java-spring/nullable-allof-extends/src/main/java/types/NullableObject.java b/seed/java-spring/nullable-allof-extends/src/main/java/types/NullableObject.java new file mode 100644 index 000000000000..108bce0ee09d --- /dev/null +++ b/seed/java-spring/nullable-allof-extends/src/main/java/types/NullableObject.java @@ -0,0 +1,92 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package types; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import core.ObjectMappers; +import java.lang.Object; +import java.lang.String; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize( + builder = NullableObject.Builder.class +) +public final class NullableObject implements INullableObject { + private final Optional nullableField; + + private NullableObject(Optional nullableField) { + this.nullableField = nullableField; + } + + @JsonProperty("nullableField") + @java.lang.Override + public Optional getNullableField() { + return nullableField; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof NullableObject && equalTo((NullableObject) other); + } + + private boolean equalTo(NullableObject other) { + return nullableField.equals(other.nullableField); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.nullableField); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties( + ignoreUnknown = true + ) + public static final class Builder { + private Optional nullableField = Optional.empty(); + + private Builder() { + } + + public Builder from(NullableObject other) { + nullableField(other.getNullableField()); + return this; + } + + @JsonSetter( + value = "nullableField", + nulls = Nulls.SKIP + ) + public Builder nullableField(Optional nullableField) { + this.nullableField = nullableField; + return this; + } + + public Builder nullableField(String nullableField) { + this.nullableField = Optional.ofNullable(nullableField); + return this; + } + + public NullableObject build() { + return new NullableObject(nullableField); + } + } +} diff --git a/seed/java-spring/nullable-allof-extends/src/main/java/types/RootObject.java b/seed/java-spring/nullable-allof-extends/src/main/java/types/RootObject.java new file mode 100644 index 000000000000..09f7f114a27b --- /dev/null +++ b/seed/java-spring/nullable-allof-extends/src/main/java/types/RootObject.java @@ -0,0 +1,118 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package types; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import core.ObjectMappers; +import java.lang.Object; +import java.lang.String; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize( + builder = RootObject.Builder.class +) +public final class RootObject implements INormalObject, INullableObject { + private final Optional normalField; + + private final Optional nullableField; + + private RootObject(Optional normalField, Optional nullableField) { + this.normalField = normalField; + this.nullableField = nullableField; + } + + @JsonProperty("normalField") + @java.lang.Override + public Optional getNormalField() { + return normalField; + } + + @JsonProperty("nullableField") + @java.lang.Override + public Optional getNullableField() { + return nullableField; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof RootObject && equalTo((RootObject) other); + } + + private boolean equalTo(RootObject other) { + return normalField.equals(other.normalField) && nullableField.equals(other.nullableField); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.normalField, this.nullableField); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties( + ignoreUnknown = true + ) + public static final class Builder { + private Optional normalField = Optional.empty(); + + private Optional nullableField = Optional.empty(); + + private Builder() { + } + + public Builder from(RootObject other) { + normalField(other.getNormalField()); + nullableField(other.getNullableField()); + return this; + } + + @JsonSetter( + value = "normalField", + nulls = Nulls.SKIP + ) + public Builder normalField(Optional normalField) { + this.normalField = normalField; + return this; + } + + public Builder normalField(String normalField) { + this.normalField = Optional.ofNullable(normalField); + return this; + } + + @JsonSetter( + value = "nullableField", + nulls = Nulls.SKIP + ) + public Builder nullableField(Optional nullableField) { + this.nullableField = nullableField; + return this; + } + + public Builder nullableField(String nullableField) { + this.nullableField = Optional.ofNullable(nullableField); + return this; + } + + public RootObject build() { + return new RootObject(normalField, nullableField); + } + } +} diff --git a/seed/java-spring/oauth-client-credentials-mandatory-auth/snippet.json b/seed/java-spring/oauth-client-credentials-mandatory-auth/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/java-spring/oauth-client-credentials-mandatory-auth/src/main/java/core/APIException.java b/seed/java-spring/oauth-client-credentials-mandatory-auth/src/main/java/core/APIException.java new file mode 100644 index 000000000000..27289cf9b2e4 --- /dev/null +++ b/seed/java-spring/oauth-client-credentials-mandatory-auth/src/main/java/core/APIException.java @@ -0,0 +1,10 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package core; + +import java.lang.Exception; + +public class APIException extends Exception { +} diff --git a/seed/java-spring/oauth-client-credentials-mandatory-auth/src/main/java/core/BearerAuth.java b/seed/java-spring/oauth-client-credentials-mandatory-auth/src/main/java/core/BearerAuth.java new file mode 100644 index 000000000000..58a05842c2fc --- /dev/null +++ b/seed/java-spring/oauth-client-credentials-mandatory-auth/src/main/java/core/BearerAuth.java @@ -0,0 +1,30 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package core; + +import com.fasterxml.jackson.annotation.JsonValue; +import java.lang.String; + +public final class BearerAuth { + private final String token; + + private BearerAuth(String token) { + this.token = token; + } + + @JsonValue + public String getToken() { + return token; + } + + @java.lang.Override + public String toString() { + return "Bearer " + getToken(); + } + + public static BearerAuth of(String token) { + return new BearerAuth(token.startsWith("Bearer ") ? token.substring(7) : token); + } +} diff --git a/seed/java-spring/oauth-client-credentials-mandatory-auth/src/main/java/core/DateTimeDeserializer.java b/seed/java-spring/oauth-client-credentials-mandatory-auth/src/main/java/core/DateTimeDeserializer.java new file mode 100644 index 000000000000..3d3174aec001 --- /dev/null +++ b/seed/java-spring/oauth-client-credentials-mandatory-auth/src/main/java/core/DateTimeDeserializer.java @@ -0,0 +1,56 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package core; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.module.SimpleModule; +import java.io.IOException; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalQueries; + +/** + * Custom deserializer that handles converting ISO8601 dates into {@link OffsetDateTime} objects. + */ +class DateTimeDeserializer extends JsonDeserializer { + private static final SimpleModule MODULE; + + static { + MODULE = new SimpleModule().addDeserializer(OffsetDateTime.class, new DateTimeDeserializer()); + } + + /** + * Gets a module wrapping this deserializer as an adapter for the Jackson ObjectMapper. + * + * @return A {@link SimpleModule} to be plugged onto Jackson ObjectMapper. + */ + public static SimpleModule getModule() { + return MODULE; + } + + @Override + public OffsetDateTime deserialize(JsonParser parser, DeserializationContext context) throws IOException { + JsonToken token = parser.currentToken(); + if (token == JsonToken.VALUE_NUMBER_INT) { + return OffsetDateTime.ofInstant(Instant.ofEpochSecond(parser.getValueAsLong()), ZoneOffset.UTC); + } else { + TemporalAccessor temporal = DateTimeFormatter.ISO_DATE_TIME.parseBest( + parser.getValueAsString(), OffsetDateTime::from, LocalDateTime::from); + + if (temporal.query(TemporalQueries.offset()) == null) { + return LocalDateTime.from(temporal).atOffset(ZoneOffset.UTC); + } else { + return OffsetDateTime.from(temporal); + } + } + } +} \ No newline at end of file diff --git a/seed/java-spring/oauth-client-credentials-mandatory-auth/src/main/java/core/ObjectMappers.java b/seed/java-spring/oauth-client-credentials-mandatory-auth/src/main/java/core/ObjectMappers.java new file mode 100644 index 000000000000..7208607e674e --- /dev/null +++ b/seed/java-spring/oauth-client-credentials-mandatory-auth/src/main/java/core/ObjectMappers.java @@ -0,0 +1,51 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package core; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import java.io.IOException; +import java.lang.Integer; +import java.lang.Object; +import java.lang.String; + +public final class ObjectMappers { + public static final ObjectMapper JSON_MAPPER = JsonMapper.builder() + .addModule(new Jdk8Module()) + .addModule(new JavaTimeModule()) + .addModule(DateTimeDeserializer.getModule()) + .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .build(); + + private ObjectMappers() { + } + + public static String stringify(Object o) { + try { + return JSON_MAPPER.setSerializationInclusion(JsonInclude.Include.ALWAYS) + .writerWithDefaultPrettyPrinter() + .writeValueAsString(o); + } + catch (IOException e) { + return o.getClass().getName() + "@" + Integer.toHexString(o.hashCode()); + } + } + + public static Object parseErrorBody(String responseBodyString) { + try { + return JSON_MAPPER.readValue(responseBodyString, Object.class); + } + catch (JsonProcessingException ignored) { + return responseBodyString; + } + } + } diff --git a/seed/java-spring/oauth-client-credentials-mandatory-auth/src/main/java/core/OptionalNullable.java b/seed/java-spring/oauth-client-credentials-mandatory-auth/src/main/java/core/OptionalNullable.java new file mode 100644 index 000000000000..15415ffefc09 --- /dev/null +++ b/seed/java-spring/oauth-client-credentials-mandatory-auth/src/main/java/core/OptionalNullable.java @@ -0,0 +1,182 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package core; + +import static java.util.Objects.requireNonNull; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import java.util.Objects; +import java.util.Optional; + +/** + * An optional wrapper type that supports null values, distinguishing between: + * - ABSENT: field not present (e.g., not included in request) + * - NULL: field explicitly set to null + * - PRESENT: field has a non-null value + * + * This is useful for partial updates, JSON Merge Patch (RFC 7396), and any API + * that needs to differentiate between "not specified" and "set to null". + * + * Use this for optional> types where null is a valid value. + * For regular optional types (where null is not valid), use Optional instead. + */ +public final class OptionalNullable { + private final State state; + + private final T value; + + private OptionalNullable(State state, T value) { + this.state = state; + this.value = value; + } + + /** + * Creates an absent OptionalNullable (field not present). + */ + public static OptionalNullable absent() { + return new OptionalNullable<>(State.ABSENT, null); + } + + /** + * Creates a null OptionalNullable (field explicitly set to null). + */ + public static OptionalNullable ofNull() { + return new OptionalNullable<>(State.NULL, null); + } + + /** + * Creates an OptionalNullable with a value. + */ + public static OptionalNullable of(T value) { + requireNonNull(value, "Use ofNull() for null values"); + return new OptionalNullable<>(State.PRESENT, value); + } + + /** + * Creates an OptionalNullable from a nullable value. + */ + @JsonCreator + public static OptionalNullable ofNullable(T value) { + return value == null ? ofNull() : of(value); + } + + /** + * Returns true if the field was absent from the request. + */ + public boolean isAbsent() { + return state == State.ABSENT; + } + + /** + * Returns true if the field was explicitly set to null. + */ + public boolean isNull() { + return state == State.NULL; + } + + /** + * Returns true if the field has a value. + */ + public boolean isPresent() { + return state == State.PRESENT; + } + + /** + * Returns true if the field was present in the request (either null or with a value). + */ + public boolean wasSpecified() { + return state != State.ABSENT; + } + + /** + * Gets the value if present, throws if absent or null. + */ + public T get() { + if (state != State.PRESENT) { + throw new IllegalStateException("Cannot get value from " + state + " OptionalNullable"); + } + return value; + } + + /** + * Gets the value if present or explicitly null, throws if absent. + * This is useful for update operations where null is a valid value to set. + */ + public T getValueOrNull() { + if (state == State.ABSENT) { + throw new IllegalStateException("No value set"); + } + return value; + } + + /** + * Gets the value if present, returns null if explicitly set to null, or returns the provided default if absent. + */ + public T orElse(T defaultValue) { + if (state == State.PRESENT) { + return value; + } + if (state == State.NULL) { + return null; + } + return defaultValue; + } + + /** + * Converts to an Optional, returning empty for both absent and null states. + */ + public Optional toOptional() { + return state == State.PRESENT ? Optional.of(value) : Optional.empty(); + } + + /** + * For JSON serialization - serialize the actual value or null. + * Absent values should be handled by @JsonInclude(JsonInclude.Include.CUSTOM) + */ + @JsonValue + public Object getJsonValue() { + if (state == State.ABSENT) { + // Should not be serialized - handled by custom inclusion + throw new IllegalStateException("Absent values should not be serialized"); + } + return state == State.NULL ? null : value; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + OptionalNullable that = (OptionalNullable) o; + return state == that.state && Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(state, value); + } + + @Override + public String toString() { + switch (state) { + case ABSENT: return "OptionalNullable.absent()"; + case NULL: return "OptionalNullable.ofNull()"; + case PRESENT: return "OptionalNullable.of(" + value + ")"; + default: throw new IllegalStateException("Unknown state: " + state); + } + } + + private enum State { + ABSENT, + + NULL, + + PRESENT + } +} diff --git a/seed/java-spring/oauth-client-credentials-mandatory-auth/src/main/java/resources/auth/AuthService.java b/seed/java-spring/oauth-client-credentials-mandatory-auth/src/main/java/resources/auth/AuthService.java new file mode 100644 index 000000000000..190e5bbb02ca --- /dev/null +++ b/seed/java-spring/oauth-client-credentials-mandatory-auth/src/main/java/resources/auth/AuthService.java @@ -0,0 +1,31 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package resources.auth; + +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import resources.auth.requests.GetTokenRequest; +import resources.auth.requests.RefreshTokenRequest; +import resources.auth.types.TokenResponse; + +@RequestMapping( + path = "/" +) +public interface AuthService { + @PostMapping( + value = "/token", + produces = "application/json", + consumes = "application/json" + ) + TokenResponse getTokenWithClientCredentials(@RequestBody GetTokenRequest body); + + @PostMapping( + value = "/token", + produces = "application/json", + consumes = "application/json" + ) + TokenResponse refreshToken(@RequestBody RefreshTokenRequest body); +} diff --git a/seed/java-spring/oauth-client-credentials-mandatory-auth/src/main/java/resources/auth/requests/GetTokenRequest.java b/seed/java-spring/oauth-client-credentials-mandatory-auth/src/main/java/resources/auth/requests/GetTokenRequest.java new file mode 100644 index 000000000000..c88c86224393 --- /dev/null +++ b/seed/java-spring/oauth-client-credentials-mandatory-auth/src/main/java/resources/auth/requests/GetTokenRequest.java @@ -0,0 +1,160 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package resources.auth.requests; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import core.ObjectMappers; +import java.lang.Object; +import java.lang.String; +import java.util.Objects; +import java.util.Optional; +import org.jetbrains.annotations.NotNull; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize( + builder = GetTokenRequest.Builder.class +) +public final class GetTokenRequest { + private final String clientId; + + private final String clientSecret; + + private final Optional scope; + + private GetTokenRequest(String clientId, String clientSecret, Optional scope) { + this.clientId = clientId; + this.clientSecret = clientSecret; + this.scope = scope; + } + + @JsonProperty("client_id") + public String getClientId() { + return clientId; + } + + @JsonProperty("client_secret") + public String getClientSecret() { + return clientSecret; + } + + @JsonProperty("audience") + public String getAudience() { + return "https://api.example.com"; + } + + @JsonProperty("grant_type") + public String getGrantType() { + return "client_credentials"; + } + + @JsonProperty("scope") + public Optional getScope() { + return scope; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof GetTokenRequest && equalTo((GetTokenRequest) other); + } + + private boolean equalTo(GetTokenRequest other) { + return clientId.equals(other.clientId) && clientSecret.equals(other.clientSecret) && scope.equals(other.scope); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.clientId, this.clientSecret, this.scope); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static ClientIdStage builder() { + return new Builder(); + } + + public interface ClientIdStage { + ClientSecretStage clientId(@NotNull String clientId); + + Builder from(GetTokenRequest other); + } + + public interface ClientSecretStage { + _FinalStage clientSecret(@NotNull String clientSecret); + } + + public interface _FinalStage { + GetTokenRequest build(); + + _FinalStage scope(Optional scope); + + _FinalStage scope(String scope); + } + + @JsonIgnoreProperties( + ignoreUnknown = true + ) + public static final class Builder implements ClientIdStage, ClientSecretStage, _FinalStage { + private String clientId; + + private String clientSecret; + + private Optional scope = Optional.empty(); + + private Builder() { + } + + @java.lang.Override + public Builder from(GetTokenRequest other) { + clientId(other.getClientId()); + clientSecret(other.getClientSecret()); + scope(other.getScope()); + return this; + } + + @java.lang.Override + @JsonSetter("client_id") + public ClientSecretStage clientId(@NotNull String clientId) { + this.clientId = Objects.requireNonNull(clientId, "clientId must not be null"); + return this; + } + + @java.lang.Override + @JsonSetter("client_secret") + public _FinalStage clientSecret(@NotNull String clientSecret) { + this.clientSecret = Objects.requireNonNull(clientSecret, "clientSecret must not be null"); + return this; + } + + @java.lang.Override + public _FinalStage scope(String scope) { + this.scope = Optional.ofNullable(scope); + return this; + } + + @java.lang.Override + @JsonSetter( + value = "scope", + nulls = Nulls.SKIP + ) + public _FinalStage scope(Optional scope) { + this.scope = scope; + return this; + } + + @java.lang.Override + public GetTokenRequest build() { + return new GetTokenRequest(clientId, clientSecret, scope); + } + } +} diff --git a/seed/java-spring/oauth-client-credentials-mandatory-auth/src/main/java/resources/auth/requests/RefreshTokenRequest.java b/seed/java-spring/oauth-client-credentials-mandatory-auth/src/main/java/resources/auth/requests/RefreshTokenRequest.java new file mode 100644 index 000000000000..5d1dda4f9839 --- /dev/null +++ b/seed/java-spring/oauth-client-credentials-mandatory-auth/src/main/java/resources/auth/requests/RefreshTokenRequest.java @@ -0,0 +1,183 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package resources.auth.requests; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import core.ObjectMappers; +import java.lang.Object; +import java.lang.String; +import java.util.Objects; +import java.util.Optional; +import org.jetbrains.annotations.NotNull; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize( + builder = RefreshTokenRequest.Builder.class +) +public final class RefreshTokenRequest { + private final String clientId; + + private final String clientSecret; + + private final String refreshToken; + + private final Optional scope; + + private RefreshTokenRequest(String clientId, String clientSecret, String refreshToken, + Optional scope) { + this.clientId = clientId; + this.clientSecret = clientSecret; + this.refreshToken = refreshToken; + this.scope = scope; + } + + @JsonProperty("client_id") + public String getClientId() { + return clientId; + } + + @JsonProperty("client_secret") + public String getClientSecret() { + return clientSecret; + } + + @JsonProperty("refresh_token") + public String getRefreshToken() { + return refreshToken; + } + + @JsonProperty("audience") + public String getAudience() { + return "https://api.example.com"; + } + + @JsonProperty("grant_type") + public String getGrantType() { + return "refresh_token"; + } + + @JsonProperty("scope") + public Optional getScope() { + return scope; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof RefreshTokenRequest && equalTo((RefreshTokenRequest) other); + } + + private boolean equalTo(RefreshTokenRequest other) { + return clientId.equals(other.clientId) && clientSecret.equals(other.clientSecret) && refreshToken.equals(other.refreshToken) && scope.equals(other.scope); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.clientId, this.clientSecret, this.refreshToken, this.scope); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static ClientIdStage builder() { + return new Builder(); + } + + public interface ClientIdStage { + ClientSecretStage clientId(@NotNull String clientId); + + Builder from(RefreshTokenRequest other); + } + + public interface ClientSecretStage { + RefreshTokenStage clientSecret(@NotNull String clientSecret); + } + + public interface RefreshTokenStage { + _FinalStage refreshToken(@NotNull String refreshToken); + } + + public interface _FinalStage { + RefreshTokenRequest build(); + + _FinalStage scope(Optional scope); + + _FinalStage scope(String scope); + } + + @JsonIgnoreProperties( + ignoreUnknown = true + ) + public static final class Builder implements ClientIdStage, ClientSecretStage, RefreshTokenStage, _FinalStage { + private String clientId; + + private String clientSecret; + + private String refreshToken; + + private Optional scope = Optional.empty(); + + private Builder() { + } + + @java.lang.Override + public Builder from(RefreshTokenRequest other) { + clientId(other.getClientId()); + clientSecret(other.getClientSecret()); + refreshToken(other.getRefreshToken()); + scope(other.getScope()); + return this; + } + + @java.lang.Override + @JsonSetter("client_id") + public ClientSecretStage clientId(@NotNull String clientId) { + this.clientId = Objects.requireNonNull(clientId, "clientId must not be null"); + return this; + } + + @java.lang.Override + @JsonSetter("client_secret") + public RefreshTokenStage clientSecret(@NotNull String clientSecret) { + this.clientSecret = Objects.requireNonNull(clientSecret, "clientSecret must not be null"); + return this; + } + + @java.lang.Override + @JsonSetter("refresh_token") + public _FinalStage refreshToken(@NotNull String refreshToken) { + this.refreshToken = Objects.requireNonNull(refreshToken, "refreshToken must not be null"); + return this; + } + + @java.lang.Override + public _FinalStage scope(String scope) { + this.scope = Optional.ofNullable(scope); + return this; + } + + @java.lang.Override + @JsonSetter( + value = "scope", + nulls = Nulls.SKIP + ) + public _FinalStage scope(Optional scope) { + this.scope = scope; + return this; + } + + @java.lang.Override + public RefreshTokenRequest build() { + return new RefreshTokenRequest(clientId, clientSecret, refreshToken, scope); + } + } +} diff --git a/seed/java-spring/oauth-client-credentials-mandatory-auth/src/main/java/resources/auth/types/TokenResponse.java b/seed/java-spring/oauth-client-credentials-mandatory-auth/src/main/java/resources/auth/types/TokenResponse.java new file mode 100644 index 000000000000..8f337f6338d2 --- /dev/null +++ b/seed/java-spring/oauth-client-credentials-mandatory-auth/src/main/java/resources/auth/types/TokenResponse.java @@ -0,0 +1,150 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package resources.auth.types; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import core.ObjectMappers; +import java.lang.Object; +import java.lang.String; +import java.util.Objects; +import java.util.Optional; +import org.jetbrains.annotations.NotNull; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize( + builder = TokenResponse.Builder.class +) +public final class TokenResponse { + private final String accessToken; + + private final int expiresIn; + + private final Optional refreshToken; + + private TokenResponse(String accessToken, int expiresIn, Optional refreshToken) { + this.accessToken = accessToken; + this.expiresIn = expiresIn; + this.refreshToken = refreshToken; + } + + @JsonProperty("access_token") + public String getAccessToken() { + return accessToken; + } + + @JsonProperty("expires_in") + public int getExpiresIn() { + return expiresIn; + } + + @JsonProperty("refresh_token") + public Optional getRefreshToken() { + return refreshToken; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof TokenResponse && equalTo((TokenResponse) other); + } + + private boolean equalTo(TokenResponse other) { + return accessToken.equals(other.accessToken) && expiresIn == other.expiresIn && refreshToken.equals(other.refreshToken); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.accessToken, this.expiresIn, this.refreshToken); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static AccessTokenStage builder() { + return new Builder(); + } + + public interface AccessTokenStage { + ExpiresInStage accessToken(@NotNull String accessToken); + + Builder from(TokenResponse other); + } + + public interface ExpiresInStage { + _FinalStage expiresIn(int expiresIn); + } + + public interface _FinalStage { + TokenResponse build(); + + _FinalStage refreshToken(Optional refreshToken); + + _FinalStage refreshToken(String refreshToken); + } + + @JsonIgnoreProperties( + ignoreUnknown = true + ) + public static final class Builder implements AccessTokenStage, ExpiresInStage, _FinalStage { + private String accessToken; + + private int expiresIn; + + private Optional refreshToken = Optional.empty(); + + private Builder() { + } + + @java.lang.Override + public Builder from(TokenResponse other) { + accessToken(other.getAccessToken()); + expiresIn(other.getExpiresIn()); + refreshToken(other.getRefreshToken()); + return this; + } + + @java.lang.Override + @JsonSetter("access_token") + public ExpiresInStage accessToken(@NotNull String accessToken) { + this.accessToken = Objects.requireNonNull(accessToken, "accessToken must not be null"); + return this; + } + + @java.lang.Override + @JsonSetter("expires_in") + public _FinalStage expiresIn(int expiresIn) { + this.expiresIn = expiresIn; + return this; + } + + @java.lang.Override + public _FinalStage refreshToken(String refreshToken) { + this.refreshToken = Optional.ofNullable(refreshToken); + return this; + } + + @java.lang.Override + @JsonSetter( + value = "refresh_token", + nulls = Nulls.SKIP + ) + public _FinalStage refreshToken(Optional refreshToken) { + this.refreshToken = refreshToken; + return this; + } + + @java.lang.Override + public TokenResponse build() { + return new TokenResponse(accessToken, expiresIn, refreshToken); + } + } +} diff --git a/seed/java-spring/oauth-client-credentials-mandatory-auth/src/main/java/resources/nested/api/ApiService.java b/seed/java-spring/oauth-client-credentials-mandatory-auth/src/main/java/resources/nested/api/ApiService.java new file mode 100644 index 000000000000..08235152c190 --- /dev/null +++ b/seed/java-spring/oauth-client-credentials-mandatory-auth/src/main/java/resources/nested/api/ApiService.java @@ -0,0 +1,22 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package resources.nested.api; + +import core.BearerAuth; +import java.security.Principal; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; + +@RequestMapping( + path = "/nested" +) +public interface ApiService { + @GetMapping( + value = "/get-something", + produces = "application/json" + ) + void getSomething(@RequestHeader("Authorization") BearerAuth auth, Principal principal); +} diff --git a/seed/java-spring/oauth-client-credentials-mandatory-auth/src/main/java/resources/simple/SimpleService.java b/seed/java-spring/oauth-client-credentials-mandatory-auth/src/main/java/resources/simple/SimpleService.java new file mode 100644 index 000000000000..75a6b8fab1f2 --- /dev/null +++ b/seed/java-spring/oauth-client-credentials-mandatory-auth/src/main/java/resources/simple/SimpleService.java @@ -0,0 +1,22 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package resources.simple; + +import core.BearerAuth; +import java.security.Principal; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; + +@RequestMapping( + path = "/" +) +public interface SimpleService { + @GetMapping( + value = "/get-something", + produces = "application/json" + ) + void getSomething(@RequestHeader("Authorization") BearerAuth auth, Principal principal); +} diff --git a/seed/java-spring/pagination/src/main/java/resources/users/UsersService.java b/seed/java-spring/pagination/src/main/java/resources/users/UsersService.java index 17d10a812fc2..b775af00e5e8 100644 --- a/seed/java-spring/pagination/src/main/java/resources/users/UsersService.java +++ b/seed/java-spring/pagination/src/main/java/resources/users/UsersService.java @@ -19,6 +19,7 @@ import resources.users.types.ListUsersExtendedOptionalListResponse; import resources.users.types.ListUsersExtendedResponse; import resources.users.types.ListUsersMixedTypePaginationResponse; +import resources.users.types.ListUsersOptionalDataPaginationResponse; import resources.users.types.ListUsersPaginationResponse; import resources.users.types.Order; import resources.users.types.UsernameContainer; @@ -126,4 +127,11 @@ Optional listUsernamesWithOptionalResponse( produces = "application/json" ) UsernameContainer listWithGlobalConfig(@RequestParam("offset") Optional offset); + + @GetMapping( + value = "/optional-data", + produces = "application/json" + ) + ListUsersOptionalDataPaginationResponse listWithOptionalData( + @RequestParam("page") Optional page); } diff --git a/seed/java-spring/pagination/src/main/java/resources/users/types/ListUsersOptionalDataPaginationResponse.java b/seed/java-spring/pagination/src/main/java/resources/users/types/ListUsersOptionalDataPaginationResponse.java new file mode 100644 index 000000000000..850c67c5321b --- /dev/null +++ b/seed/java-spring/pagination/src/main/java/resources/users/types/ListUsersOptionalDataPaginationResponse.java @@ -0,0 +1,203 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package resources.users.types; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import core.ObjectMappers; +import java.lang.Boolean; +import java.lang.Object; +import java.lang.String; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize( + builder = ListUsersOptionalDataPaginationResponse.Builder.class +) +public final class ListUsersOptionalDataPaginationResponse { + private final Optional hasNextPage; + + private final Optional page; + + private final int totalCount; + + private final Optional> data; + + private ListUsersOptionalDataPaginationResponse(Optional hasNextPage, + Optional page, int totalCount, Optional> data) { + this.hasNextPage = hasNextPage; + this.page = page; + this.totalCount = totalCount; + this.data = data; + } + + @JsonProperty("hasNextPage") + public Optional getHasNextPage() { + return hasNextPage; + } + + @JsonProperty("page") + public Optional getPage() { + return page; + } + + /** + * @return The totall number of /users + */ + @JsonProperty("total_count") + public int getTotalCount() { + return totalCount; + } + + @JsonProperty("data") + public Optional> getData() { + return data; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof ListUsersOptionalDataPaginationResponse && equalTo((ListUsersOptionalDataPaginationResponse) other); + } + + private boolean equalTo(ListUsersOptionalDataPaginationResponse other) { + return hasNextPage.equals(other.hasNextPage) && page.equals(other.page) && totalCount == other.totalCount && data.equals(other.data); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.hasNextPage, this.page, this.totalCount, this.data); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static TotalCountStage builder() { + return new Builder(); + } + + public interface TotalCountStage { + /** + *

The totall number of /users

+ */ + _FinalStage totalCount(int totalCount); + + Builder from(ListUsersOptionalDataPaginationResponse other); + } + + public interface _FinalStage { + ListUsersOptionalDataPaginationResponse build(); + + _FinalStage hasNextPage(Optional hasNextPage); + + _FinalStage hasNextPage(Boolean hasNextPage); + + _FinalStage page(Optional page); + + _FinalStage page(Page page); + + _FinalStage data(Optional> data); + + _FinalStage data(List data); + } + + @JsonIgnoreProperties( + ignoreUnknown = true + ) + public static final class Builder implements TotalCountStage, _FinalStage { + private int totalCount; + + private Optional> data = Optional.empty(); + + private Optional page = Optional.empty(); + + private Optional hasNextPage = Optional.empty(); + + private Builder() { + } + + @java.lang.Override + public Builder from(ListUsersOptionalDataPaginationResponse other) { + hasNextPage(other.getHasNextPage()); + page(other.getPage()); + totalCount(other.getTotalCount()); + data(other.getData()); + return this; + } + + /** + *

The totall number of /users

+ *

The totall number of /users

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + @JsonSetter("total_count") + public _FinalStage totalCount(int totalCount) { + this.totalCount = totalCount; + return this; + } + + @java.lang.Override + public _FinalStage data(List data) { + this.data = Optional.ofNullable(data); + return this; + } + + @java.lang.Override + @JsonSetter( + value = "data", + nulls = Nulls.SKIP + ) + public _FinalStage data(Optional> data) { + this.data = data; + return this; + } + + @java.lang.Override + public _FinalStage page(Page page) { + this.page = Optional.ofNullable(page); + return this; + } + + @java.lang.Override + @JsonSetter( + value = "page", + nulls = Nulls.SKIP + ) + public _FinalStage page(Optional page) { + this.page = page; + return this; + } + + @java.lang.Override + public _FinalStage hasNextPage(Boolean hasNextPage) { + this.hasNextPage = Optional.ofNullable(hasNextPage); + return this; + } + + @java.lang.Override + @JsonSetter( + value = "hasNextPage", + nulls = Nulls.SKIP + ) + public _FinalStage hasNextPage(Optional hasNextPage) { + this.hasNextPage = hasNextPage; + return this; + } + + @java.lang.Override + public ListUsersOptionalDataPaginationResponse build() { + return new ListUsersOptionalDataPaginationResponse(hasNextPage, page, totalCount, data); + } + } +} diff --git a/seed/openapi/inferred-auth-implicit-api-key/openapi.yml b/seed/openapi/inferred-auth-implicit-api-key/openapi.yml new file mode 100644 index 000000000000..97c14472bb99 --- /dev/null +++ b/seed/openapi/inferred-auth-implicit-api-key/openapi.yml @@ -0,0 +1,75 @@ +openapi: 3.0.1 +info: + title: inferred-auth-implicit-api-key + version: '' +paths: + /token: + post: + operationId: auth_getToken + tags: + - Auth + parameters: + - name: X-Api-Key + in: header + required: true + schema: + type: string + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/TokenResponse' + /nested-no-auth/get-something: + get: + operationId: nestedNoAuth_api_getSomething + tags: + - NestedNoAuthApi + parameters: [] + responses: + '204': + description: '' + /nested/get-something: + get: + operationId: nested_api_getSomething + tags: + - NestedApi + parameters: [] + responses: + '204': + description: '' + security: + - {} + /get-something: + get: + operationId: simple_getSomething + tags: + - Simple + parameters: [] + responses: + '204': + description: '' + security: + - {} +components: + schemas: + TokenResponse: + title: TokenResponse + type: object + description: An auth token response. + properties: + access_token: + type: string + token_type: + type: string + expires_in: + type: integer + scope: + type: string + nullable: true + required: + - access_token + - token_type + - expires_in + securitySchemes: {} diff --git a/seed/openapi/inferred-auth-implicit-api-key/snippet.json b/seed/openapi/inferred-auth-implicit-api-key/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/openapi/inferred-auth-implicit-reference/openapi.yml b/seed/openapi/inferred-auth-implicit-reference/openapi.yml new file mode 100644 index 000000000000..815a6b20ac32 --- /dev/null +++ b/seed/openapi/inferred-auth-implicit-reference/openapi.yml @@ -0,0 +1,141 @@ +openapi: 3.0.1 +info: + title: inferred-auth-implicit + version: '' +paths: + /token: + post: + operationId: auth_getTokenWithClientCredentials + tags: + - Auth + parameters: [] + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/TokenResponse' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/GetTokenRequest' + /token/refresh: + post: + operationId: auth_refreshToken + tags: + - Auth + parameters: [] + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/TokenResponse' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/RefreshTokenRequest' + /nested-no-auth/get-something: + get: + operationId: nestedNoAuth_api_getSomething + tags: + - NestedNoAuthApi + parameters: [] + responses: + '204': + description: '' + /nested/get-something: + get: + operationId: nested_api_getSomething + tags: + - NestedApi + parameters: [] + responses: + '204': + description: '' + security: + - {} + /get-something: + get: + operationId: simple_getSomething + tags: + - Simple + parameters: [] + responses: + '204': + description: '' + security: + - {} +components: + schemas: + GetTokenRequest: + title: GetTokenRequest + type: object + description: A request to obtain an OAuth token. + properties: + client_id: + type: string + client_secret: + type: string + audience: + type: string + const: https://api.example.com + grant_type: + type: string + const: client_credentials + scope: + type: string + nullable: true + required: + - client_id + - client_secret + - audience + - grant_type + RefreshTokenRequest: + title: RefreshTokenRequest + type: object + description: A request to refresh an OAuth token. + properties: + client_id: + type: string + client_secret: + type: string + refresh_token: + type: string + audience: + type: string + const: https://api.example.com + grant_type: + type: string + const: refresh_token + scope: + type: string + nullable: true + required: + - client_id + - client_secret + - refresh_token + - audience + - grant_type + TokenResponse: + title: TokenResponse + type: object + description: An OAuth token response. + properties: + access_token: + type: string + expires_in: + type: integer + refresh_token: + type: string + nullable: true + required: + - access_token + - expires_in + securitySchemes: {} diff --git a/seed/openapi/inferred-auth-implicit-reference/snippet.json b/seed/openapi/inferred-auth-implicit-reference/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/openapi/nullable-allof-extends/openapi.yml b/seed/openapi/nullable-allof-extends/openapi.yml new file mode 100644 index 000000000000..6c579534f365 --- /dev/null +++ b/seed/openapi/nullable-allof-extends/openapi.yml @@ -0,0 +1,81 @@ +openapi: 3.0.1 +info: + title: Nullable AllOf Extends Test + version: '' +paths: + /test: + get: + description: Returns a RootObject which inherits from a nullable schema. + operationId: getTest + tags: + - '' + parameters: [] + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/RootObject' + examples: + Example1: + value: + normalField: normalField + summary: Get Test + post: + description: Creates a test object with nullable allOf in request body. + operationId: createTest + tags: + - '' + parameters: [] + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/RootObject' + examples: + Example1: + value: + normalField: normalField + summary: Create Test + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/RootObject' + examples: + Example1: + value: {} +components: + schemas: + RootObject: + title: RootObject + type: object + description: Object inheriting from a nullable schema via allOf. + properties: {} + allOf: + - $ref: '#/components/schemas/NormalObject' + - $ref: '#/components/schemas/NullableObject' + NormalObject: + title: NormalObject + type: object + description: A standard object with no nullable issues. + properties: + normalField: + type: string + nullable: true + NullableObject: + title: NullableObject + type: object + description: This schema has nullable:true at the top level. + properties: + nullableField: + type: string + nullable: true + securitySchemes: {} +servers: + - url: https://api.example.com + description: Default diff --git a/seed/openapi/nullable-allof-extends/snippet.json b/seed/openapi/nullable-allof-extends/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/openapi/oauth-client-credentials-mandatory-auth/snippet.json b/seed/openapi/oauth-client-credentials-mandatory-auth/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/php-model/accept-header/src/Core/Json/JsonEncoder.php b/seed/php-model/accept-header/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/accept-header/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/accept-header/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/accept-header/src/Core/Json/JsonProperty.php b/seed/php-model/accept-header/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/accept-header/src/Core/Json/JsonProperty.php +++ b/seed/php-model/accept-header/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/accept-header/src/Core/Types/ArrayType.php b/seed/php-model/accept-header/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/accept-header/src/Core/Types/ArrayType.php +++ b/seed/php-model/accept-header/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/accept-header/src/Core/Types/Constant.php b/seed/php-model/accept-header/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/accept-header/src/Core/Types/Constant.php +++ b/seed/php-model/accept-header/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/accept-header/src/Core/Types/Union.php b/seed/php-model/accept-header/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/accept-header/src/Core/Types/Union.php +++ b/seed/php-model/accept-header/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/accept-header/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/accept-header/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/accept-header/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/accept-header/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/accept-header/tests/Core/Json/EnumTest.php b/seed/php-model/accept-header/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/accept-header/tests/Core/Json/EnumTest.php +++ b/seed/php-model/accept-header/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/accept-header/tests/Core/Json/TraitTest.php b/seed/php-model/accept-header/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/accept-header/tests/Core/Json/TraitTest.php +++ b/seed/php-model/accept-header/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/accept-header/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/accept-header/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/accept-header/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/accept-header/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/alias-extends/src/Child.php b/seed/php-model/alias-extends/src/Child.php index 6c26c99569d0..1e0cea9a8844 100644 --- a/seed/php-model/alias-extends/src/Child.php +++ b/seed/php-model/alias-extends/src/Child.php @@ -24,15 +24,16 @@ class Child extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->parent = $values['parent'];$this->child = $values['child']; + ) { + $this->parent = $values['parent']; + $this->child = $values['child']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/alias-extends/src/Core/Json/JsonEncoder.php b/seed/php-model/alias-extends/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/alias-extends/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/alias-extends/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/alias-extends/src/Core/Json/JsonProperty.php b/seed/php-model/alias-extends/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/alias-extends/src/Core/Json/JsonProperty.php +++ b/seed/php-model/alias-extends/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/alias-extends/src/Core/Types/ArrayType.php b/seed/php-model/alias-extends/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/alias-extends/src/Core/Types/ArrayType.php +++ b/seed/php-model/alias-extends/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/alias-extends/src/Core/Types/Constant.php b/seed/php-model/alias-extends/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/alias-extends/src/Core/Types/Constant.php +++ b/seed/php-model/alias-extends/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/alias-extends/src/Core/Types/Union.php b/seed/php-model/alias-extends/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/alias-extends/src/Core/Types/Union.php +++ b/seed/php-model/alias-extends/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/alias-extends/src/Parent_.php b/seed/php-model/alias-extends/src/Parent_.php index 878d91559e4e..b4a86356102b 100644 --- a/seed/php-model/alias-extends/src/Parent_.php +++ b/seed/php-model/alias-extends/src/Parent_.php @@ -20,15 +20,15 @@ class Parent_ extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->parent = $values['parent']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/alias-extends/src/Traits/AliasType.php b/seed/php-model/alias-extends/src/Traits/AliasType.php index 17679092403d..f9b6ed67e282 100644 --- a/seed/php-model/alias-extends/src/Traits/AliasType.php +++ b/seed/php-model/alias-extends/src/Traits/AliasType.php @@ -7,7 +7,7 @@ /** * @property string $parent */ -trait AliasType +trait AliasType { /** * @var string $parent diff --git a/seed/php-model/alias-extends/src/Traits/Parent_.php b/seed/php-model/alias-extends/src/Traits/Parent_.php index 31cc4250b25d..b6198ecc686f 100644 --- a/seed/php-model/alias-extends/src/Traits/Parent_.php +++ b/seed/php-model/alias-extends/src/Traits/Parent_.php @@ -7,7 +7,7 @@ /** * @property string $parent */ -trait Parent_ +trait Parent_ { /** * @var string $parent diff --git a/seed/php-model/alias-extends/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/alias-extends/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/alias-extends/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/alias-extends/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/alias-extends/tests/Core/Json/EnumTest.php b/seed/php-model/alias-extends/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/alias-extends/tests/Core/Json/EnumTest.php +++ b/seed/php-model/alias-extends/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/alias-extends/tests/Core/Json/TraitTest.php b/seed/php-model/alias-extends/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/alias-extends/tests/Core/Json/TraitTest.php +++ b/seed/php-model/alias-extends/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/alias-extends/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/alias-extends/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/alias-extends/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/alias-extends/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/alias/src/Core/Json/JsonEncoder.php b/seed/php-model/alias/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/alias/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/alias/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/alias/src/Core/Json/JsonProperty.php b/seed/php-model/alias/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/alias/src/Core/Json/JsonProperty.php +++ b/seed/php-model/alias/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/alias/src/Core/Types/ArrayType.php b/seed/php-model/alias/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/alias/src/Core/Types/ArrayType.php +++ b/seed/php-model/alias/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/alias/src/Core/Types/Constant.php b/seed/php-model/alias/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/alias/src/Core/Types/Constant.php +++ b/seed/php-model/alias/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/alias/src/Core/Types/Union.php b/seed/php-model/alias/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/alias/src/Core/Types/Union.php +++ b/seed/php-model/alias/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/alias/src/Type.php b/seed/php-model/alias/src/Type.php index 9d5a63976092..9383d481f4ba 100644 --- a/seed/php-model/alias/src/Type.php +++ b/seed/php-model/alias/src/Type.php @@ -30,15 +30,16 @@ class Type extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->name = $values['name']; + ) { + $this->id = $values['id']; + $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/alias/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/alias/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/alias/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/alias/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/alias/tests/Core/Json/EnumTest.php b/seed/php-model/alias/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/alias/tests/Core/Json/EnumTest.php +++ b/seed/php-model/alias/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/alias/tests/Core/Json/TraitTest.php b/seed/php-model/alias/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/alias/tests/Core/Json/TraitTest.php +++ b/seed/php-model/alias/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/alias/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/alias/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/alias/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/alias/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/any-auth/src/Auth/TokenResponse.php b/seed/php-model/any-auth/src/Auth/TokenResponse.php index 56fbe31ea0c2..9222a65c62ca 100644 --- a/seed/php-model/any-auth/src/Auth/TokenResponse.php +++ b/seed/php-model/any-auth/src/Auth/TokenResponse.php @@ -37,15 +37,17 @@ class TokenResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->accessToken = $values['accessToken'];$this->expiresIn = $values['expiresIn'];$this->refreshToken = $values['refreshToken'] ?? null; + ) { + $this->accessToken = $values['accessToken']; + $this->expiresIn = $values['expiresIn']; + $this->refreshToken = $values['refreshToken'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/any-auth/src/Core/Json/JsonEncoder.php b/seed/php-model/any-auth/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/any-auth/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/any-auth/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/any-auth/src/Core/Json/JsonProperty.php b/seed/php-model/any-auth/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/any-auth/src/Core/Json/JsonProperty.php +++ b/seed/php-model/any-auth/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/any-auth/src/Core/Types/ArrayType.php b/seed/php-model/any-auth/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/any-auth/src/Core/Types/ArrayType.php +++ b/seed/php-model/any-auth/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/any-auth/src/Core/Types/Constant.php b/seed/php-model/any-auth/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/any-auth/src/Core/Types/Constant.php +++ b/seed/php-model/any-auth/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/any-auth/src/Core/Types/Union.php b/seed/php-model/any-auth/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/any-auth/src/Core/Types/Union.php +++ b/seed/php-model/any-auth/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/any-auth/src/User/User.php b/seed/php-model/any-auth/src/User/User.php index 7a5290436e3f..e9c0c5b98814 100644 --- a/seed/php-model/any-auth/src/User/User.php +++ b/seed/php-model/any-auth/src/User/User.php @@ -27,15 +27,16 @@ class User extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->name = $values['name']; + ) { + $this->id = $values['id']; + $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/any-auth/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/any-auth/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/any-auth/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/any-auth/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/any-auth/tests/Core/Json/EnumTest.php b/seed/php-model/any-auth/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/any-auth/tests/Core/Json/EnumTest.php +++ b/seed/php-model/any-auth/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/any-auth/tests/Core/Json/TraitTest.php b/seed/php-model/any-auth/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/any-auth/tests/Core/Json/TraitTest.php +++ b/seed/php-model/any-auth/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/any-auth/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/any-auth/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/any-auth/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/any-auth/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/api-wide-base-path/src/Core/Json/JsonEncoder.php b/seed/php-model/api-wide-base-path/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/api-wide-base-path/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/api-wide-base-path/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/api-wide-base-path/src/Core/Json/JsonProperty.php b/seed/php-model/api-wide-base-path/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/api-wide-base-path/src/Core/Json/JsonProperty.php +++ b/seed/php-model/api-wide-base-path/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/api-wide-base-path/src/Core/Types/ArrayType.php b/seed/php-model/api-wide-base-path/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/api-wide-base-path/src/Core/Types/ArrayType.php +++ b/seed/php-model/api-wide-base-path/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/api-wide-base-path/src/Core/Types/Constant.php b/seed/php-model/api-wide-base-path/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/api-wide-base-path/src/Core/Types/Constant.php +++ b/seed/php-model/api-wide-base-path/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/api-wide-base-path/src/Core/Types/Union.php b/seed/php-model/api-wide-base-path/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/api-wide-base-path/src/Core/Types/Union.php +++ b/seed/php-model/api-wide-base-path/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/api-wide-base-path/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/api-wide-base-path/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/api-wide-base-path/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/api-wide-base-path/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/api-wide-base-path/tests/Core/Json/EnumTest.php b/seed/php-model/api-wide-base-path/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/api-wide-base-path/tests/Core/Json/EnumTest.php +++ b/seed/php-model/api-wide-base-path/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/api-wide-base-path/tests/Core/Json/TraitTest.php b/seed/php-model/api-wide-base-path/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/api-wide-base-path/tests/Core/Json/TraitTest.php +++ b/seed/php-model/api-wide-base-path/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/api-wide-base-path/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/api-wide-base-path/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/api-wide-base-path/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/api-wide-base-path/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/audiences/src/Core/Json/JsonEncoder.php b/seed/php-model/audiences/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/audiences/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/audiences/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/audiences/src/Core/Json/JsonProperty.php b/seed/php-model/audiences/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/audiences/src/Core/Json/JsonProperty.php +++ b/seed/php-model/audiences/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/audiences/src/Core/Types/ArrayType.php b/seed/php-model/audiences/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/audiences/src/Core/Types/ArrayType.php +++ b/seed/php-model/audiences/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/audiences/src/Core/Types/Constant.php b/seed/php-model/audiences/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/audiences/src/Core/Types/Constant.php +++ b/seed/php-model/audiences/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/audiences/src/Core/Types/Union.php b/seed/php-model/audiences/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/audiences/src/Core/Types/Union.php +++ b/seed/php-model/audiences/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/audiences/src/FolderA/Service/Response.php b/seed/php-model/audiences/src/FolderA/Service/Response.php index 73cb7853e8b4..a65dff7630d3 100644 --- a/seed/php-model/audiences/src/FolderA/Service/Response.php +++ b/seed/php-model/audiences/src/FolderA/Service/Response.php @@ -21,15 +21,15 @@ class Response extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->foo = $values['foo'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/audiences/src/FolderB/Common/Foo.php b/seed/php-model/audiences/src/FolderB/Common/Foo.php index 4e87f8212de6..386ed3c4b5f7 100644 --- a/seed/php-model/audiences/src/FolderB/Common/Foo.php +++ b/seed/php-model/audiences/src/FolderB/Common/Foo.php @@ -21,15 +21,15 @@ class Foo extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->foo = $values['foo'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/audiences/src/FolderC/Common/FolderCFoo.php b/seed/php-model/audiences/src/FolderC/Common/FolderCFoo.php index c06a9296a457..940120de48b9 100644 --- a/seed/php-model/audiences/src/FolderC/Common/FolderCFoo.php +++ b/seed/php-model/audiences/src/FolderC/Common/FolderCFoo.php @@ -20,15 +20,15 @@ class FolderCFoo extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->barProperty = $values['barProperty']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/audiences/src/FolderD/Service/Response.php b/seed/php-model/audiences/src/FolderD/Service/Response.php index 674a4143dd52..93c221e1215f 100644 --- a/seed/php-model/audiences/src/FolderD/Service/Response.php +++ b/seed/php-model/audiences/src/FolderD/Service/Response.php @@ -20,15 +20,15 @@ class Response extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->foo = $values['foo']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/audiences/src/Foo/FilteredType.php b/seed/php-model/audiences/src/Foo/FilteredType.php index e89d0fb60a4a..f766ed35e88b 100644 --- a/seed/php-model/audiences/src/Foo/FilteredType.php +++ b/seed/php-model/audiences/src/Foo/FilteredType.php @@ -27,15 +27,16 @@ class FilteredType extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->publicProperty = $values['publicProperty'] ?? null;$this->privateProperty = $values['privateProperty']; + ) { + $this->publicProperty = $values['publicProperty'] ?? null; + $this->privateProperty = $values['privateProperty']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/audiences/src/Foo/ImportingType.php b/seed/php-model/audiences/src/Foo/ImportingType.php index 58903d106ef4..4ae8ba849bd7 100644 --- a/seed/php-model/audiences/src/Foo/ImportingType.php +++ b/seed/php-model/audiences/src/Foo/ImportingType.php @@ -20,15 +20,15 @@ class ImportingType extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->imported = $values['imported']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/audiences/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/audiences/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/audiences/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/audiences/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/audiences/tests/Core/Json/EnumTest.php b/seed/php-model/audiences/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/audiences/tests/Core/Json/EnumTest.php +++ b/seed/php-model/audiences/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/audiences/tests/Core/Json/TraitTest.php b/seed/php-model/audiences/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/audiences/tests/Core/Json/TraitTest.php +++ b/seed/php-model/audiences/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/audiences/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/audiences/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/audiences/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/audiences/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/basic-auth-environment-variables/src/Core/Json/JsonEncoder.php b/seed/php-model/basic-auth-environment-variables/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/basic-auth-environment-variables/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/basic-auth-environment-variables/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/basic-auth-environment-variables/src/Core/Json/JsonProperty.php b/seed/php-model/basic-auth-environment-variables/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/basic-auth-environment-variables/src/Core/Json/JsonProperty.php +++ b/seed/php-model/basic-auth-environment-variables/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/basic-auth-environment-variables/src/Core/Types/ArrayType.php b/seed/php-model/basic-auth-environment-variables/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/basic-auth-environment-variables/src/Core/Types/ArrayType.php +++ b/seed/php-model/basic-auth-environment-variables/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/basic-auth-environment-variables/src/Core/Types/Constant.php b/seed/php-model/basic-auth-environment-variables/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/basic-auth-environment-variables/src/Core/Types/Constant.php +++ b/seed/php-model/basic-auth-environment-variables/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/basic-auth-environment-variables/src/Core/Types/Union.php b/seed/php-model/basic-auth-environment-variables/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/basic-auth-environment-variables/src/Core/Types/Union.php +++ b/seed/php-model/basic-auth-environment-variables/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/basic-auth-environment-variables/src/Errors/UnauthorizedRequestErrorBody.php b/seed/php-model/basic-auth-environment-variables/src/Errors/UnauthorizedRequestErrorBody.php index a1ac676af462..7167dd355c41 100644 --- a/seed/php-model/basic-auth-environment-variables/src/Errors/UnauthorizedRequestErrorBody.php +++ b/seed/php-model/basic-auth-environment-variables/src/Errors/UnauthorizedRequestErrorBody.php @@ -20,15 +20,15 @@ class UnauthorizedRequestErrorBody extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->message = $values['message']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/basic-auth-environment-variables/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/basic-auth-environment-variables/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/basic-auth-environment-variables/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/basic-auth-environment-variables/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/basic-auth-environment-variables/tests/Core/Json/EnumTest.php b/seed/php-model/basic-auth-environment-variables/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/basic-auth-environment-variables/tests/Core/Json/EnumTest.php +++ b/seed/php-model/basic-auth-environment-variables/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/basic-auth-environment-variables/tests/Core/Json/TraitTest.php b/seed/php-model/basic-auth-environment-variables/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/basic-auth-environment-variables/tests/Core/Json/TraitTest.php +++ b/seed/php-model/basic-auth-environment-variables/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/basic-auth-environment-variables/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/basic-auth-environment-variables/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/basic-auth-environment-variables/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/basic-auth-environment-variables/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/basic-auth/src/Core/Json/JsonEncoder.php b/seed/php-model/basic-auth/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/basic-auth/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/basic-auth/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/basic-auth/src/Core/Json/JsonProperty.php b/seed/php-model/basic-auth/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/basic-auth/src/Core/Json/JsonProperty.php +++ b/seed/php-model/basic-auth/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/basic-auth/src/Core/Types/ArrayType.php b/seed/php-model/basic-auth/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/basic-auth/src/Core/Types/ArrayType.php +++ b/seed/php-model/basic-auth/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/basic-auth/src/Core/Types/Constant.php b/seed/php-model/basic-auth/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/basic-auth/src/Core/Types/Constant.php +++ b/seed/php-model/basic-auth/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/basic-auth/src/Core/Types/Union.php b/seed/php-model/basic-auth/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/basic-auth/src/Core/Types/Union.php +++ b/seed/php-model/basic-auth/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/basic-auth/src/Errors/UnauthorizedRequestErrorBody.php b/seed/php-model/basic-auth/src/Errors/UnauthorizedRequestErrorBody.php index a1ac676af462..7167dd355c41 100644 --- a/seed/php-model/basic-auth/src/Errors/UnauthorizedRequestErrorBody.php +++ b/seed/php-model/basic-auth/src/Errors/UnauthorizedRequestErrorBody.php @@ -20,15 +20,15 @@ class UnauthorizedRequestErrorBody extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->message = $values['message']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/basic-auth/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/basic-auth/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/basic-auth/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/basic-auth/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/basic-auth/tests/Core/Json/EnumTest.php b/seed/php-model/basic-auth/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/basic-auth/tests/Core/Json/EnumTest.php +++ b/seed/php-model/basic-auth/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/basic-auth/tests/Core/Json/TraitTest.php b/seed/php-model/basic-auth/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/basic-auth/tests/Core/Json/TraitTest.php +++ b/seed/php-model/basic-auth/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/basic-auth/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/basic-auth/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/basic-auth/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/basic-auth/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/bearer-token-environment-variable/src/Core/Json/JsonEncoder.php b/seed/php-model/bearer-token-environment-variable/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/bearer-token-environment-variable/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/bearer-token-environment-variable/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/bearer-token-environment-variable/src/Core/Json/JsonProperty.php b/seed/php-model/bearer-token-environment-variable/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/bearer-token-environment-variable/src/Core/Json/JsonProperty.php +++ b/seed/php-model/bearer-token-environment-variable/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/bearer-token-environment-variable/src/Core/Types/ArrayType.php b/seed/php-model/bearer-token-environment-variable/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/bearer-token-environment-variable/src/Core/Types/ArrayType.php +++ b/seed/php-model/bearer-token-environment-variable/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/bearer-token-environment-variable/src/Core/Types/Constant.php b/seed/php-model/bearer-token-environment-variable/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/bearer-token-environment-variable/src/Core/Types/Constant.php +++ b/seed/php-model/bearer-token-environment-variable/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/bearer-token-environment-variable/src/Core/Types/Union.php b/seed/php-model/bearer-token-environment-variable/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/bearer-token-environment-variable/src/Core/Types/Union.php +++ b/seed/php-model/bearer-token-environment-variable/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/bearer-token-environment-variable/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/bearer-token-environment-variable/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/bearer-token-environment-variable/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/bearer-token-environment-variable/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/bearer-token-environment-variable/tests/Core/Json/EnumTest.php b/seed/php-model/bearer-token-environment-variable/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/bearer-token-environment-variable/tests/Core/Json/EnumTest.php +++ b/seed/php-model/bearer-token-environment-variable/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/bearer-token-environment-variable/tests/Core/Json/TraitTest.php b/seed/php-model/bearer-token-environment-variable/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/bearer-token-environment-variable/tests/Core/Json/TraitTest.php +++ b/seed/php-model/bearer-token-environment-variable/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/bearer-token-environment-variable/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/bearer-token-environment-variable/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/bearer-token-environment-variable/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/bearer-token-environment-variable/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/bytes-download/src/Core/Json/JsonEncoder.php b/seed/php-model/bytes-download/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/bytes-download/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/bytes-download/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/bytes-download/src/Core/Json/JsonProperty.php b/seed/php-model/bytes-download/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/bytes-download/src/Core/Json/JsonProperty.php +++ b/seed/php-model/bytes-download/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/bytes-download/src/Core/Types/ArrayType.php b/seed/php-model/bytes-download/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/bytes-download/src/Core/Types/ArrayType.php +++ b/seed/php-model/bytes-download/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/bytes-download/src/Core/Types/Constant.php b/seed/php-model/bytes-download/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/bytes-download/src/Core/Types/Constant.php +++ b/seed/php-model/bytes-download/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/bytes-download/src/Core/Types/Union.php b/seed/php-model/bytes-download/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/bytes-download/src/Core/Types/Union.php +++ b/seed/php-model/bytes-download/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/bytes-download/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/bytes-download/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/bytes-download/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/bytes-download/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/bytes-download/tests/Core/Json/EnumTest.php b/seed/php-model/bytes-download/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/bytes-download/tests/Core/Json/EnumTest.php +++ b/seed/php-model/bytes-download/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/bytes-download/tests/Core/Json/TraitTest.php b/seed/php-model/bytes-download/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/bytes-download/tests/Core/Json/TraitTest.php +++ b/seed/php-model/bytes-download/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/bytes-download/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/bytes-download/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/bytes-download/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/bytes-download/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/bytes-upload/src/Core/Json/JsonEncoder.php b/seed/php-model/bytes-upload/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/bytes-upload/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/bytes-upload/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/bytes-upload/src/Core/Json/JsonProperty.php b/seed/php-model/bytes-upload/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/bytes-upload/src/Core/Json/JsonProperty.php +++ b/seed/php-model/bytes-upload/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/bytes-upload/src/Core/Types/ArrayType.php b/seed/php-model/bytes-upload/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/bytes-upload/src/Core/Types/ArrayType.php +++ b/seed/php-model/bytes-upload/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/bytes-upload/src/Core/Types/Constant.php b/seed/php-model/bytes-upload/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/bytes-upload/src/Core/Types/Constant.php +++ b/seed/php-model/bytes-upload/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/bytes-upload/src/Core/Types/Union.php b/seed/php-model/bytes-upload/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/bytes-upload/src/Core/Types/Union.php +++ b/seed/php-model/bytes-upload/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/bytes-upload/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/bytes-upload/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/bytes-upload/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/bytes-upload/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/bytes-upload/tests/Core/Json/EnumTest.php b/seed/php-model/bytes-upload/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/bytes-upload/tests/Core/Json/EnumTest.php +++ b/seed/php-model/bytes-upload/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/bytes-upload/tests/Core/Json/TraitTest.php b/seed/php-model/bytes-upload/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/bytes-upload/tests/Core/Json/TraitTest.php +++ b/seed/php-model/bytes-upload/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/bytes-upload/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/bytes-upload/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/bytes-upload/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/bytes-upload/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/circular-references-advanced/src/A/A.php b/seed/php-model/circular-references-advanced/src/A/A.php index d70c5d55e098..d37381d357e8 100644 --- a/seed/php-model/circular-references-advanced/src/A/A.php +++ b/seed/php-model/circular-references-advanced/src/A/A.php @@ -17,15 +17,15 @@ class A extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->s = $values['s']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/circular-references-advanced/src/Ast/Acai.php b/seed/php-model/circular-references-advanced/src/Ast/Acai.php index ed0fa7cadbb0..fe9c98fb85e7 100644 --- a/seed/php-model/circular-references-advanced/src/Ast/Acai.php +++ b/seed/php-model/circular-references-advanced/src/Ast/Acai.php @@ -20,15 +20,15 @@ class Acai extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->animal = $values['animal']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/circular-references-advanced/src/Ast/Berry.php b/seed/php-model/circular-references-advanced/src/Ast/Berry.php index f06f1809245c..616e297acfcb 100644 --- a/seed/php-model/circular-references-advanced/src/Ast/Berry.php +++ b/seed/php-model/circular-references-advanced/src/Ast/Berry.php @@ -14,7 +14,7 @@ class Berry extends JsonSerializableType * |Dog * ) $animal */ - #[JsonProperty('animal'), Union(Cat::class,Dog::class)] + #[JsonProperty('animal'), Union(Cat::class, Dog::class)] public Cat|Dog $animal; /** @@ -27,15 +27,15 @@ class Berry extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->animal = $values['animal']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/circular-references-advanced/src/Ast/BranchNode.php b/seed/php-model/circular-references-advanced/src/Ast/BranchNode.php index 273c5e390453..f8aa3b435209 100644 --- a/seed/php-model/circular-references-advanced/src/Ast/BranchNode.php +++ b/seed/php-model/circular-references-advanced/src/Ast/BranchNode.php @@ -28,15 +28,15 @@ class BranchNode extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->children = $values['children']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/circular-references-advanced/src/Ast/Cat.php b/seed/php-model/circular-references-advanced/src/Ast/Cat.php index 823b3abb109c..924e74efefc3 100644 --- a/seed/php-model/circular-references-advanced/src/Ast/Cat.php +++ b/seed/php-model/circular-references-advanced/src/Ast/Cat.php @@ -14,7 +14,7 @@ class Cat extends JsonSerializableType * |Fig * ) $fruit */ - #[JsonProperty('fruit'), Union(Acai::class,Fig::class)] + #[JsonProperty('fruit'), Union(Acai::class, Fig::class)] public Acai|Fig $fruit; /** @@ -27,15 +27,15 @@ class Cat extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->fruit = $values['fruit']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/circular-references-advanced/src/Ast/ContainerValue.php b/seed/php-model/circular-references-advanced/src/Ast/ContainerValue.php index d22596135ecf..d2fa4a578acf 100644 --- a/seed/php-model/circular-references-advanced/src/Ast/ContainerValue.php +++ b/seed/php-model/circular-references-advanced/src/Ast/ContainerValue.php @@ -44,16 +44,17 @@ class ContainerValue extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param array $list * @return ContainerValue */ - public static function list(array $list): ContainerValue { + public static function list(array $list): ContainerValue + { return new ContainerValue([ 'type' => 'list', 'value' => $list, @@ -64,7 +65,8 @@ public static function list(array $list): ContainerValue { * @param ?FieldValue $optional * @return ContainerValue */ - public static function optional(?FieldValue $optional = null): ContainerValue { + public static function optional(?FieldValue $optional = null): ContainerValue + { return new ContainerValue([ 'type' => 'optional', 'value' => $optional, @@ -74,94 +76,101 @@ public static function optional(?FieldValue $optional = null): ContainerValue { /** * @return bool */ - public function isList_(): bool { - return is_array($this->value)&& $this->type === 'list'; + public function isList_(): bool + { + return is_array($this->value) && $this->type === 'list'; } /** * @return array */ - public function asList_(): array { - if (!(is_array($this->value)&& $this->type === 'list')){ + public function asList_(): array + { + if (!(is_array($this->value) && $this->type === 'list')) { throw new Exception( "Expected list; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isOptional(): bool { - return (is_null($this->value) || $this->value instanceof FieldValue)&& $this->type === 'optional'; + public function isOptional(): bool + { + return (is_null($this->value) || $this->value instanceof FieldValue) && $this->type === 'optional'; } /** * @return ?FieldValue */ - public function asOptional(): ?FieldValue { - if (!((is_null($this->value) || $this->value instanceof FieldValue)&& $this->type === 'optional')){ + public function asOptional(): ?FieldValue + { + if (!((is_null($this->value) || $this->value instanceof FieldValue) && $this->type === 'optional')) { throw new Exception( "Expected optional; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'list': $value = $this->value; $result['list'] = $value; break; case 'optional': $value = $this->asOptional(); - if (!is_null($value)){ + if (!is_null($value)) { $value = $value->jsonSerialize(); } $result['optional'] = $value; break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -170,42 +179,43 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'list': - if (!array_key_exists('list', $data)){ + if (!array_key_exists('list', $data)) { throw new Exception( "JSON data is missing property 'list'", ); } - + $args['value'] = $data['list']; break; case 'optional': - if (!array_key_exists('optional', $data)){ + if (!array_key_exists('optional', $data)) { throw new Exception( "JSON data is missing property 'optional'", ); } - - if (is_null($data['optional'])){ + + if (is_null($data['optional'])) { $args['value'] = null; - } else{ - if (!(is_array($data['optional']))){ + } else { + if (!(is_array($data['optional']))) { throw new Exception( "Expected property 'optional' in JSON data to be array, instead received " . get_debug_type($data['optional']), ); @@ -218,7 +228,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/circular-references-advanced/src/Ast/Dog.php b/seed/php-model/circular-references-advanced/src/Ast/Dog.php index 9a7774adfe74..e019dbe71a06 100644 --- a/seed/php-model/circular-references-advanced/src/Ast/Dog.php +++ b/seed/php-model/circular-references-advanced/src/Ast/Dog.php @@ -14,7 +14,7 @@ class Dog extends JsonSerializableType * |Fig * ) $fruit */ - #[JsonProperty('fruit'), Union(Acai::class,Fig::class)] + #[JsonProperty('fruit'), Union(Acai::class, Fig::class)] public Acai|Fig $fruit; /** @@ -27,15 +27,15 @@ class Dog extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->fruit = $values['fruit']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/circular-references-advanced/src/Ast/FieldValue.php b/seed/php-model/circular-references-advanced/src/Ast/FieldValue.php index 3e690627a423..62e8268589d7 100644 --- a/seed/php-model/circular-references-advanced/src/Ast/FieldValue.php +++ b/seed/php-model/circular-references-advanced/src/Ast/FieldValue.php @@ -46,16 +46,17 @@ class FieldValue extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param value-of $primitiveValue * @return FieldValue */ - public static function primitiveValue(string $primitiveValue): FieldValue { + public static function primitiveValue(string $primitiveValue): FieldValue + { return new FieldValue([ 'type' => 'primitive_value', 'value' => $primitiveValue, @@ -66,7 +67,8 @@ public static function primitiveValue(string $primitiveValue): FieldValue { * @param ObjectValue $objectValue * @return FieldValue */ - public static function objectValue(ObjectValue $objectValue): FieldValue { + public static function objectValue(ObjectValue $objectValue): FieldValue + { return new FieldValue([ 'type' => 'object_value', 'value' => $objectValue, @@ -77,7 +79,8 @@ public static function objectValue(ObjectValue $objectValue): FieldValue { * @param ContainerValue $containerValue * @return FieldValue */ - public static function containerValue(ContainerValue $containerValue): FieldValue { + public static function containerValue(ContainerValue $containerValue): FieldValue + { return new FieldValue([ 'type' => 'container_value', 'value' => $containerValue, @@ -87,81 +90,89 @@ public static function containerValue(ContainerValue $containerValue): FieldValu /** * @return bool */ - public function isPrimitiveValue(): bool { - return $this->value instanceof PrimitiveValue&& $this->type === 'primitive_value'; + public function isPrimitiveValue(): bool + { + return $this->value instanceof PrimitiveValue && $this->type === 'primitive_value'; } /** * @return value-of */ - public function asPrimitiveValue(): string { - if (!($this->value instanceof PrimitiveValue&& $this->type === 'primitive_value')){ + public function asPrimitiveValue(): string + { + if (!($this->value instanceof PrimitiveValue && $this->type === 'primitive_value')) { throw new Exception( "Expected primitive_value; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isObjectValue(): bool { - return $this->value instanceof ObjectValue&& $this->type === 'object_value'; + public function isObjectValue(): bool + { + return $this->value instanceof ObjectValue && $this->type === 'object_value'; } /** * @return ObjectValue */ - public function asObjectValue(): ObjectValue { - if (!($this->value instanceof ObjectValue&& $this->type === 'object_value')){ + public function asObjectValue(): ObjectValue + { + if (!($this->value instanceof ObjectValue && $this->type === 'object_value')) { throw new Exception( "Expected object_value; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isContainerValue(): bool { - return $this->value instanceof ContainerValue&& $this->type === 'container_value'; + public function isContainerValue(): bool + { + return $this->value instanceof ContainerValue && $this->type === 'container_value'; } /** * @return ContainerValue */ - public function asContainerValue(): ContainerValue { - if (!($this->value instanceof ContainerValue&& $this->type === 'container_value')){ + public function asContainerValue(): ContainerValue + { + if (!($this->value instanceof ContainerValue && $this->type === 'container_value')) { throw new Exception( "Expected container_value; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'primitive_value': $value = $this->value; $result['primitive_value'] = $value; @@ -176,26 +187,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -204,42 +216,43 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'primitive_value': - if (!array_key_exists('primitive_value', $data)){ + if (!array_key_exists('primitive_value', $data)) { throw new Exception( "JSON data is missing property 'primitive_value'", ); } - + $args['value'] = $data['primitive_value']; break; case 'object_value': $args['value'] = ObjectValue::jsonDeserialize($data); break; case 'container_value': - if (!array_key_exists('container_value', $data)){ + if (!array_key_exists('container_value', $data)) { throw new Exception( "JSON data is missing property 'container_value'", ); } - - if (!(is_array($data['container_value']))){ + + if (!(is_array($data['container_value']))) { throw new Exception( "Expected property 'containerValue' in JSON data to be array, instead received " . get_debug_type($data['container_value']), ); @@ -251,7 +264,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/circular-references-advanced/src/Ast/Fig.php b/seed/php-model/circular-references-advanced/src/Ast/Fig.php index 998895d05071..b1ffa7ddfa20 100644 --- a/seed/php-model/circular-references-advanced/src/Ast/Fig.php +++ b/seed/php-model/circular-references-advanced/src/Ast/Fig.php @@ -14,7 +14,7 @@ class Fig extends JsonSerializableType * |Dog * ) $animal */ - #[JsonProperty('animal'), Union(Cat::class,Dog::class)] + #[JsonProperty('animal'), Union(Cat::class, Dog::class)] public Cat|Dog $animal; /** @@ -27,15 +27,15 @@ class Fig extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->animal = $values['animal']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/circular-references-advanced/src/Ast/LeafNode.php b/seed/php-model/circular-references-advanced/src/Ast/LeafNode.php index f274b570653f..feed110abe54 100644 --- a/seed/php-model/circular-references-advanced/src/Ast/LeafNode.php +++ b/seed/php-model/circular-references-advanced/src/Ast/LeafNode.php @@ -6,22 +6,21 @@ class LeafNode extends JsonSerializableType { - /** * @param array{ * } $values */ public function __construct( array $values = [], - ) - { + ) { unset($values); } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/circular-references-advanced/src/Ast/NodesWrapper.php b/seed/php-model/circular-references-advanced/src/Ast/NodesWrapper.php index 83ab35ee8059..43699e28c487 100644 --- a/seed/php-model/circular-references-advanced/src/Ast/NodesWrapper.php +++ b/seed/php-model/circular-references-advanced/src/Ast/NodesWrapper.php @@ -28,15 +28,15 @@ class NodesWrapper extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->nodes = $values['nodes']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/circular-references-advanced/src/Ast/ObjectFieldValue.php b/seed/php-model/circular-references-advanced/src/Ast/ObjectFieldValue.php index 85e597b45949..366af8fa026a 100644 --- a/seed/php-model/circular-references-advanced/src/Ast/ObjectFieldValue.php +++ b/seed/php-model/circular-references-advanced/src/Ast/ObjectFieldValue.php @@ -30,15 +30,16 @@ class ObjectFieldValue extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->value = $values['value']; + ) { + $this->name = $values['name']; + $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/circular-references-advanced/src/Ast/ObjectValue.php b/seed/php-model/circular-references-advanced/src/Ast/ObjectValue.php index 2c09380f36b7..9fcb205c2f22 100644 --- a/seed/php-model/circular-references-advanced/src/Ast/ObjectValue.php +++ b/seed/php-model/circular-references-advanced/src/Ast/ObjectValue.php @@ -6,22 +6,21 @@ class ObjectValue extends JsonSerializableType { - /** * @param array{ * } $values */ public function __construct( array $values = [], - ) - { + ) { unset($values); } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/circular-references-advanced/src/Ast/PrimitiveValue.php b/seed/php-model/circular-references-advanced/src/Ast/PrimitiveValue.php index c41a5f950c50..6a21888ecfcd 100644 --- a/seed/php-model/circular-references-advanced/src/Ast/PrimitiveValue.php +++ b/seed/php-model/circular-references-advanced/src/Ast/PrimitiveValue.php @@ -2,8 +2,8 @@ namespace Seed\Ast; -enum PrimitiveValue - : string { +enum PrimitiveValue: string +{ case String = "STRING"; case Number = "NUMBER"; } diff --git a/seed/php-model/circular-references-advanced/src/Ast/Traits/Berry.php b/seed/php-model/circular-references-advanced/src/Ast/Traits/Berry.php index eabf75349748..a01df9324f18 100644 --- a/seed/php-model/circular-references-advanced/src/Ast/Traits/Berry.php +++ b/seed/php-model/circular-references-advanced/src/Ast/Traits/Berry.php @@ -13,7 +13,7 @@ * |Dog * ) $animal */ -trait Berry +trait Berry { /** * @var ( @@ -21,6 +21,6 @@ trait Berry * |Dog * ) $animal */ - #[JsonProperty('animal'), Union(Cat::class,Dog::class)] + #[JsonProperty('animal'), Union(Cat::class, Dog::class)] public Cat|Dog $animal; } diff --git a/seed/php-model/circular-references-advanced/src/Core/Json/JsonEncoder.php b/seed/php-model/circular-references-advanced/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/circular-references-advanced/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/circular-references-advanced/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/circular-references-advanced/src/Core/Json/JsonProperty.php b/seed/php-model/circular-references-advanced/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/circular-references-advanced/src/Core/Json/JsonProperty.php +++ b/seed/php-model/circular-references-advanced/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/circular-references-advanced/src/Core/Types/ArrayType.php b/seed/php-model/circular-references-advanced/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/circular-references-advanced/src/Core/Types/ArrayType.php +++ b/seed/php-model/circular-references-advanced/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/circular-references-advanced/src/Core/Types/Constant.php b/seed/php-model/circular-references-advanced/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/circular-references-advanced/src/Core/Types/Constant.php +++ b/seed/php-model/circular-references-advanced/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/circular-references-advanced/src/Core/Types/Union.php b/seed/php-model/circular-references-advanced/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/circular-references-advanced/src/Core/Types/Union.php +++ b/seed/php-model/circular-references-advanced/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/circular-references-advanced/src/ImportingA.php b/seed/php-model/circular-references-advanced/src/ImportingA.php index 5dedb77a116c..9f8e2e1569d9 100644 --- a/seed/php-model/circular-references-advanced/src/ImportingA.php +++ b/seed/php-model/circular-references-advanced/src/ImportingA.php @@ -21,15 +21,15 @@ class ImportingA extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->a = $values['a'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/circular-references-advanced/src/RootType.php b/seed/php-model/circular-references-advanced/src/RootType.php index 795d3683e9ee..9d0a7f746329 100644 --- a/seed/php-model/circular-references-advanced/src/RootType.php +++ b/seed/php-model/circular-references-advanced/src/RootType.php @@ -20,15 +20,15 @@ class RootType extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->s = $values['s']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/circular-references-advanced/src/Traits/RootType.php b/seed/php-model/circular-references-advanced/src/Traits/RootType.php index a2075e9032e2..ef8eae518c71 100644 --- a/seed/php-model/circular-references-advanced/src/Traits/RootType.php +++ b/seed/php-model/circular-references-advanced/src/Traits/RootType.php @@ -7,7 +7,7 @@ /** * @property string $s */ -trait RootType +trait RootType { /** * @var string $s diff --git a/seed/php-model/circular-references-advanced/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/circular-references-advanced/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/circular-references-advanced/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/circular-references-advanced/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/circular-references-advanced/tests/Core/Json/EnumTest.php b/seed/php-model/circular-references-advanced/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/circular-references-advanced/tests/Core/Json/EnumTest.php +++ b/seed/php-model/circular-references-advanced/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/circular-references-advanced/tests/Core/Json/TraitTest.php b/seed/php-model/circular-references-advanced/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/circular-references-advanced/tests/Core/Json/TraitTest.php +++ b/seed/php-model/circular-references-advanced/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/circular-references-advanced/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/circular-references-advanced/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/circular-references-advanced/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/circular-references-advanced/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/circular-references/src/A/A.php b/seed/php-model/circular-references/src/A/A.php index d70c5d55e098..d37381d357e8 100644 --- a/seed/php-model/circular-references/src/A/A.php +++ b/seed/php-model/circular-references/src/A/A.php @@ -17,15 +17,15 @@ class A extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->s = $values['s']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/circular-references/src/Ast/ContainerValue.php b/seed/php-model/circular-references/src/Ast/ContainerValue.php index d22596135ecf..d2fa4a578acf 100644 --- a/seed/php-model/circular-references/src/Ast/ContainerValue.php +++ b/seed/php-model/circular-references/src/Ast/ContainerValue.php @@ -44,16 +44,17 @@ class ContainerValue extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param array $list * @return ContainerValue */ - public static function list(array $list): ContainerValue { + public static function list(array $list): ContainerValue + { return new ContainerValue([ 'type' => 'list', 'value' => $list, @@ -64,7 +65,8 @@ public static function list(array $list): ContainerValue { * @param ?FieldValue $optional * @return ContainerValue */ - public static function optional(?FieldValue $optional = null): ContainerValue { + public static function optional(?FieldValue $optional = null): ContainerValue + { return new ContainerValue([ 'type' => 'optional', 'value' => $optional, @@ -74,94 +76,101 @@ public static function optional(?FieldValue $optional = null): ContainerValue { /** * @return bool */ - public function isList_(): bool { - return is_array($this->value)&& $this->type === 'list'; + public function isList_(): bool + { + return is_array($this->value) && $this->type === 'list'; } /** * @return array */ - public function asList_(): array { - if (!(is_array($this->value)&& $this->type === 'list')){ + public function asList_(): array + { + if (!(is_array($this->value) && $this->type === 'list')) { throw new Exception( "Expected list; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isOptional(): bool { - return (is_null($this->value) || $this->value instanceof FieldValue)&& $this->type === 'optional'; + public function isOptional(): bool + { + return (is_null($this->value) || $this->value instanceof FieldValue) && $this->type === 'optional'; } /** * @return ?FieldValue */ - public function asOptional(): ?FieldValue { - if (!((is_null($this->value) || $this->value instanceof FieldValue)&& $this->type === 'optional')){ + public function asOptional(): ?FieldValue + { + if (!((is_null($this->value) || $this->value instanceof FieldValue) && $this->type === 'optional')) { throw new Exception( "Expected optional; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'list': $value = $this->value; $result['list'] = $value; break; case 'optional': $value = $this->asOptional(); - if (!is_null($value)){ + if (!is_null($value)) { $value = $value->jsonSerialize(); } $result['optional'] = $value; break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -170,42 +179,43 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'list': - if (!array_key_exists('list', $data)){ + if (!array_key_exists('list', $data)) { throw new Exception( "JSON data is missing property 'list'", ); } - + $args['value'] = $data['list']; break; case 'optional': - if (!array_key_exists('optional', $data)){ + if (!array_key_exists('optional', $data)) { throw new Exception( "JSON data is missing property 'optional'", ); } - - if (is_null($data['optional'])){ + + if (is_null($data['optional'])) { $args['value'] = null; - } else{ - if (!(is_array($data['optional']))){ + } else { + if (!(is_array($data['optional']))) { throw new Exception( "Expected property 'optional' in JSON data to be array, instead received " . get_debug_type($data['optional']), ); @@ -218,7 +228,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/circular-references/src/Ast/FieldValue.php b/seed/php-model/circular-references/src/Ast/FieldValue.php index 3e690627a423..62e8268589d7 100644 --- a/seed/php-model/circular-references/src/Ast/FieldValue.php +++ b/seed/php-model/circular-references/src/Ast/FieldValue.php @@ -46,16 +46,17 @@ class FieldValue extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param value-of $primitiveValue * @return FieldValue */ - public static function primitiveValue(string $primitiveValue): FieldValue { + public static function primitiveValue(string $primitiveValue): FieldValue + { return new FieldValue([ 'type' => 'primitive_value', 'value' => $primitiveValue, @@ -66,7 +67,8 @@ public static function primitiveValue(string $primitiveValue): FieldValue { * @param ObjectValue $objectValue * @return FieldValue */ - public static function objectValue(ObjectValue $objectValue): FieldValue { + public static function objectValue(ObjectValue $objectValue): FieldValue + { return new FieldValue([ 'type' => 'object_value', 'value' => $objectValue, @@ -77,7 +79,8 @@ public static function objectValue(ObjectValue $objectValue): FieldValue { * @param ContainerValue $containerValue * @return FieldValue */ - public static function containerValue(ContainerValue $containerValue): FieldValue { + public static function containerValue(ContainerValue $containerValue): FieldValue + { return new FieldValue([ 'type' => 'container_value', 'value' => $containerValue, @@ -87,81 +90,89 @@ public static function containerValue(ContainerValue $containerValue): FieldValu /** * @return bool */ - public function isPrimitiveValue(): bool { - return $this->value instanceof PrimitiveValue&& $this->type === 'primitive_value'; + public function isPrimitiveValue(): bool + { + return $this->value instanceof PrimitiveValue && $this->type === 'primitive_value'; } /** * @return value-of */ - public function asPrimitiveValue(): string { - if (!($this->value instanceof PrimitiveValue&& $this->type === 'primitive_value')){ + public function asPrimitiveValue(): string + { + if (!($this->value instanceof PrimitiveValue && $this->type === 'primitive_value')) { throw new Exception( "Expected primitive_value; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isObjectValue(): bool { - return $this->value instanceof ObjectValue&& $this->type === 'object_value'; + public function isObjectValue(): bool + { + return $this->value instanceof ObjectValue && $this->type === 'object_value'; } /** * @return ObjectValue */ - public function asObjectValue(): ObjectValue { - if (!($this->value instanceof ObjectValue&& $this->type === 'object_value')){ + public function asObjectValue(): ObjectValue + { + if (!($this->value instanceof ObjectValue && $this->type === 'object_value')) { throw new Exception( "Expected object_value; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isContainerValue(): bool { - return $this->value instanceof ContainerValue&& $this->type === 'container_value'; + public function isContainerValue(): bool + { + return $this->value instanceof ContainerValue && $this->type === 'container_value'; } /** * @return ContainerValue */ - public function asContainerValue(): ContainerValue { - if (!($this->value instanceof ContainerValue&& $this->type === 'container_value')){ + public function asContainerValue(): ContainerValue + { + if (!($this->value instanceof ContainerValue && $this->type === 'container_value')) { throw new Exception( "Expected container_value; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'primitive_value': $value = $this->value; $result['primitive_value'] = $value; @@ -176,26 +187,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -204,42 +216,43 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'primitive_value': - if (!array_key_exists('primitive_value', $data)){ + if (!array_key_exists('primitive_value', $data)) { throw new Exception( "JSON data is missing property 'primitive_value'", ); } - + $args['value'] = $data['primitive_value']; break; case 'object_value': $args['value'] = ObjectValue::jsonDeserialize($data); break; case 'container_value': - if (!array_key_exists('container_value', $data)){ + if (!array_key_exists('container_value', $data)) { throw new Exception( "JSON data is missing property 'container_value'", ); } - - if (!(is_array($data['container_value']))){ + + if (!(is_array($data['container_value']))) { throw new Exception( "Expected property 'containerValue' in JSON data to be array, instead received " . get_debug_type($data['container_value']), ); @@ -251,7 +264,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/circular-references/src/Ast/ObjectValue.php b/seed/php-model/circular-references/src/Ast/ObjectValue.php index 2c09380f36b7..9fcb205c2f22 100644 --- a/seed/php-model/circular-references/src/Ast/ObjectValue.php +++ b/seed/php-model/circular-references/src/Ast/ObjectValue.php @@ -6,22 +6,21 @@ class ObjectValue extends JsonSerializableType { - /** * @param array{ * } $values */ public function __construct( array $values = [], - ) - { + ) { unset($values); } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/circular-references/src/Ast/PrimitiveValue.php b/seed/php-model/circular-references/src/Ast/PrimitiveValue.php index c41a5f950c50..6a21888ecfcd 100644 --- a/seed/php-model/circular-references/src/Ast/PrimitiveValue.php +++ b/seed/php-model/circular-references/src/Ast/PrimitiveValue.php @@ -2,8 +2,8 @@ namespace Seed\Ast; -enum PrimitiveValue - : string { +enum PrimitiveValue: string +{ case String = "STRING"; case Number = "NUMBER"; } diff --git a/seed/php-model/circular-references/src/Ast/T.php b/seed/php-model/circular-references/src/Ast/T.php index 76d67cd85796..4119951915fc 100644 --- a/seed/php-model/circular-references/src/Ast/T.php +++ b/seed/php-model/circular-references/src/Ast/T.php @@ -14,7 +14,7 @@ class T extends JsonSerializableType * |U * ) $child */ - #[JsonProperty('child'), Union(T::class,U::class)] + #[JsonProperty('child'), Union(T::class, U::class)] public T|U $child; /** @@ -27,15 +27,15 @@ class T extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->child = $values['child']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/circular-references/src/Ast/U.php b/seed/php-model/circular-references/src/Ast/U.php index b34237341d7e..6250457f3b4f 100644 --- a/seed/php-model/circular-references/src/Ast/U.php +++ b/seed/php-model/circular-references/src/Ast/U.php @@ -20,15 +20,15 @@ class U extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->child = $values['child']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/circular-references/src/Core/Json/JsonEncoder.php b/seed/php-model/circular-references/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/circular-references/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/circular-references/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/circular-references/src/Core/Json/JsonProperty.php b/seed/php-model/circular-references/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/circular-references/src/Core/Json/JsonProperty.php +++ b/seed/php-model/circular-references/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/circular-references/src/Core/Types/ArrayType.php b/seed/php-model/circular-references/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/circular-references/src/Core/Types/ArrayType.php +++ b/seed/php-model/circular-references/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/circular-references/src/Core/Types/Constant.php b/seed/php-model/circular-references/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/circular-references/src/Core/Types/Constant.php +++ b/seed/php-model/circular-references/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/circular-references/src/Core/Types/Union.php b/seed/php-model/circular-references/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/circular-references/src/Core/Types/Union.php +++ b/seed/php-model/circular-references/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/circular-references/src/ImportingA.php b/seed/php-model/circular-references/src/ImportingA.php index 5dedb77a116c..9f8e2e1569d9 100644 --- a/seed/php-model/circular-references/src/ImportingA.php +++ b/seed/php-model/circular-references/src/ImportingA.php @@ -21,15 +21,15 @@ class ImportingA extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->a = $values['a'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/circular-references/src/RootType.php b/seed/php-model/circular-references/src/RootType.php index 795d3683e9ee..9d0a7f746329 100644 --- a/seed/php-model/circular-references/src/RootType.php +++ b/seed/php-model/circular-references/src/RootType.php @@ -20,15 +20,15 @@ class RootType extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->s = $values['s']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/circular-references/src/Traits/RootType.php b/seed/php-model/circular-references/src/Traits/RootType.php index a2075e9032e2..ef8eae518c71 100644 --- a/seed/php-model/circular-references/src/Traits/RootType.php +++ b/seed/php-model/circular-references/src/Traits/RootType.php @@ -7,7 +7,7 @@ /** * @property string $s */ -trait RootType +trait RootType { /** * @var string $s diff --git a/seed/php-model/circular-references/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/circular-references/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/circular-references/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/circular-references/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/circular-references/tests/Core/Json/EnumTest.php b/seed/php-model/circular-references/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/circular-references/tests/Core/Json/EnumTest.php +++ b/seed/php-model/circular-references/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/circular-references/tests/Core/Json/TraitTest.php b/seed/php-model/circular-references/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/circular-references/tests/Core/Json/TraitTest.php +++ b/seed/php-model/circular-references/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/circular-references/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/circular-references/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/circular-references/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/circular-references/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/client-side-params/src/Core/Json/JsonEncoder.php b/seed/php-model/client-side-params/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/client-side-params/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/client-side-params/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/client-side-params/src/Core/Json/JsonProperty.php b/seed/php-model/client-side-params/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/client-side-params/src/Core/Json/JsonProperty.php +++ b/seed/php-model/client-side-params/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/client-side-params/src/Core/Types/ArrayType.php b/seed/php-model/client-side-params/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/client-side-params/src/Core/Types/ArrayType.php +++ b/seed/php-model/client-side-params/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/client-side-params/src/Core/Types/Constant.php b/seed/php-model/client-side-params/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/client-side-params/src/Core/Types/Constant.php +++ b/seed/php-model/client-side-params/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/client-side-params/src/Core/Types/Union.php b/seed/php-model/client-side-params/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/client-side-params/src/Core/Types/Union.php +++ b/seed/php-model/client-side-params/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/client-side-params/src/Types/Client.php b/seed/php-model/client-side-params/src/Types/Client.php index 7a5c41a816ff..27f1ee6a83cb 100644 --- a/seed/php-model/client-side-params/src/Types/Client.php +++ b/seed/php-model/client-side-params/src/Types/Client.php @@ -227,15 +227,44 @@ class Client extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->clientId = $values['clientId'];$this->tenant = $values['tenant'] ?? null;$this->name = $values['name'];$this->description = $values['description'] ?? null;$this->global = $values['global'] ?? null;$this->clientSecret = $values['clientSecret'] ?? null;$this->appType = $values['appType'] ?? null;$this->logoUri = $values['logoUri'] ?? null;$this->isFirstParty = $values['isFirstParty'] ?? null;$this->oidcConformant = $values['oidcConformant'] ?? null;$this->callbacks = $values['callbacks'] ?? null;$this->allowedOrigins = $values['allowedOrigins'] ?? null;$this->webOrigins = $values['webOrigins'] ?? null;$this->grantTypes = $values['grantTypes'] ?? null;$this->jwtConfiguration = $values['jwtConfiguration'] ?? null;$this->signingKeys = $values['signingKeys'] ?? null;$this->encryptionKey = $values['encryptionKey'] ?? null;$this->sso = $values['sso'] ?? null;$this->ssoDisabled = $values['ssoDisabled'] ?? null;$this->crossOriginAuth = $values['crossOriginAuth'] ?? null;$this->crossOriginLoc = $values['crossOriginLoc'] ?? null;$this->customLoginPageOn = $values['customLoginPageOn'] ?? null;$this->customLoginPage = $values['customLoginPage'] ?? null;$this->customLoginPagePreview = $values['customLoginPagePreview'] ?? null;$this->formTemplate = $values['formTemplate'] ?? null;$this->isHerokuApp = $values['isHerokuApp'] ?? null;$this->addons = $values['addons'] ?? null;$this->tokenEndpointAuthMethod = $values['tokenEndpointAuthMethod'] ?? null;$this->clientMetadata = $values['clientMetadata'] ?? null;$this->mobile = $values['mobile'] ?? null; + ) { + $this->clientId = $values['clientId']; + $this->tenant = $values['tenant'] ?? null; + $this->name = $values['name']; + $this->description = $values['description'] ?? null; + $this->global = $values['global'] ?? null; + $this->clientSecret = $values['clientSecret'] ?? null; + $this->appType = $values['appType'] ?? null; + $this->logoUri = $values['logoUri'] ?? null; + $this->isFirstParty = $values['isFirstParty'] ?? null; + $this->oidcConformant = $values['oidcConformant'] ?? null; + $this->callbacks = $values['callbacks'] ?? null; + $this->allowedOrigins = $values['allowedOrigins'] ?? null; + $this->webOrigins = $values['webOrigins'] ?? null; + $this->grantTypes = $values['grantTypes'] ?? null; + $this->jwtConfiguration = $values['jwtConfiguration'] ?? null; + $this->signingKeys = $values['signingKeys'] ?? null; + $this->encryptionKey = $values['encryptionKey'] ?? null; + $this->sso = $values['sso'] ?? null; + $this->ssoDisabled = $values['ssoDisabled'] ?? null; + $this->crossOriginAuth = $values['crossOriginAuth'] ?? null; + $this->crossOriginLoc = $values['crossOriginLoc'] ?? null; + $this->customLoginPageOn = $values['customLoginPageOn'] ?? null; + $this->customLoginPage = $values['customLoginPage'] ?? null; + $this->customLoginPagePreview = $values['customLoginPagePreview'] ?? null; + $this->formTemplate = $values['formTemplate'] ?? null; + $this->isHerokuApp = $values['isHerokuApp'] ?? null; + $this->addons = $values['addons'] ?? null; + $this->tokenEndpointAuthMethod = $values['tokenEndpointAuthMethod'] ?? null; + $this->clientMetadata = $values['clientMetadata'] ?? null; + $this->mobile = $values['mobile'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/client-side-params/src/Types/Connection.php b/seed/php-model/client-side-params/src/Types/Connection.php index 288b6fa17a08..913db17b76c5 100644 --- a/seed/php-model/client-side-params/src/Types/Connection.php +++ b/seed/php-model/client-side-params/src/Types/Connection.php @@ -80,15 +80,23 @@ class Connection extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->name = $values['name'];$this->displayName = $values['displayName'] ?? null;$this->strategy = $values['strategy'];$this->options = $values['options'] ?? null;$this->enabledClients = $values['enabledClients'] ?? null;$this->realms = $values['realms'] ?? null;$this->isDomainConnection = $values['isDomainConnection'] ?? null;$this->metadata = $values['metadata'] ?? null; + ) { + $this->id = $values['id']; + $this->name = $values['name']; + $this->displayName = $values['displayName'] ?? null; + $this->strategy = $values['strategy']; + $this->options = $values['options'] ?? null; + $this->enabledClients = $values['enabledClients'] ?? null; + $this->realms = $values['realms'] ?? null; + $this->isDomainConnection = $values['isDomainConnection'] ?? null; + $this->metadata = $values['metadata'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/client-side-params/src/Types/CreateUserRequest.php b/seed/php-model/client-side-params/src/Types/CreateUserRequest.php index bd7251465fd0..8a72fcab86c5 100644 --- a/seed/php-model/client-side-params/src/Types/CreateUserRequest.php +++ b/seed/php-model/client-side-params/src/Types/CreateUserRequest.php @@ -77,15 +77,23 @@ class CreateUserRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->email = $values['email'];$this->emailVerified = $values['emailVerified'] ?? null;$this->username = $values['username'] ?? null;$this->password = $values['password'] ?? null;$this->phoneNumber = $values['phoneNumber'] ?? null;$this->phoneVerified = $values['phoneVerified'] ?? null;$this->userMetadata = $values['userMetadata'] ?? null;$this->appMetadata = $values['appMetadata'] ?? null;$this->connection = $values['connection']; + ) { + $this->email = $values['email']; + $this->emailVerified = $values['emailVerified'] ?? null; + $this->username = $values['username'] ?? null; + $this->password = $values['password'] ?? null; + $this->phoneNumber = $values['phoneNumber'] ?? null; + $this->phoneVerified = $values['phoneVerified'] ?? null; + $this->userMetadata = $values['userMetadata'] ?? null; + $this->appMetadata = $values['appMetadata'] ?? null; + $this->connection = $values['connection']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/client-side-params/src/Types/Identity.php b/seed/php-model/client-side-params/src/Types/Identity.php index 8ac4b3790b72..08291eeb7446 100644 --- a/seed/php-model/client-side-params/src/Types/Identity.php +++ b/seed/php-model/client-side-params/src/Types/Identity.php @@ -55,15 +55,20 @@ class Identity extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->connection = $values['connection'];$this->userId = $values['userId'];$this->provider = $values['provider'];$this->isSocial = $values['isSocial'];$this->accessToken = $values['accessToken'] ?? null;$this->expiresIn = $values['expiresIn'] ?? null; + ) { + $this->connection = $values['connection']; + $this->userId = $values['userId']; + $this->provider = $values['provider']; + $this->isSocial = $values['isSocial']; + $this->accessToken = $values['accessToken'] ?? null; + $this->expiresIn = $values['expiresIn'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/client-side-params/src/Types/PaginatedClientResponse.php b/seed/php-model/client-side-params/src/Types/PaginatedClientResponse.php index 718256079566..02ede664a194 100644 --- a/seed/php-model/client-side-params/src/Types/PaginatedClientResponse.php +++ b/seed/php-model/client-side-params/src/Types/PaginatedClientResponse.php @@ -52,15 +52,19 @@ class PaginatedClientResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->start = $values['start'];$this->limit = $values['limit'];$this->length = $values['length'];$this->total = $values['total'] ?? null;$this->clients = $values['clients']; + ) { + $this->start = $values['start']; + $this->limit = $values['limit']; + $this->length = $values['length']; + $this->total = $values['total'] ?? null; + $this->clients = $values['clients']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/client-side-params/src/Types/PaginatedUserResponse.php b/seed/php-model/client-side-params/src/Types/PaginatedUserResponse.php index 9715c7676317..fe95cb612c3c 100644 --- a/seed/php-model/client-side-params/src/Types/PaginatedUserResponse.php +++ b/seed/php-model/client-side-params/src/Types/PaginatedUserResponse.php @@ -52,15 +52,19 @@ class PaginatedUserResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->users = $values['users'];$this->start = $values['start'];$this->limit = $values['limit'];$this->length = $values['length'];$this->total = $values['total'] ?? null; + ) { + $this->users = $values['users']; + $this->start = $values['start']; + $this->limit = $values['limit']; + $this->length = $values['length']; + $this->total = $values['total'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/client-side-params/src/Types/Resource.php b/seed/php-model/client-side-params/src/Types/Resource.php index c8e831c18320..1035f872add5 100644 --- a/seed/php-model/client-side-params/src/Types/Resource.php +++ b/seed/php-model/client-side-params/src/Types/Resource.php @@ -58,15 +58,20 @@ class Resource extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->name = $values['name'];$this->description = $values['description'] ?? null;$this->createdAt = $values['createdAt'];$this->updatedAt = $values['updatedAt'];$this->metadata = $values['metadata'] ?? null; + ) { + $this->id = $values['id']; + $this->name = $values['name']; + $this->description = $values['description'] ?? null; + $this->createdAt = $values['createdAt']; + $this->updatedAt = $values['updatedAt']; + $this->metadata = $values['metadata'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/client-side-params/src/Types/SearchResponse.php b/seed/php-model/client-side-params/src/Types/SearchResponse.php index 6b2c9957be97..c084ab478d70 100644 --- a/seed/php-model/client-side-params/src/Types/SearchResponse.php +++ b/seed/php-model/client-side-params/src/Types/SearchResponse.php @@ -35,15 +35,17 @@ class SearchResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->results = $values['results'];$this->total = $values['total'] ?? null;$this->nextOffset = $values['nextOffset'] ?? null; + ) { + $this->results = $values['results']; + $this->total = $values['total'] ?? null; + $this->nextOffset = $values['nextOffset'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/client-side-params/src/Types/UpdateUserRequest.php b/seed/php-model/client-side-params/src/Types/UpdateUserRequest.php index 0c4fa7e3e500..523cc33fec7d 100644 --- a/seed/php-model/client-side-params/src/Types/UpdateUserRequest.php +++ b/seed/php-model/client-side-params/src/Types/UpdateUserRequest.php @@ -77,15 +77,23 @@ class UpdateUserRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->email = $values['email'] ?? null;$this->emailVerified = $values['emailVerified'] ?? null;$this->username = $values['username'] ?? null;$this->phoneNumber = $values['phoneNumber'] ?? null;$this->phoneVerified = $values['phoneVerified'] ?? null;$this->userMetadata = $values['userMetadata'] ?? null;$this->appMetadata = $values['appMetadata'] ?? null;$this->password = $values['password'] ?? null;$this->blocked = $values['blocked'] ?? null; + ) { + $this->email = $values['email'] ?? null; + $this->emailVerified = $values['emailVerified'] ?? null; + $this->username = $values['username'] ?? null; + $this->phoneNumber = $values['phoneNumber'] ?? null; + $this->phoneVerified = $values['phoneVerified'] ?? null; + $this->userMetadata = $values['userMetadata'] ?? null; + $this->appMetadata = $values['appMetadata'] ?? null; + $this->password = $values['password'] ?? null; + $this->blocked = $values['blocked'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/client-side-params/src/Types/User.php b/seed/php-model/client-side-params/src/Types/User.php index 82dd1783d9b0..10a908e64729 100644 --- a/seed/php-model/client-side-params/src/Types/User.php +++ b/seed/php-model/client-side-params/src/Types/User.php @@ -166,15 +166,35 @@ class User extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->userId = $values['userId'];$this->email = $values['email'];$this->emailVerified = $values['emailVerified'];$this->username = $values['username'] ?? null;$this->phoneNumber = $values['phoneNumber'] ?? null;$this->phoneVerified = $values['phoneVerified'] ?? null;$this->createdAt = $values['createdAt'];$this->updatedAt = $values['updatedAt'];$this->identities = $values['identities'] ?? null;$this->appMetadata = $values['appMetadata'] ?? null;$this->userMetadata = $values['userMetadata'] ?? null;$this->picture = $values['picture'] ?? null;$this->name = $values['name'] ?? null;$this->nickname = $values['nickname'] ?? null;$this->multifactor = $values['multifactor'] ?? null;$this->lastIp = $values['lastIp'] ?? null;$this->lastLogin = $values['lastLogin'] ?? null;$this->loginsCount = $values['loginsCount'] ?? null;$this->blocked = $values['blocked'] ?? null;$this->givenName = $values['givenName'] ?? null;$this->familyName = $values['familyName'] ?? null; + ) { + $this->userId = $values['userId']; + $this->email = $values['email']; + $this->emailVerified = $values['emailVerified']; + $this->username = $values['username'] ?? null; + $this->phoneNumber = $values['phoneNumber'] ?? null; + $this->phoneVerified = $values['phoneVerified'] ?? null; + $this->createdAt = $values['createdAt']; + $this->updatedAt = $values['updatedAt']; + $this->identities = $values['identities'] ?? null; + $this->appMetadata = $values['appMetadata'] ?? null; + $this->userMetadata = $values['userMetadata'] ?? null; + $this->picture = $values['picture'] ?? null; + $this->name = $values['name'] ?? null; + $this->nickname = $values['nickname'] ?? null; + $this->multifactor = $values['multifactor'] ?? null; + $this->lastIp = $values['lastIp'] ?? null; + $this->lastLogin = $values['lastLogin'] ?? null; + $this->loginsCount = $values['loginsCount'] ?? null; + $this->blocked = $values['blocked'] ?? null; + $this->givenName = $values['givenName'] ?? null; + $this->familyName = $values['familyName'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/client-side-params/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/client-side-params/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/client-side-params/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/client-side-params/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/client-side-params/tests/Core/Json/EnumTest.php b/seed/php-model/client-side-params/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/client-side-params/tests/Core/Json/EnumTest.php +++ b/seed/php-model/client-side-params/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/client-side-params/tests/Core/Json/TraitTest.php b/seed/php-model/client-side-params/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/client-side-params/tests/Core/Json/TraitTest.php +++ b/seed/php-model/client-side-params/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/client-side-params/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/client-side-params/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/client-side-params/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/client-side-params/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/content-type/src/Core/Json/JsonEncoder.php b/seed/php-model/content-type/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/content-type/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/content-type/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/content-type/src/Core/Json/JsonProperty.php b/seed/php-model/content-type/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/content-type/src/Core/Json/JsonProperty.php +++ b/seed/php-model/content-type/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/content-type/src/Core/Types/ArrayType.php b/seed/php-model/content-type/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/content-type/src/Core/Types/ArrayType.php +++ b/seed/php-model/content-type/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/content-type/src/Core/Types/Constant.php b/seed/php-model/content-type/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/content-type/src/Core/Types/Constant.php +++ b/seed/php-model/content-type/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/content-type/src/Core/Types/Union.php b/seed/php-model/content-type/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/content-type/src/Core/Types/Union.php +++ b/seed/php-model/content-type/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/content-type/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/content-type/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/content-type/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/content-type/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/content-type/tests/Core/Json/EnumTest.php b/seed/php-model/content-type/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/content-type/tests/Core/Json/EnumTest.php +++ b/seed/php-model/content-type/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/content-type/tests/Core/Json/TraitTest.php b/seed/php-model/content-type/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/content-type/tests/Core/Json/TraitTest.php +++ b/seed/php-model/content-type/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/content-type/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/content-type/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/content-type/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/content-type/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/cross-package-type-names/src/Core/Json/JsonEncoder.php b/seed/php-model/cross-package-type-names/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/cross-package-type-names/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/cross-package-type-names/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/cross-package-type-names/src/Core/Json/JsonProperty.php b/seed/php-model/cross-package-type-names/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/cross-package-type-names/src/Core/Json/JsonProperty.php +++ b/seed/php-model/cross-package-type-names/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/cross-package-type-names/src/Core/Types/ArrayType.php b/seed/php-model/cross-package-type-names/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/cross-package-type-names/src/Core/Types/ArrayType.php +++ b/seed/php-model/cross-package-type-names/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/cross-package-type-names/src/Core/Types/Constant.php b/seed/php-model/cross-package-type-names/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/cross-package-type-names/src/Core/Types/Constant.php +++ b/seed/php-model/cross-package-type-names/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/cross-package-type-names/src/Core/Types/Union.php b/seed/php-model/cross-package-type-names/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/cross-package-type-names/src/Core/Types/Union.php +++ b/seed/php-model/cross-package-type-names/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/cross-package-type-names/src/FolderA/Service/Response.php b/seed/php-model/cross-package-type-names/src/FolderA/Service/Response.php index 73cb7853e8b4..a65dff7630d3 100644 --- a/seed/php-model/cross-package-type-names/src/FolderA/Service/Response.php +++ b/seed/php-model/cross-package-type-names/src/FolderA/Service/Response.php @@ -21,15 +21,15 @@ class Response extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->foo = $values['foo'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/cross-package-type-names/src/FolderB/Common/Foo.php b/seed/php-model/cross-package-type-names/src/FolderB/Common/Foo.php index c5a992f838af..07fc6cee1d3e 100644 --- a/seed/php-model/cross-package-type-names/src/FolderB/Common/Foo.php +++ b/seed/php-model/cross-package-type-names/src/FolderB/Common/Foo.php @@ -20,15 +20,15 @@ class Foo extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->foo = $values['foo'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/cross-package-type-names/src/FolderC/Common/Foo.php b/seed/php-model/cross-package-type-names/src/FolderC/Common/Foo.php index 3424c11050fa..b31c9b7ff242 100644 --- a/seed/php-model/cross-package-type-names/src/FolderC/Common/Foo.php +++ b/seed/php-model/cross-package-type-names/src/FolderC/Common/Foo.php @@ -20,15 +20,15 @@ class Foo extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->barProperty = $values['barProperty']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/cross-package-type-names/src/FolderD/Service/Response.php b/seed/php-model/cross-package-type-names/src/FolderD/Service/Response.php index 1ad7fc60dd98..0fe9663ebb3a 100644 --- a/seed/php-model/cross-package-type-names/src/FolderD/Service/Response.php +++ b/seed/php-model/cross-package-type-names/src/FolderD/Service/Response.php @@ -21,15 +21,15 @@ class Response extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->foo = $values['foo'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/cross-package-type-names/src/Foo/ImportingType.php b/seed/php-model/cross-package-type-names/src/Foo/ImportingType.php index 58903d106ef4..4ae8ba849bd7 100644 --- a/seed/php-model/cross-package-type-names/src/Foo/ImportingType.php +++ b/seed/php-model/cross-package-type-names/src/Foo/ImportingType.php @@ -20,15 +20,15 @@ class ImportingType extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->imported = $values['imported']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/cross-package-type-names/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/cross-package-type-names/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/cross-package-type-names/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/cross-package-type-names/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/cross-package-type-names/tests/Core/Json/EnumTest.php b/seed/php-model/cross-package-type-names/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/cross-package-type-names/tests/Core/Json/EnumTest.php +++ b/seed/php-model/cross-package-type-names/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/cross-package-type-names/tests/Core/Json/TraitTest.php b/seed/php-model/cross-package-type-names/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/cross-package-type-names/tests/Core/Json/TraitTest.php +++ b/seed/php-model/cross-package-type-names/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/cross-package-type-names/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/cross-package-type-names/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/cross-package-type-names/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/cross-package-type-names/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/empty-clients/src/Core/Json/JsonEncoder.php b/seed/php-model/empty-clients/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/empty-clients/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/empty-clients/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/empty-clients/src/Core/Json/JsonProperty.php b/seed/php-model/empty-clients/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/empty-clients/src/Core/Json/JsonProperty.php +++ b/seed/php-model/empty-clients/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/empty-clients/src/Core/Types/ArrayType.php b/seed/php-model/empty-clients/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/empty-clients/src/Core/Types/ArrayType.php +++ b/seed/php-model/empty-clients/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/empty-clients/src/Core/Types/Constant.php b/seed/php-model/empty-clients/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/empty-clients/src/Core/Types/Constant.php +++ b/seed/php-model/empty-clients/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/empty-clients/src/Core/Types/Union.php b/seed/php-model/empty-clients/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/empty-clients/src/Core/Types/Union.php +++ b/seed/php-model/empty-clients/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/empty-clients/src/Level1/Level2/Types/Address.php b/seed/php-model/empty-clients/src/Level1/Level2/Types/Address.php index 0f547aaf893b..4631354db916 100644 --- a/seed/php-model/empty-clients/src/Level1/Level2/Types/Address.php +++ b/seed/php-model/empty-clients/src/Level1/Level2/Types/Address.php @@ -55,15 +55,20 @@ class Address extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->line1 = $values['line1'];$this->line2 = $values['line2'] ?? null;$this->city = $values['city'];$this->state = $values['state'];$this->zip = $values['zip'];$this->country = $values['country']; + ) { + $this->line1 = $values['line1']; + $this->line2 = $values['line2'] ?? null; + $this->city = $values['city']; + $this->state = $values['state']; + $this->zip = $values['zip']; + $this->country = $values['country']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/empty-clients/src/Level1/Level2/Types/Person.php b/seed/php-model/empty-clients/src/Level1/Level2/Types/Person.php index 2fdf9131e3b3..9329e0ee58cd 100644 --- a/seed/php-model/empty-clients/src/Level1/Level2/Types/Person.php +++ b/seed/php-model/empty-clients/src/Level1/Level2/Types/Person.php @@ -27,15 +27,16 @@ class Person extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->address = $values['address']; + ) { + $this->name = $values['name']; + $this->address = $values['address']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/empty-clients/src/Level1/Types/Address.php b/seed/php-model/empty-clients/src/Level1/Types/Address.php index 1d8296221e04..b4bd8791472c 100644 --- a/seed/php-model/empty-clients/src/Level1/Types/Address.php +++ b/seed/php-model/empty-clients/src/Level1/Types/Address.php @@ -55,15 +55,20 @@ class Address extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->line1 = $values['line1'];$this->line2 = $values['line2'] ?? null;$this->city = $values['city'];$this->state = $values['state'];$this->zip = $values['zip'];$this->country = $values['country']; + ) { + $this->line1 = $values['line1']; + $this->line2 = $values['line2'] ?? null; + $this->city = $values['city']; + $this->state = $values['state']; + $this->zip = $values['zip']; + $this->country = $values['country']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/empty-clients/src/Level1/Types/Person.php b/seed/php-model/empty-clients/src/Level1/Types/Person.php index 01fd863effd0..f74ba648ba1e 100644 --- a/seed/php-model/empty-clients/src/Level1/Types/Person.php +++ b/seed/php-model/empty-clients/src/Level1/Types/Person.php @@ -27,15 +27,16 @@ class Person extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->address = $values['address']; + ) { + $this->name = $values['name']; + $this->address = $values['address']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/empty-clients/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/empty-clients/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/empty-clients/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/empty-clients/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/empty-clients/tests/Core/Json/EnumTest.php b/seed/php-model/empty-clients/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/empty-clients/tests/Core/Json/EnumTest.php +++ b/seed/php-model/empty-clients/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/empty-clients/tests/Core/Json/TraitTest.php b/seed/php-model/empty-clients/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/empty-clients/tests/Core/Json/TraitTest.php +++ b/seed/php-model/empty-clients/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/empty-clients/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/empty-clients/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/empty-clients/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/empty-clients/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/enum/src/Color.php b/seed/php-model/enum/src/Color.php index 89647fce4ec3..d7aaf5b4985c 100644 --- a/seed/php-model/enum/src/Color.php +++ b/seed/php-model/enum/src/Color.php @@ -2,8 +2,8 @@ namespace Seed; -enum Color - : string { +enum Color: string +{ case Red = "red"; case Blue = "blue"; } diff --git a/seed/php-model/enum/src/Core/Json/JsonEncoder.php b/seed/php-model/enum/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/enum/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/enum/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/enum/src/Core/Json/JsonProperty.php b/seed/php-model/enum/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/enum/src/Core/Json/JsonProperty.php +++ b/seed/php-model/enum/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/enum/src/Core/Types/ArrayType.php b/seed/php-model/enum/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/enum/src/Core/Types/ArrayType.php +++ b/seed/php-model/enum/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/enum/src/Core/Types/Constant.php b/seed/php-model/enum/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/enum/src/Core/Types/Constant.php +++ b/seed/php-model/enum/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/enum/src/Core/Types/Union.php b/seed/php-model/enum/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/enum/src/Core/Types/Union.php +++ b/seed/php-model/enum/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/enum/src/EnumWithCustom.php b/seed/php-model/enum/src/EnumWithCustom.php index bb04c2bf9cdc..81edd8105a81 100644 --- a/seed/php-model/enum/src/EnumWithCustom.php +++ b/seed/php-model/enum/src/EnumWithCustom.php @@ -2,8 +2,8 @@ namespace Seed; -enum EnumWithCustom - : string { +enum EnumWithCustom: string +{ case Safe = "safe"; case Custom = "Custom"; } diff --git a/seed/php-model/enum/src/EnumWithSpecialCharacters.php b/seed/php-model/enum/src/EnumWithSpecialCharacters.php index 045129470419..1010607e455b 100644 --- a/seed/php-model/enum/src/EnumWithSpecialCharacters.php +++ b/seed/php-model/enum/src/EnumWithSpecialCharacters.php @@ -2,8 +2,8 @@ namespace Seed; -enum EnumWithSpecialCharacters - : string { +enum EnumWithSpecialCharacters: string +{ case Bla = "\\\$bla"; case Yo = "\\\$yo"; } diff --git a/seed/php-model/enum/src/Operand.php b/seed/php-model/enum/src/Operand.php index d3a020021299..f312669e7994 100644 --- a/seed/php-model/enum/src/Operand.php +++ b/seed/php-model/enum/src/Operand.php @@ -2,8 +2,8 @@ namespace Seed; -enum Operand - : string { +enum Operand: string +{ case GreaterThan = ">"; case EqualTo = "="; case LessThan = "less_than"; diff --git a/seed/php-model/enum/src/SpecialEnum.php b/seed/php-model/enum/src/SpecialEnum.php index aff41c3d5098..3c36675e3560 100644 --- a/seed/php-model/enum/src/SpecialEnum.php +++ b/seed/php-model/enum/src/SpecialEnum.php @@ -2,8 +2,8 @@ namespace Seed; -enum SpecialEnum - : string { +enum SpecialEnum: string +{ case A = ""; case B = "Hello \\\"World\\\""; case C = "Hello 'World'"; diff --git a/seed/php-model/enum/src/Unknown/Status.php b/seed/php-model/enum/src/Unknown/Status.php index 3530c35a440a..abe326bd8bbd 100644 --- a/seed/php-model/enum/src/Unknown/Status.php +++ b/seed/php-model/enum/src/Unknown/Status.php @@ -2,8 +2,8 @@ namespace Seed\Unknown; -enum Status - : string { +enum Status: string +{ case Known = "Known"; case Unknown = "Unknown"; } diff --git a/seed/php-model/enum/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/enum/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/enum/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/enum/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/enum/tests/Core/Json/EnumTest.php b/seed/php-model/enum/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/enum/tests/Core/Json/EnumTest.php +++ b/seed/php-model/enum/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/enum/tests/Core/Json/TraitTest.php b/seed/php-model/enum/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/enum/tests/Core/Json/TraitTest.php +++ b/seed/php-model/enum/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/enum/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/enum/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/enum/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/enum/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/error-property/src/Core/Json/JsonEncoder.php b/seed/php-model/error-property/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/error-property/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/error-property/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/error-property/src/Core/Json/JsonProperty.php b/seed/php-model/error-property/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/error-property/src/Core/Json/JsonProperty.php +++ b/seed/php-model/error-property/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/error-property/src/Core/Types/ArrayType.php b/seed/php-model/error-property/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/error-property/src/Core/Types/ArrayType.php +++ b/seed/php-model/error-property/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/error-property/src/Core/Types/Constant.php b/seed/php-model/error-property/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/error-property/src/Core/Types/Constant.php +++ b/seed/php-model/error-property/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/error-property/src/Core/Types/Union.php b/seed/php-model/error-property/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/error-property/src/Core/Types/Union.php +++ b/seed/php-model/error-property/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/error-property/src/Errors/PropertyBasedErrorTestBody.php b/seed/php-model/error-property/src/Errors/PropertyBasedErrorTestBody.php index 29d0e762aad9..6ac9e881e3bb 100644 --- a/seed/php-model/error-property/src/Errors/PropertyBasedErrorTestBody.php +++ b/seed/php-model/error-property/src/Errors/PropertyBasedErrorTestBody.php @@ -20,15 +20,15 @@ class PropertyBasedErrorTestBody extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->message = $values['message']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/error-property/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/error-property/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/error-property/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/error-property/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/error-property/tests/Core/Json/EnumTest.php b/seed/php-model/error-property/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/error-property/tests/Core/Json/EnumTest.php +++ b/seed/php-model/error-property/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/error-property/tests/Core/Json/TraitTest.php b/seed/php-model/error-property/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/error-property/tests/Core/Json/TraitTest.php +++ b/seed/php-model/error-property/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/error-property/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/error-property/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/error-property/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/error-property/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/errors/src/Commons/ErrorBody.php b/seed/php-model/errors/src/Commons/ErrorBody.php index 456d74b418cb..9710188e6f12 100644 --- a/seed/php-model/errors/src/Commons/ErrorBody.php +++ b/seed/php-model/errors/src/Commons/ErrorBody.php @@ -27,15 +27,16 @@ class ErrorBody extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->message = $values['message'];$this->code = $values['code']; + ) { + $this->message = $values['message']; + $this->code = $values['code']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/errors/src/Core/Json/JsonEncoder.php b/seed/php-model/errors/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/errors/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/errors/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/errors/src/Core/Json/JsonProperty.php b/seed/php-model/errors/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/errors/src/Core/Json/JsonProperty.php +++ b/seed/php-model/errors/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/errors/src/Core/Types/ArrayType.php b/seed/php-model/errors/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/errors/src/Core/Types/ArrayType.php +++ b/seed/php-model/errors/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/errors/src/Core/Types/Constant.php b/seed/php-model/errors/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/errors/src/Core/Types/Constant.php +++ b/seed/php-model/errors/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/errors/src/Core/Types/Union.php b/seed/php-model/errors/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/errors/src/Core/Types/Union.php +++ b/seed/php-model/errors/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/errors/src/Simple/FooRequest.php b/seed/php-model/errors/src/Simple/FooRequest.php index b2a1fa55a508..4cc99693cef5 100644 --- a/seed/php-model/errors/src/Simple/FooRequest.php +++ b/seed/php-model/errors/src/Simple/FooRequest.php @@ -20,15 +20,15 @@ class FooRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->bar = $values['bar']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/errors/src/Simple/FooResponse.php b/seed/php-model/errors/src/Simple/FooResponse.php index 025a937299bf..88d6c0fec392 100644 --- a/seed/php-model/errors/src/Simple/FooResponse.php +++ b/seed/php-model/errors/src/Simple/FooResponse.php @@ -20,15 +20,15 @@ class FooResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->bar = $values['bar']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/errors/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/errors/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/errors/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/errors/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/errors/tests/Core/Json/EnumTest.php b/seed/php-model/errors/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/errors/tests/Core/Json/EnumTest.php +++ b/seed/php-model/errors/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/errors/tests/Core/Json/TraitTest.php b/seed/php-model/errors/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/errors/tests/Core/Json/TraitTest.php +++ b/seed/php-model/errors/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/errors/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/errors/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/errors/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/errors/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/examples/src/BasicType.php b/seed/php-model/examples/src/BasicType.php index 27ea8d24f934..28c43837aa46 100644 --- a/seed/php-model/examples/src/BasicType.php +++ b/seed/php-model/examples/src/BasicType.php @@ -2,8 +2,8 @@ namespace Seed; -enum BasicType - : string { +enum BasicType: string +{ case Primitive = "primitive"; case Literal = "literal"; } diff --git a/seed/php-model/examples/src/Commons/Types/Data.php b/seed/php-model/examples/src/Commons/Types/Data.php index 11435eda72e7..939e34085134 100644 --- a/seed/php-model/examples/src/Commons/Types/Data.php +++ b/seed/php-model/examples/src/Commons/Types/Data.php @@ -40,16 +40,17 @@ class Data extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param string $string * @return Data */ - public static function string(string $string): Data { + public static function string(string $string): Data + { return new Data([ 'type' => 'string', 'value' => $string, @@ -60,7 +61,8 @@ public static function string(string $string): Data { * @param string $base64 * @return Data */ - public static function base64(string $base64): Data { + public static function base64(string $base64): Data + { return new Data([ 'type' => 'base64', 'value' => $base64, @@ -70,61 +72,67 @@ public static function base64(string $base64): Data { /** * @return bool */ - public function isString(): bool { - return is_string($this->value)&& $this->type === 'string'; + public function isString(): bool + { + return is_string($this->value) && $this->type === 'string'; } /** * @return string */ - public function asString(): string { - if (!(is_string($this->value)&& $this->type === 'string')){ + public function asString(): string + { + if (!(is_string($this->value) && $this->type === 'string')) { throw new Exception( "Expected string; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isBase64(): bool { - return is_string($this->value)&& $this->type === 'base64'; + public function isBase64(): bool + { + return is_string($this->value) && $this->type === 'base64'; } /** * @return string */ - public function asBase64(): string { - if (!(is_string($this->value)&& $this->type === 'base64')){ + public function asBase64(): string + { + if (!(is_string($this->value) && $this->type === 'base64')) { throw new Exception( "Expected base64; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'string': $value = $this->value; $result['string'] = $value; @@ -135,26 +143,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -163,38 +172,39 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'string': - if (!array_key_exists('string', $data)){ + if (!array_key_exists('string', $data)) { throw new Exception( "JSON data is missing property 'string'", ); } - + $args['value'] = $data['string']; break; case 'base64': - if (!array_key_exists('base64', $data)){ + if (!array_key_exists('base64', $data)) { throw new Exception( "JSON data is missing property 'base64'", ); } - + $args['value'] = $data['base64']; break; case '_unknown': @@ -202,7 +212,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/examples/src/Commons/Types/EventInfo.php b/seed/php-model/examples/src/Commons/Types/EventInfo.php index e8567e17e62e..3aa65dc736a0 100644 --- a/seed/php-model/examples/src/Commons/Types/EventInfo.php +++ b/seed/php-model/examples/src/Commons/Types/EventInfo.php @@ -42,16 +42,17 @@ class EventInfo extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param Metadata $metadata * @return EventInfo */ - public static function metadata(Metadata $metadata): EventInfo { + public static function metadata(Metadata $metadata): EventInfo + { return new EventInfo([ 'type' => 'metadata', 'value' => $metadata, @@ -62,7 +63,8 @@ public static function metadata(Metadata $metadata): EventInfo { * @param string $tag * @return EventInfo */ - public static function tag(string $tag): EventInfo { + public static function tag(string $tag): EventInfo + { return new EventInfo([ 'type' => 'tag', 'value' => $tag, @@ -72,61 +74,67 @@ public static function tag(string $tag): EventInfo { /** * @return bool */ - public function isMetadata(): bool { - return $this->value instanceof Metadata&& $this->type === 'metadata'; + public function isMetadata(): bool + { + return $this->value instanceof Metadata && $this->type === 'metadata'; } /** * @return Metadata */ - public function asMetadata(): Metadata { - if (!($this->value instanceof Metadata&& $this->type === 'metadata')){ + public function asMetadata(): Metadata + { + if (!($this->value instanceof Metadata && $this->type === 'metadata')) { throw new Exception( "Expected metadata; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isTag(): bool { - return is_string($this->value)&& $this->type === 'tag'; + public function isTag(): bool + { + return is_string($this->value) && $this->type === 'tag'; } /** * @return string */ - public function asTag(): string { - if (!(is_string($this->value)&& $this->type === 'tag')){ + public function asTag(): string + { + if (!(is_string($this->value) && $this->type === 'tag')) { throw new Exception( "Expected tag; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'metadata': $value = $this->asMetadata()->jsonSerialize(); $result = array_merge($value, $result); @@ -137,26 +145,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -165,32 +174,33 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'metadata': $args['value'] = Metadata::jsonDeserialize($data); break; case 'tag': - if (!array_key_exists('tag', $data)){ + if (!array_key_exists('tag', $data)) { throw new Exception( "JSON data is missing property 'tag'", ); } - + $args['value'] = $data['tag']; break; case '_unknown': @@ -198,7 +208,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/examples/src/Commons/Types/Metadata.php b/seed/php-model/examples/src/Commons/Types/Metadata.php index e13fa3262482..972906eb565a 100644 --- a/seed/php-model/examples/src/Commons/Types/Metadata.php +++ b/seed/php-model/examples/src/Commons/Types/Metadata.php @@ -35,15 +35,17 @@ class Metadata extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->data = $values['data'] ?? null;$this->jsonString = $values['jsonString'] ?? null; + ) { + $this->id = $values['id']; + $this->data = $values['data'] ?? null; + $this->jsonString = $values['jsonString'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/examples/src/ComplexType.php b/seed/php-model/examples/src/ComplexType.php index 670cfde730d5..496c3e20dca0 100644 --- a/seed/php-model/examples/src/ComplexType.php +++ b/seed/php-model/examples/src/ComplexType.php @@ -2,8 +2,8 @@ namespace Seed; -enum ComplexType - : string { +enum ComplexType: string +{ case Object = "object"; case Union = "union"; case Unknown = "unknown"; diff --git a/seed/php-model/examples/src/Core/Json/JsonEncoder.php b/seed/php-model/examples/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/examples/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/examples/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/examples/src/Core/Json/JsonProperty.php b/seed/php-model/examples/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/examples/src/Core/Json/JsonProperty.php +++ b/seed/php-model/examples/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/examples/src/Core/Types/ArrayType.php b/seed/php-model/examples/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/examples/src/Core/Types/ArrayType.php +++ b/seed/php-model/examples/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/examples/src/Core/Types/Constant.php b/seed/php-model/examples/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/examples/src/Core/Types/Constant.php +++ b/seed/php-model/examples/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/examples/src/Core/Types/Union.php b/seed/php-model/examples/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/examples/src/Core/Types/Union.php +++ b/seed/php-model/examples/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/examples/src/Identifier.php b/seed/php-model/examples/src/Identifier.php index 98ae978224a5..95c79e061c1f 100644 --- a/seed/php-model/examples/src/Identifier.php +++ b/seed/php-model/examples/src/Identifier.php @@ -40,15 +40,17 @@ class Identifier extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value'];$this->label = $values['label']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; + $this->label = $values['label']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/examples/src/TypeWithSingleCharPropertyEqualToTypeStartingLetter.php b/seed/php-model/examples/src/TypeWithSingleCharPropertyEqualToTypeStartingLetter.php index 6d438c73cf58..fb19a41c6e4c 100644 --- a/seed/php-model/examples/src/TypeWithSingleCharPropertyEqualToTypeStartingLetter.php +++ b/seed/php-model/examples/src/TypeWithSingleCharPropertyEqualToTypeStartingLetter.php @@ -27,15 +27,16 @@ class TypeWithSingleCharPropertyEqualToTypeStartingLetter extends JsonSerializab */ public function __construct( array $values, - ) - { - $this->t = $values['t'];$this->ty = $values['ty']; + ) { + $this->t = $values['t']; + $this->ty = $values['ty']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/examples/src/Types/Actor.php b/seed/php-model/examples/src/Types/Actor.php index aca41c11490d..2a6a9c40c966 100644 --- a/seed/php-model/examples/src/Types/Actor.php +++ b/seed/php-model/examples/src/Types/Actor.php @@ -27,15 +27,16 @@ class Actor extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->id = $values['id']; + ) { + $this->name = $values['name']; + $this->id = $values['id']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/examples/src/Types/Actress.php b/seed/php-model/examples/src/Types/Actress.php index 47b647cedd5e..53a904c36ae0 100644 --- a/seed/php-model/examples/src/Types/Actress.php +++ b/seed/php-model/examples/src/Types/Actress.php @@ -27,15 +27,16 @@ class Actress extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->id = $values['id']; + ) { + $this->name = $values['name']; + $this->id = $values['id']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/examples/src/Types/BigEntity.php b/seed/php-model/examples/src/Types/BigEntity.php index 2e20750c79fa..a0875fba93dd 100644 --- a/seed/php-model/examples/src/Types/BigEntity.php +++ b/seed/php-model/examples/src/Types/BigEntity.php @@ -17,7 +17,7 @@ class BigEntity extends JsonSerializableType * |StuntDouble * )|null $castMember */ - #[JsonProperty('castMember'), Union(Actor::class,Actress::class,StuntDouble::class,'null')] + #[JsonProperty('castMember'), Union(Actor::class, Actress::class, StuntDouble::class, 'null')] public Actor|Actress|StuntDouble|null $castMember; /** @@ -115,15 +115,27 @@ class BigEntity extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->castMember = $values['castMember'] ?? null;$this->extendedMovie = $values['extendedMovie'] ?? null;$this->entity = $values['entity'] ?? null;$this->metadata = $values['metadata'] ?? null;$this->commonMetadata = $values['commonMetadata'] ?? null;$this->eventInfo = $values['eventInfo'] ?? null;$this->data = $values['data'] ?? null;$this->migration = $values['migration'] ?? null;$this->exception = $values['exception'] ?? null;$this->test = $values['test'] ?? null;$this->node = $values['node'] ?? null;$this->directory = $values['directory'] ?? null;$this->moment = $values['moment'] ?? null; + ) { + $this->castMember = $values['castMember'] ?? null; + $this->extendedMovie = $values['extendedMovie'] ?? null; + $this->entity = $values['entity'] ?? null; + $this->metadata = $values['metadata'] ?? null; + $this->commonMetadata = $values['commonMetadata'] ?? null; + $this->eventInfo = $values['eventInfo'] ?? null; + $this->data = $values['data'] ?? null; + $this->migration = $values['migration'] ?? null; + $this->exception = $values['exception'] ?? null; + $this->test = $values['test'] ?? null; + $this->node = $values['node'] ?? null; + $this->directory = $values['directory'] ?? null; + $this->moment = $values['moment'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/examples/src/Types/CronJob.php b/seed/php-model/examples/src/Types/CronJob.php index 6d5cc342535a..fcef0b8ea0b8 100644 --- a/seed/php-model/examples/src/Types/CronJob.php +++ b/seed/php-model/examples/src/Types/CronJob.php @@ -20,15 +20,15 @@ class CronJob extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->expression = $values['expression']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/examples/src/Types/Directory.php b/seed/php-model/examples/src/Types/Directory.php index 652ab7c29340..46af694d6e15 100644 --- a/seed/php-model/examples/src/Types/Directory.php +++ b/seed/php-model/examples/src/Types/Directory.php @@ -35,15 +35,17 @@ class Directory extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->files = $values['files'] ?? null;$this->directories = $values['directories'] ?? null; + ) { + $this->name = $values['name']; + $this->files = $values['files'] ?? null; + $this->directories = $values['directories'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/examples/src/Types/Entity.php b/seed/php-model/examples/src/Types/Entity.php index 7f77d998d84c..06655134a89c 100644 --- a/seed/php-model/examples/src/Types/Entity.php +++ b/seed/php-model/examples/src/Types/Entity.php @@ -35,15 +35,16 @@ class Entity extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->type = $values['type'];$this->name = $values['name']; + ) { + $this->type = $values['type']; + $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/examples/src/Types/ExceptionInfo.php b/seed/php-model/examples/src/Types/ExceptionInfo.php index 9763af47a021..b20c5ee6bfad 100644 --- a/seed/php-model/examples/src/Types/ExceptionInfo.php +++ b/seed/php-model/examples/src/Types/ExceptionInfo.php @@ -34,15 +34,17 @@ class ExceptionInfo extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->exceptionType = $values['exceptionType'];$this->exceptionMessage = $values['exceptionMessage'];$this->exceptionStacktrace = $values['exceptionStacktrace']; + ) { + $this->exceptionType = $values['exceptionType']; + $this->exceptionMessage = $values['exceptionMessage']; + $this->exceptionStacktrace = $values['exceptionStacktrace']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/examples/src/Types/ExtendedMovie.php b/seed/php-model/examples/src/Types/ExtendedMovie.php index f972e31bae0b..67ac030208b8 100644 --- a/seed/php-model/examples/src/Types/ExtendedMovie.php +++ b/seed/php-model/examples/src/Types/ExtendedMovie.php @@ -34,15 +34,25 @@ class ExtendedMovie extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->prequel = $values['prequel'] ?? null;$this->title = $values['title'];$this->from = $values['from'];$this->rating = $values['rating'];$this->type = $values['type'];$this->tag = $values['tag'];$this->book = $values['book'] ?? null;$this->metadata = $values['metadata'];$this->revenue = $values['revenue'];$this->cast = $values['cast']; + ) { + $this->id = $values['id']; + $this->prequel = $values['prequel'] ?? null; + $this->title = $values['title']; + $this->from = $values['from']; + $this->rating = $values['rating']; + $this->type = $values['type']; + $this->tag = $values['tag']; + $this->book = $values['book'] ?? null; + $this->metadata = $values['metadata']; + $this->revenue = $values['revenue']; + $this->cast = $values['cast']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/examples/src/Types/File.php b/seed/php-model/examples/src/Types/File.php index 2ad02ab0b71e..69b973216d92 100644 --- a/seed/php-model/examples/src/Types/File.php +++ b/seed/php-model/examples/src/Types/File.php @@ -27,15 +27,16 @@ class File extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->contents = $values['contents']; + ) { + $this->name = $values['name']; + $this->contents = $values['contents']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/examples/src/Types/Metadata.php b/seed/php-model/examples/src/Types/Metadata.php index 5cbc173c626a..fdfafcf50003 100644 --- a/seed/php-model/examples/src/Types/Metadata.php +++ b/seed/php-model/examples/src/Types/Metadata.php @@ -56,9 +56,11 @@ class Metadata extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->extra = $values['extra'];$this->tags = $values['tags'];$this->type = $values['type'];$this->value = $values['value']; + ) { + $this->extra = $values['extra']; + $this->tags = $values['tags']; + $this->type = $values['type']; + $this->value = $values['value']; } /** @@ -67,7 +69,8 @@ private function __construct( * @param string $html * @return Metadata */ - public static function html(array $extra, array $tags, string $html): Metadata { + public static function html(array $extra, array $tags, string $html): Metadata + { return new Metadata([ 'extra' => $extra, 'tags' => $tags, @@ -82,7 +85,8 @@ public static function html(array $extra, array $tags, string $html): Metadata { * @param string $markdown * @return Metadata */ - public static function markdown(array $extra, array $tags, string $markdown): Metadata { + public static function markdown(array $extra, array $tags, string $markdown): Metadata + { return new Metadata([ 'extra' => $extra, 'tags' => $tags, @@ -94,61 +98,67 @@ public static function markdown(array $extra, array $tags, string $markdown): Me /** * @return bool */ - public function isHtml(): bool { - return is_string($this->value)&& $this->type === 'html'; + public function isHtml(): bool + { + return is_string($this->value) && $this->type === 'html'; } /** * @return string */ - public function asHtml(): string { - if (!(is_string($this->value)&& $this->type === 'html')){ + public function asHtml(): string + { + if (!(is_string($this->value) && $this->type === 'html')) { throw new Exception( "Expected html; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isMarkdown(): bool { - return is_string($this->value)&& $this->type === 'markdown'; + public function isMarkdown(): bool + { + return is_string($this->value) && $this->type === 'markdown'; } /** * @return string */ - public function asMarkdown(): string { - if (!(is_string($this->value)&& $this->type === 'markdown')){ + public function asMarkdown(): string + { + if (!(is_string($this->value) && $this->type === 'markdown')) { throw new Exception( "Expected markdown; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'html': $value = $this->value; $result['html'] = $value; @@ -159,26 +169,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -187,62 +198,63 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('extra', $data)){ + if (!array_key_exists('extra', $data)) { throw new Exception( "JSON data is missing property 'extra'", ); } - if (!(is_array($data['extra']))){ + if (!(is_array($data['extra']))) { throw new Exception( "Expected property 'extra' in JSON data to be map, instead received " . get_debug_type($data['extra']), ); } $args['extra'] = $data['extra']; - - if (!array_key_exists('tags', $data)){ + + if (!array_key_exists('tags', $data)) { throw new Exception( "JSON data is missing property 'tags'", ); } - if (!(is_array($data['tags']))){ + if (!(is_array($data['tags']))) { throw new Exception( "Expected property 'tags' in JSON data to be array, instead received " . get_debug_type($data['tags']), ); } $args['tags'] = $data['tags']; - - if (!array_key_exists('type', $data)){ + + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'html': - if (!array_key_exists('html', $data)){ + if (!array_key_exists('html', $data)) { throw new Exception( "JSON data is missing property 'html'", ); } - + $args['value'] = $data['html']; break; case 'markdown': - if (!array_key_exists('markdown', $data)){ + if (!array_key_exists('markdown', $data)) { throw new Exception( "JSON data is missing property 'markdown'", ); } - + $args['value'] = $data['markdown']; break; case '_unknown': @@ -250,7 +262,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/examples/src/Types/Migration.php b/seed/php-model/examples/src/Types/Migration.php index 44cd34360854..40ff2a2e71be 100644 --- a/seed/php-model/examples/src/Types/Migration.php +++ b/seed/php-model/examples/src/Types/Migration.php @@ -27,15 +27,16 @@ class Migration extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->status = $values['status']; + ) { + $this->name = $values['name']; + $this->status = $values['status']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/examples/src/Types/MigrationStatus.php b/seed/php-model/examples/src/Types/MigrationStatus.php index 291487ba65e0..fa9c470de8aa 100644 --- a/seed/php-model/examples/src/Types/MigrationStatus.php +++ b/seed/php-model/examples/src/Types/MigrationStatus.php @@ -2,8 +2,8 @@ namespace Seed\Types; -enum MigrationStatus - : string { +enum MigrationStatus: string +{ case Running = "RUNNING"; case Failed = "FAILED"; case Finished = "FINISHED"; diff --git a/seed/php-model/examples/src/Types/Moment.php b/seed/php-model/examples/src/Types/Moment.php index 899a9555a0ca..c717cc3af83e 100644 --- a/seed/php-model/examples/src/Types/Moment.php +++ b/seed/php-model/examples/src/Types/Moment.php @@ -36,15 +36,17 @@ class Moment extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->date = $values['date'];$this->datetime = $values['datetime']; + ) { + $this->id = $values['id']; + $this->date = $values['date']; + $this->datetime = $values['datetime']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/examples/src/Types/Movie.php b/seed/php-model/examples/src/Types/Movie.php index 8692d5149499..6f3ee4266053 100644 --- a/seed/php-model/examples/src/Types/Movie.php +++ b/seed/php-model/examples/src/Types/Movie.php @@ -84,15 +84,24 @@ class Movie extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->prequel = $values['prequel'] ?? null;$this->title = $values['title'];$this->from = $values['from'];$this->rating = $values['rating'];$this->type = $values['type'];$this->tag = $values['tag'];$this->book = $values['book'] ?? null;$this->metadata = $values['metadata'];$this->revenue = $values['revenue']; + ) { + $this->id = $values['id']; + $this->prequel = $values['prequel'] ?? null; + $this->title = $values['title']; + $this->from = $values['from']; + $this->rating = $values['rating']; + $this->type = $values['type']; + $this->tag = $values['tag']; + $this->book = $values['book'] ?? null; + $this->metadata = $values['metadata']; + $this->revenue = $values['revenue']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/examples/src/Types/Node.php b/seed/php-model/examples/src/Types/Node.php index 66d6c7683e06..56abf3d887a5 100644 --- a/seed/php-model/examples/src/Types/Node.php +++ b/seed/php-model/examples/src/Types/Node.php @@ -35,15 +35,17 @@ class Node extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->nodes = $values['nodes'] ?? null;$this->trees = $values['trees'] ?? null; + ) { + $this->name = $values['name']; + $this->nodes = $values['nodes'] ?? null; + $this->trees = $values['trees'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/examples/src/Types/RefreshTokenRequest.php b/seed/php-model/examples/src/Types/RefreshTokenRequest.php index 74fc2159bb1c..f97ac10f3fb9 100644 --- a/seed/php-model/examples/src/Types/RefreshTokenRequest.php +++ b/seed/php-model/examples/src/Types/RefreshTokenRequest.php @@ -20,15 +20,15 @@ class RefreshTokenRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->ttl = $values['ttl']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/examples/src/Types/Request.php b/seed/php-model/examples/src/Types/Request.php index a3ae0b8e56c7..0713dc89c198 100644 --- a/seed/php-model/examples/src/Types/Request.php +++ b/seed/php-model/examples/src/Types/Request.php @@ -20,15 +20,15 @@ class Request extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->request = $values['request']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/examples/src/Types/Response.php b/seed/php-model/examples/src/Types/Response.php index 57e4d8555412..de030f38ffb8 100644 --- a/seed/php-model/examples/src/Types/Response.php +++ b/seed/php-model/examples/src/Types/Response.php @@ -29,15 +29,16 @@ class Response extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->response = $values['response'];$this->identifiers = $values['identifiers']; + ) { + $this->response = $values['response']; + $this->identifiers = $values['identifiers']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/examples/src/Types/ResponseType.php b/seed/php-model/examples/src/Types/ResponseType.php index 1c08d556b4fb..19b721857eb5 100644 --- a/seed/php-model/examples/src/Types/ResponseType.php +++ b/seed/php-model/examples/src/Types/ResponseType.php @@ -28,15 +28,15 @@ class ResponseType extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->type = $values['type']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/examples/src/Types/StuntDouble.php b/seed/php-model/examples/src/Types/StuntDouble.php index d42464e86895..a62e4aabcfe8 100644 --- a/seed/php-model/examples/src/Types/StuntDouble.php +++ b/seed/php-model/examples/src/Types/StuntDouble.php @@ -27,15 +27,16 @@ class StuntDouble extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->actorOrActressId = $values['actorOrActressId']; + ) { + $this->name = $values['name']; + $this->actorOrActressId = $values['actorOrActressId']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/examples/src/Types/Test.php b/seed/php-model/examples/src/Types/Test.php index 0a66d045145e..0651ab5ad4c1 100644 --- a/seed/php-model/examples/src/Types/Test.php +++ b/seed/php-model/examples/src/Types/Test.php @@ -40,16 +40,17 @@ class Test extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param bool $and * @return Test */ - public static function and(bool $and): Test { + public static function and(bool $and): Test + { return new Test([ 'type' => 'and', 'value' => $and, @@ -60,7 +61,8 @@ public static function and(bool $and): Test { * @param bool $or * @return Test */ - public static function or(bool $or): Test { + public static function or(bool $or): Test + { return new Test([ 'type' => 'or', 'value' => $or, @@ -70,61 +72,67 @@ public static function or(bool $or): Test { /** * @return bool */ - public function isAnd_(): bool { - return is_bool($this->value)&& $this->type === 'and'; + public function isAnd_(): bool + { + return is_bool($this->value) && $this->type === 'and'; } /** * @return bool */ - public function asAnd_(): bool { - if (!(is_bool($this->value)&& $this->type === 'and')){ + public function asAnd_(): bool + { + if (!(is_bool($this->value) && $this->type === 'and')) { throw new Exception( "Expected and; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isOr_(): bool { - return is_bool($this->value)&& $this->type === 'or'; + public function isOr_(): bool + { + return is_bool($this->value) && $this->type === 'or'; } /** * @return bool */ - public function asOr_(): bool { - if (!(is_bool($this->value)&& $this->type === 'or')){ + public function asOr_(): bool + { + if (!(is_bool($this->value) && $this->type === 'or')) { throw new Exception( "Expected or; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'and': $value = $this->value; $result['and'] = $value; @@ -135,26 +143,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -163,38 +172,39 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'and': - if (!array_key_exists('and', $data)){ + if (!array_key_exists('and', $data)) { throw new Exception( "JSON data is missing property 'and'", ); } - + $args['value'] = $data['and']; break; case 'or': - if (!array_key_exists('or', $data)){ + if (!array_key_exists('or', $data)) { throw new Exception( "JSON data is missing property 'or'", ); } - + $args['value'] = $data['or']; break; case '_unknown': @@ -202,7 +212,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/examples/src/Types/Traits/Movie.php b/seed/php-model/examples/src/Types/Traits/Movie.php index 59d8a5590cd6..5193090f6b76 100644 --- a/seed/php-model/examples/src/Types/Traits/Movie.php +++ b/seed/php-model/examples/src/Types/Traits/Movie.php @@ -17,7 +17,7 @@ * @property array $metadata * @property int $revenue */ -trait Movie +trait Movie { /** * @var string $id diff --git a/seed/php-model/examples/src/Types/Tree.php b/seed/php-model/examples/src/Types/Tree.php index 8f56631ed813..7a7374066325 100644 --- a/seed/php-model/examples/src/Types/Tree.php +++ b/seed/php-model/examples/src/Types/Tree.php @@ -21,15 +21,15 @@ class Tree extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->nodes = $values['nodes'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/examples/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/examples/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/examples/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/examples/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/examples/tests/Core/Json/EnumTest.php b/seed/php-model/examples/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/examples/tests/Core/Json/EnumTest.php +++ b/seed/php-model/examples/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/examples/tests/Core/Json/TraitTest.php b/seed/php-model/examples/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/examples/tests/Core/Json/TraitTest.php +++ b/seed/php-model/examples/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/examples/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/examples/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/examples/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/examples/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/exhaustive/src/Core/Json/JsonEncoder.php b/seed/php-model/exhaustive/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/exhaustive/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/exhaustive/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/exhaustive/src/Core/Json/JsonProperty.php b/seed/php-model/exhaustive/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/exhaustive/src/Core/Json/JsonProperty.php +++ b/seed/php-model/exhaustive/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/exhaustive/src/Core/Types/ArrayType.php b/seed/php-model/exhaustive/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/exhaustive/src/Core/Types/ArrayType.php +++ b/seed/php-model/exhaustive/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/exhaustive/src/Core/Types/Constant.php b/seed/php-model/exhaustive/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/exhaustive/src/Core/Types/Constant.php +++ b/seed/php-model/exhaustive/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/exhaustive/src/Core/Types/Union.php b/seed/php-model/exhaustive/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/exhaustive/src/Core/Types/Union.php +++ b/seed/php-model/exhaustive/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/exhaustive/src/Endpoints/Put/Error.php b/seed/php-model/exhaustive/src/Endpoints/Put/Error.php index 1f2fff50bec0..b09152161d51 100644 --- a/seed/php-model/exhaustive/src/Endpoints/Put/Error.php +++ b/seed/php-model/exhaustive/src/Endpoints/Put/Error.php @@ -41,15 +41,18 @@ class Error extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->category = $values['category'];$this->code = $values['code'];$this->detail = $values['detail'] ?? null;$this->field = $values['field'] ?? null; + ) { + $this->category = $values['category']; + $this->code = $values['code']; + $this->detail = $values['detail'] ?? null; + $this->field = $values['field'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/exhaustive/src/Endpoints/Put/ErrorCategory.php b/seed/php-model/exhaustive/src/Endpoints/Put/ErrorCategory.php index 536a7225ef31..244a71a7f19d 100644 --- a/seed/php-model/exhaustive/src/Endpoints/Put/ErrorCategory.php +++ b/seed/php-model/exhaustive/src/Endpoints/Put/ErrorCategory.php @@ -2,8 +2,8 @@ namespace Seed\Endpoints\Put; -enum ErrorCategory - : string { +enum ErrorCategory: string +{ case ApiError = "API_ERROR"; case AuthenticationError = "AUTHENTICATION_ERROR"; case InvalidRequestError = "INVALID_REQUEST_ERROR"; diff --git a/seed/php-model/exhaustive/src/Endpoints/Put/ErrorCode.php b/seed/php-model/exhaustive/src/Endpoints/Put/ErrorCode.php index 9baf6894fe57..b9d841993efa 100644 --- a/seed/php-model/exhaustive/src/Endpoints/Put/ErrorCode.php +++ b/seed/php-model/exhaustive/src/Endpoints/Put/ErrorCode.php @@ -2,8 +2,8 @@ namespace Seed\Endpoints\Put; -enum ErrorCode - : string { +enum ErrorCode: string +{ case InternalServerError = "INTERNAL_SERVER_ERROR"; case Unauthorized = "UNAUTHORIZED"; case Forbidden = "FORBIDDEN"; diff --git a/seed/php-model/exhaustive/src/Endpoints/Put/PutResponse.php b/seed/php-model/exhaustive/src/Endpoints/Put/PutResponse.php index 474b67144f52..e9565378b122 100644 --- a/seed/php-model/exhaustive/src/Endpoints/Put/PutResponse.php +++ b/seed/php-model/exhaustive/src/Endpoints/Put/PutResponse.php @@ -21,15 +21,15 @@ class PutResponse extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->errors = $values['errors'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/exhaustive/src/GeneralErrors/BadObjectRequestInfo.php b/seed/php-model/exhaustive/src/GeneralErrors/BadObjectRequestInfo.php index ea8af6a3766f..53cbb9c39031 100644 --- a/seed/php-model/exhaustive/src/GeneralErrors/BadObjectRequestInfo.php +++ b/seed/php-model/exhaustive/src/GeneralErrors/BadObjectRequestInfo.php @@ -20,15 +20,15 @@ class BadObjectRequestInfo extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->message = $values['message']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/exhaustive/src/Types/Docs/ObjectWithDocs.php b/seed/php-model/exhaustive/src/Types/Docs/ObjectWithDocs.php index c53cd8c65cb0..9d2b5af217ab 100644 --- a/seed/php-model/exhaustive/src/Types/Docs/ObjectWithDocs.php +++ b/seed/php-model/exhaustive/src/Types/Docs/ObjectWithDocs.php @@ -9,11 +9,11 @@ class ObjectWithDocs extends JsonSerializableType { /** * Characters that could lead to broken generated SDKs: - * + * * Markdown Escapes: * - \_: Escaped underscore (e.g., FOO\_BAR) * - \*: Escaped asterisk - * + * * JSDoc (JavaScript/TypeScript): * - @: Used for JSDoc tags * - {: }: Used for type definitions @@ -22,7 +22,7 @@ class ObjectWithDocs extends JsonSerializableType * - /**: JSDoc comment start * - ** /: JSDoc comment end * - &: HTML entities - * + * * XMLDoc (C#): * - <: >: XML tags * - &: ': ": <: >: XML special characters @@ -30,7 +30,7 @@ class ObjectWithDocs extends JsonSerializableType * - ///: Comment marker * - /**: Block comment start * - ** /: Block comment end - * + * * Javadoc (Java): * - @: Used for Javadoc tags * - <: >: HTML tags @@ -38,7 +38,7 @@ class ObjectWithDocs extends JsonSerializableType * - *: Can interfere with comment blocks * - /**: Javadoc comment start * - ** /: Javadoc comment end - * + * * Doxygen (C++): * - \: Used for Doxygen commands * - @: Alternative command prefix @@ -46,7 +46,7 @@ class ObjectWithDocs extends JsonSerializableType * - &: HTML entities * - /**: C-style comment start * - ** /: C-style comment end - * + * * RDoc (Ruby): * - :: Used in symbol notation * - =: Section markers @@ -58,7 +58,7 @@ class ObjectWithDocs extends JsonSerializableType * - %: String literal delimiter * - #{: String interpolation start * - }: String interpolation end - * + * * PHPDoc (PHP): * - @: Used for PHPDoc tags * - {: }: Used for type definitions @@ -80,15 +80,15 @@ class ObjectWithDocs extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->string = $values['string']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/exhaustive/src/Types/Enum/WeatherReport.php b/seed/php-model/exhaustive/src/Types/Enum/WeatherReport.php index c30a20944cdf..de51fce1eed9 100644 --- a/seed/php-model/exhaustive/src/Types/Enum/WeatherReport.php +++ b/seed/php-model/exhaustive/src/Types/Enum/WeatherReport.php @@ -2,8 +2,8 @@ namespace Seed\Types\Enum; -enum WeatherReport - : string { +enum WeatherReport: string +{ case Sunny = "SUNNY"; case Cloudy = "CLOUDY"; case Raining = "RAINING"; diff --git a/seed/php-model/exhaustive/src/Types/Object/DoubleOptional.php b/seed/php-model/exhaustive/src/Types/Object/DoubleOptional.php index ea57dcb79fca..7b5ee9433a40 100644 --- a/seed/php-model/exhaustive/src/Types/Object/DoubleOptional.php +++ b/seed/php-model/exhaustive/src/Types/Object/DoubleOptional.php @@ -20,15 +20,15 @@ class DoubleOptional extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->optionalAlias = $values['optionalAlias'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/exhaustive/src/Types/Object/NestedObjectWithOptionalField.php b/seed/php-model/exhaustive/src/Types/Object/NestedObjectWithOptionalField.php index 0ebb5a26f6f9..eca4867abf9a 100644 --- a/seed/php-model/exhaustive/src/Types/Object/NestedObjectWithOptionalField.php +++ b/seed/php-model/exhaustive/src/Types/Object/NestedObjectWithOptionalField.php @@ -27,15 +27,16 @@ class NestedObjectWithOptionalField extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->string = $values['string'] ?? null;$this->nestedObject = $values['nestedObject'] ?? null; + ) { + $this->string = $values['string'] ?? null; + $this->nestedObject = $values['nestedObject'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/exhaustive/src/Types/Object/NestedObjectWithRequiredField.php b/seed/php-model/exhaustive/src/Types/Object/NestedObjectWithRequiredField.php index 0f6ecd652ac1..883212b27ab6 100644 --- a/seed/php-model/exhaustive/src/Types/Object/NestedObjectWithRequiredField.php +++ b/seed/php-model/exhaustive/src/Types/Object/NestedObjectWithRequiredField.php @@ -27,15 +27,16 @@ class NestedObjectWithRequiredField extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->string = $values['string'];$this->nestedObject = $values['nestedObject']; + ) { + $this->string = $values['string']; + $this->nestedObject = $values['nestedObject']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/exhaustive/src/Types/Object/ObjectWithMapOfMap.php b/seed/php-model/exhaustive/src/Types/Object/ObjectWithMapOfMap.php index 3bbf2de1e665..d68f157e9bb0 100644 --- a/seed/php-model/exhaustive/src/Types/Object/ObjectWithMapOfMap.php +++ b/seed/php-model/exhaustive/src/Types/Object/ObjectWithMapOfMap.php @@ -21,15 +21,15 @@ class ObjectWithMapOfMap extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->map = $values['map']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/exhaustive/src/Types/Object/ObjectWithOptionalField.php b/seed/php-model/exhaustive/src/Types/Object/ObjectWithOptionalField.php index b68962f13322..7de969752e8c 100644 --- a/seed/php-model/exhaustive/src/Types/Object/ObjectWithOptionalField.php +++ b/seed/php-model/exhaustive/src/Types/Object/ObjectWithOptionalField.php @@ -107,15 +107,27 @@ class ObjectWithOptionalField extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->string = $values['string'] ?? null;$this->integer = $values['integer'] ?? null;$this->long = $values['long'] ?? null;$this->double = $values['double'] ?? null;$this->bool = $values['bool'] ?? null;$this->datetime = $values['datetime'] ?? null;$this->date = $values['date'] ?? null;$this->uuid = $values['uuid'] ?? null;$this->base64 = $values['base64'] ?? null;$this->list = $values['list'] ?? null;$this->set = $values['set'] ?? null;$this->map = $values['map'] ?? null;$this->bigint = $values['bigint'] ?? null; + ) { + $this->string = $values['string'] ?? null; + $this->integer = $values['integer'] ?? null; + $this->long = $values['long'] ?? null; + $this->double = $values['double'] ?? null; + $this->bool = $values['bool'] ?? null; + $this->datetime = $values['datetime'] ?? null; + $this->date = $values['date'] ?? null; + $this->uuid = $values['uuid'] ?? null; + $this->base64 = $values['base64'] ?? null; + $this->list = $values['list'] ?? null; + $this->set = $values['set'] ?? null; + $this->map = $values['map'] ?? null; + $this->bigint = $values['bigint'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/exhaustive/src/Types/Object/ObjectWithRequiredField.php b/seed/php-model/exhaustive/src/Types/Object/ObjectWithRequiredField.php index 33efaf97be7c..db973df5a8a8 100644 --- a/seed/php-model/exhaustive/src/Types/Object/ObjectWithRequiredField.php +++ b/seed/php-model/exhaustive/src/Types/Object/ObjectWithRequiredField.php @@ -20,15 +20,15 @@ class ObjectWithRequiredField extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->string = $values['string']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/exhaustive/src/Types/Union/Animal.php b/seed/php-model/exhaustive/src/Types/Union/Animal.php index e4f655ab3d3d..eaf127325aa2 100644 --- a/seed/php-model/exhaustive/src/Types/Union/Animal.php +++ b/seed/php-model/exhaustive/src/Types/Union/Animal.php @@ -42,16 +42,17 @@ class Animal extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->animal = $values['animal'];$this->value = $values['value']; + ) { + $this->animal = $values['animal']; + $this->value = $values['value']; } /** * @param Dog $dog * @return Animal */ - public static function dog(Dog $dog): Animal { + public static function dog(Dog $dog): Animal + { return new Animal([ 'animal' => 'dog', 'value' => $dog, @@ -62,7 +63,8 @@ public static function dog(Dog $dog): Animal { * @param Cat $cat * @return Animal */ - public static function cat(Cat $cat): Animal { + public static function cat(Cat $cat): Animal + { return new Animal([ 'animal' => 'cat', 'value' => $cat, @@ -72,61 +74,67 @@ public static function cat(Cat $cat): Animal { /** * @return bool */ - public function isDog(): bool { - return $this->value instanceof Dog&& $this->animal === 'dog'; + public function isDog(): bool + { + return $this->value instanceof Dog && $this->animal === 'dog'; } /** * @return Dog */ - public function asDog(): Dog { - if (!($this->value instanceof Dog&& $this->animal === 'dog')){ + public function asDog(): Dog + { + if (!($this->value instanceof Dog && $this->animal === 'dog')) { throw new Exception( "Expected dog; got " . $this->animal . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isCat(): bool { - return $this->value instanceof Cat&& $this->animal === 'cat'; + public function isCat(): bool + { + return $this->value instanceof Cat && $this->animal === 'cat'; } /** * @return Cat */ - public function asCat(): Cat { - if (!($this->value instanceof Cat&& $this->animal === 'cat')){ + public function asCat(): Cat + { + if (!($this->value instanceof Cat && $this->animal === 'cat')) { throw new Exception( "Expected cat; got " . $this->animal . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['animal'] = $this->animal; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->animal){ + + switch ($this->animal) { case 'dog': $value = $this->asDog()->jsonSerialize(); $result = array_merge($value, $result); @@ -137,26 +145,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -165,22 +174,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('animal', $data)){ + if (!array_key_exists('animal', $data)) { throw new Exception( "JSON data is missing property 'animal'", ); } $animal = $data['animal']; - if (!(is_string($animal))){ + if (!(is_string($animal))) { throw new Exception( "Expected property 'animal' in JSON data to be string, instead received " . get_debug_type($data['animal']), ); } - + $args['animal'] = $animal; - switch ($animal){ + switch ($animal) { case 'dog': $args['value'] = Dog::jsonDeserialize($data); break; @@ -192,7 +202,7 @@ public static function jsonDeserialize(array $data): static { $args['animal'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/exhaustive/src/Types/Union/Cat.php b/seed/php-model/exhaustive/src/Types/Union/Cat.php index d8acc1070f66..c42295a5fdf9 100644 --- a/seed/php-model/exhaustive/src/Types/Union/Cat.php +++ b/seed/php-model/exhaustive/src/Types/Union/Cat.php @@ -27,15 +27,16 @@ class Cat extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->likesToMeow = $values['likesToMeow']; + ) { + $this->name = $values['name']; + $this->likesToMeow = $values['likesToMeow']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/exhaustive/src/Types/Union/Dog.php b/seed/php-model/exhaustive/src/Types/Union/Dog.php index 1d5a3a124e63..3fbdc68cd091 100644 --- a/seed/php-model/exhaustive/src/Types/Union/Dog.php +++ b/seed/php-model/exhaustive/src/Types/Union/Dog.php @@ -27,15 +27,16 @@ class Dog extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->likesToWoof = $values['likesToWoof']; + ) { + $this->name = $values['name']; + $this->likesToWoof = $values['likesToWoof']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/exhaustive/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/exhaustive/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/exhaustive/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/exhaustive/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/exhaustive/tests/Core/Json/EnumTest.php b/seed/php-model/exhaustive/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/exhaustive/tests/Core/Json/EnumTest.php +++ b/seed/php-model/exhaustive/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/exhaustive/tests/Core/Json/TraitTest.php b/seed/php-model/exhaustive/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/exhaustive/tests/Core/Json/TraitTest.php +++ b/seed/php-model/exhaustive/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/exhaustive/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/exhaustive/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/exhaustive/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/exhaustive/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/extends/src/Core/Json/JsonEncoder.php b/seed/php-model/extends/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/extends/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/extends/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/extends/src/Core/Json/JsonProperty.php b/seed/php-model/extends/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/extends/src/Core/Json/JsonProperty.php +++ b/seed/php-model/extends/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/extends/src/Core/Types/ArrayType.php b/seed/php-model/extends/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/extends/src/Core/Types/ArrayType.php +++ b/seed/php-model/extends/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/extends/src/Core/Types/Constant.php b/seed/php-model/extends/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/extends/src/Core/Types/Constant.php +++ b/seed/php-model/extends/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/extends/src/Core/Types/Union.php b/seed/php-model/extends/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/extends/src/Core/Types/Union.php +++ b/seed/php-model/extends/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/extends/src/Docs.php b/seed/php-model/extends/src/Docs.php index affdbbf7c148..ababd48de409 100644 --- a/seed/php-model/extends/src/Docs.php +++ b/seed/php-model/extends/src/Docs.php @@ -20,15 +20,15 @@ class Docs extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->docs = $values['docs']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/extends/src/ExampleType.php b/seed/php-model/extends/src/ExampleType.php index ef43668f71fe..94d089c530a4 100644 --- a/seed/php-model/extends/src/ExampleType.php +++ b/seed/php-model/extends/src/ExampleType.php @@ -24,15 +24,16 @@ class ExampleType extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->docs = $values['docs'];$this->name = $values['name']; + ) { + $this->docs = $values['docs']; + $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/extends/src/Json.php b/seed/php-model/extends/src/Json.php index 5e9f560e3ffb..2af2f9aeb048 100644 --- a/seed/php-model/extends/src/Json.php +++ b/seed/php-model/extends/src/Json.php @@ -24,15 +24,16 @@ class Json extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->docs = $values['docs'];$this->raw = $values['raw']; + ) { + $this->docs = $values['docs']; + $this->raw = $values['raw']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/extends/src/NestedType.php b/seed/php-model/extends/src/NestedType.php index 799c4b5c7156..b70be20d6c77 100644 --- a/seed/php-model/extends/src/NestedType.php +++ b/seed/php-model/extends/src/NestedType.php @@ -25,15 +25,17 @@ class NestedType extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->raw = $values['raw'];$this->docs = $values['docs'];$this->name = $values['name']; + ) { + $this->raw = $values['raw']; + $this->docs = $values['docs']; + $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/extends/src/Traits/Docs.php b/seed/php-model/extends/src/Traits/Docs.php index 31549c3f7ad9..6e49e98814be 100644 --- a/seed/php-model/extends/src/Traits/Docs.php +++ b/seed/php-model/extends/src/Traits/Docs.php @@ -7,7 +7,7 @@ /** * @property string $docs */ -trait Docs +trait Docs { /** * @var string $docs diff --git a/seed/php-model/extends/src/Traits/ExampleType.php b/seed/php-model/extends/src/Traits/ExampleType.php index bbccad420453..503829fdc3be 100644 --- a/seed/php-model/extends/src/Traits/ExampleType.php +++ b/seed/php-model/extends/src/Traits/ExampleType.php @@ -7,7 +7,7 @@ /** * @property string $name */ -trait ExampleType +trait ExampleType { use Docs; diff --git a/seed/php-model/extends/src/Traits/Json.php b/seed/php-model/extends/src/Traits/Json.php index 5c287d4cff59..4380468c2af0 100644 --- a/seed/php-model/extends/src/Traits/Json.php +++ b/seed/php-model/extends/src/Traits/Json.php @@ -7,7 +7,7 @@ /** * @property string $raw */ -trait Json +trait Json { use Docs; diff --git a/seed/php-model/extends/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/extends/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/extends/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/extends/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/extends/tests/Core/Json/EnumTest.php b/seed/php-model/extends/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/extends/tests/Core/Json/EnumTest.php +++ b/seed/php-model/extends/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/extends/tests/Core/Json/TraitTest.php b/seed/php-model/extends/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/extends/tests/Core/Json/TraitTest.php +++ b/seed/php-model/extends/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/extends/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/extends/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/extends/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/extends/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/extra-properties/src/Core/Json/JsonEncoder.php b/seed/php-model/extra-properties/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/extra-properties/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/extra-properties/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/extra-properties/src/Core/Json/JsonProperty.php b/seed/php-model/extra-properties/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/extra-properties/src/Core/Json/JsonProperty.php +++ b/seed/php-model/extra-properties/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/extra-properties/src/Core/Types/ArrayType.php b/seed/php-model/extra-properties/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/extra-properties/src/Core/Types/ArrayType.php +++ b/seed/php-model/extra-properties/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/extra-properties/src/Core/Types/Constant.php b/seed/php-model/extra-properties/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/extra-properties/src/Core/Types/Constant.php +++ b/seed/php-model/extra-properties/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/extra-properties/src/Core/Types/Union.php b/seed/php-model/extra-properties/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/extra-properties/src/Core/Types/Union.php +++ b/seed/php-model/extra-properties/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/extra-properties/src/Failure.php b/seed/php-model/extra-properties/src/Failure.php index 3c03d41ac334..eeabedb02d93 100644 --- a/seed/php-model/extra-properties/src/Failure.php +++ b/seed/php-model/extra-properties/src/Failure.php @@ -20,15 +20,15 @@ class Failure extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->status = $values['status']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/extra-properties/src/User/User.php b/seed/php-model/extra-properties/src/User/User.php index ae30d777212c..e8fe52ec8610 100644 --- a/seed/php-model/extra-properties/src/User/User.php +++ b/seed/php-model/extra-properties/src/User/User.php @@ -20,15 +20,15 @@ class User extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/extra-properties/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/extra-properties/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/extra-properties/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/extra-properties/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/extra-properties/tests/Core/Json/EnumTest.php b/seed/php-model/extra-properties/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/extra-properties/tests/Core/Json/EnumTest.php +++ b/seed/php-model/extra-properties/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/extra-properties/tests/Core/Json/TraitTest.php b/seed/php-model/extra-properties/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/extra-properties/tests/Core/Json/TraitTest.php +++ b/seed/php-model/extra-properties/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/extra-properties/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/extra-properties/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/extra-properties/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/extra-properties/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/file-download/src/Core/Json/JsonEncoder.php b/seed/php-model/file-download/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/file-download/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/file-download/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/file-download/src/Core/Json/JsonProperty.php b/seed/php-model/file-download/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/file-download/src/Core/Json/JsonProperty.php +++ b/seed/php-model/file-download/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/file-download/src/Core/Types/ArrayType.php b/seed/php-model/file-download/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/file-download/src/Core/Types/ArrayType.php +++ b/seed/php-model/file-download/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/file-download/src/Core/Types/Constant.php b/seed/php-model/file-download/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/file-download/src/Core/Types/Constant.php +++ b/seed/php-model/file-download/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/file-download/src/Core/Types/Union.php b/seed/php-model/file-download/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/file-download/src/Core/Types/Union.php +++ b/seed/php-model/file-download/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/file-download/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/file-download/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/file-download/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/file-download/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/file-download/tests/Core/Json/EnumTest.php b/seed/php-model/file-download/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/file-download/tests/Core/Json/EnumTest.php +++ b/seed/php-model/file-download/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/file-download/tests/Core/Json/TraitTest.php b/seed/php-model/file-download/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/file-download/tests/Core/Json/TraitTest.php +++ b/seed/php-model/file-download/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/file-download/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/file-download/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/file-download/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/file-download/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/file-upload-openapi/src/Core/Json/JsonEncoder.php b/seed/php-model/file-upload-openapi/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/file-upload-openapi/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/file-upload-openapi/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/file-upload-openapi/src/Core/Json/JsonProperty.php b/seed/php-model/file-upload-openapi/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/file-upload-openapi/src/Core/Json/JsonProperty.php +++ b/seed/php-model/file-upload-openapi/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/file-upload-openapi/src/Core/Types/ArrayType.php b/seed/php-model/file-upload-openapi/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/file-upload-openapi/src/Core/Types/ArrayType.php +++ b/seed/php-model/file-upload-openapi/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/file-upload-openapi/src/Core/Types/Constant.php b/seed/php-model/file-upload-openapi/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/file-upload-openapi/src/Core/Types/Constant.php +++ b/seed/php-model/file-upload-openapi/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/file-upload-openapi/src/Core/Types/Union.php b/seed/php-model/file-upload-openapi/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/file-upload-openapi/src/Core/Types/Union.php +++ b/seed/php-model/file-upload-openapi/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/file-upload-openapi/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/file-upload-openapi/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/file-upload-openapi/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/file-upload-openapi/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/file-upload-openapi/tests/Core/Json/EnumTest.php b/seed/php-model/file-upload-openapi/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/file-upload-openapi/tests/Core/Json/EnumTest.php +++ b/seed/php-model/file-upload-openapi/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/file-upload-openapi/tests/Core/Json/TraitTest.php b/seed/php-model/file-upload-openapi/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/file-upload-openapi/tests/Core/Json/TraitTest.php +++ b/seed/php-model/file-upload-openapi/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/file-upload-openapi/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/file-upload-openapi/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/file-upload-openapi/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/file-upload-openapi/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/file-upload/src/Core/Json/JsonEncoder.php b/seed/php-model/file-upload/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/file-upload/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/file-upload/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/file-upload/src/Core/Json/JsonProperty.php b/seed/php-model/file-upload/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/file-upload/src/Core/Json/JsonProperty.php +++ b/seed/php-model/file-upload/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/file-upload/src/Core/Types/ArrayType.php b/seed/php-model/file-upload/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/file-upload/src/Core/Types/ArrayType.php +++ b/seed/php-model/file-upload/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/file-upload/src/Core/Types/Constant.php b/seed/php-model/file-upload/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/file-upload/src/Core/Types/Constant.php +++ b/seed/php-model/file-upload/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/file-upload/src/Core/Types/Union.php b/seed/php-model/file-upload/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/file-upload/src/Core/Types/Union.php +++ b/seed/php-model/file-upload/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/file-upload/src/Service/MyInlineType.php b/seed/php-model/file-upload/src/Service/MyInlineType.php index d42bc7bae3f7..5ead5ca6c8cb 100644 --- a/seed/php-model/file-upload/src/Service/MyInlineType.php +++ b/seed/php-model/file-upload/src/Service/MyInlineType.php @@ -20,15 +20,15 @@ class MyInlineType extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->bar = $values['bar']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/file-upload/src/Service/MyObject.php b/seed/php-model/file-upload/src/Service/MyObject.php index c46c57365258..9bb38d92df4c 100644 --- a/seed/php-model/file-upload/src/Service/MyObject.php +++ b/seed/php-model/file-upload/src/Service/MyObject.php @@ -20,15 +20,15 @@ class MyObject extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->foo = $values['foo']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/file-upload/src/Service/MyObjectWithOptional.php b/seed/php-model/file-upload/src/Service/MyObjectWithOptional.php index efc28701e88a..cb93129acbea 100644 --- a/seed/php-model/file-upload/src/Service/MyObjectWithOptional.php +++ b/seed/php-model/file-upload/src/Service/MyObjectWithOptional.php @@ -27,15 +27,16 @@ class MyObjectWithOptional extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->prop = $values['prop'];$this->optionalProp = $values['optionalProp'] ?? null; + ) { + $this->prop = $values['prop']; + $this->optionalProp = $values['optionalProp'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/file-upload/src/Service/ObjectType.php b/seed/php-model/file-upload/src/Service/ObjectType.php index 96370fe1ef05..562d9570d7f1 100644 --- a/seed/php-model/file-upload/src/Service/ObjectType.php +++ b/seed/php-model/file-upload/src/Service/ObjectType.php @@ -2,8 +2,8 @@ namespace Seed\Service; -enum ObjectType - : string { +enum ObjectType: string +{ case Foo = "FOO"; case Bar = "BAR"; } diff --git a/seed/php-model/file-upload/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/file-upload/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/file-upload/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/file-upload/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/file-upload/tests/Core/Json/EnumTest.php b/seed/php-model/file-upload/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/file-upload/tests/Core/Json/EnumTest.php +++ b/seed/php-model/file-upload/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/file-upload/tests/Core/Json/TraitTest.php b/seed/php-model/file-upload/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/file-upload/tests/Core/Json/TraitTest.php +++ b/seed/php-model/file-upload/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/file-upload/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/file-upload/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/file-upload/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/file-upload/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/folders/src/Core/Json/JsonEncoder.php b/seed/php-model/folders/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/folders/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/folders/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/folders/src/Core/Json/JsonProperty.php b/seed/php-model/folders/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/folders/src/Core/Json/JsonProperty.php +++ b/seed/php-model/folders/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/folders/src/Core/Types/ArrayType.php b/seed/php-model/folders/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/folders/src/Core/Types/ArrayType.php +++ b/seed/php-model/folders/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/folders/src/Core/Types/Constant.php b/seed/php-model/folders/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/folders/src/Core/Types/Constant.php +++ b/seed/php-model/folders/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/folders/src/Core/Types/Union.php b/seed/php-model/folders/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/folders/src/Core/Types/Union.php +++ b/seed/php-model/folders/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/folders/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/folders/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/folders/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/folders/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/folders/tests/Core/Json/EnumTest.php b/seed/php-model/folders/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/folders/tests/Core/Json/EnumTest.php +++ b/seed/php-model/folders/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/folders/tests/Core/Json/TraitTest.php b/seed/php-model/folders/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/folders/tests/Core/Json/TraitTest.php +++ b/seed/php-model/folders/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/folders/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/folders/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/folders/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/folders/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/header-auth-environment-variable/src/Core/Json/JsonEncoder.php b/seed/php-model/header-auth-environment-variable/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/header-auth-environment-variable/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/header-auth-environment-variable/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/header-auth-environment-variable/src/Core/Json/JsonProperty.php b/seed/php-model/header-auth-environment-variable/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/header-auth-environment-variable/src/Core/Json/JsonProperty.php +++ b/seed/php-model/header-auth-environment-variable/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/header-auth-environment-variable/src/Core/Types/ArrayType.php b/seed/php-model/header-auth-environment-variable/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/header-auth-environment-variable/src/Core/Types/ArrayType.php +++ b/seed/php-model/header-auth-environment-variable/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/header-auth-environment-variable/src/Core/Types/Constant.php b/seed/php-model/header-auth-environment-variable/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/header-auth-environment-variable/src/Core/Types/Constant.php +++ b/seed/php-model/header-auth-environment-variable/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/header-auth-environment-variable/src/Core/Types/Union.php b/seed/php-model/header-auth-environment-variable/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/header-auth-environment-variable/src/Core/Types/Union.php +++ b/seed/php-model/header-auth-environment-variable/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/header-auth-environment-variable/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/header-auth-environment-variable/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/header-auth-environment-variable/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/header-auth-environment-variable/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/header-auth-environment-variable/tests/Core/Json/EnumTest.php b/seed/php-model/header-auth-environment-variable/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/header-auth-environment-variable/tests/Core/Json/EnumTest.php +++ b/seed/php-model/header-auth-environment-variable/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/header-auth-environment-variable/tests/Core/Json/TraitTest.php b/seed/php-model/header-auth-environment-variable/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/header-auth-environment-variable/tests/Core/Json/TraitTest.php +++ b/seed/php-model/header-auth-environment-variable/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/header-auth-environment-variable/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/header-auth-environment-variable/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/header-auth-environment-variable/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/header-auth-environment-variable/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/header-auth/src/Core/Json/JsonEncoder.php b/seed/php-model/header-auth/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/header-auth/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/header-auth/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/header-auth/src/Core/Json/JsonProperty.php b/seed/php-model/header-auth/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/header-auth/src/Core/Json/JsonProperty.php +++ b/seed/php-model/header-auth/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/header-auth/src/Core/Types/ArrayType.php b/seed/php-model/header-auth/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/header-auth/src/Core/Types/ArrayType.php +++ b/seed/php-model/header-auth/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/header-auth/src/Core/Types/Constant.php b/seed/php-model/header-auth/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/header-auth/src/Core/Types/Constant.php +++ b/seed/php-model/header-auth/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/header-auth/src/Core/Types/Union.php b/seed/php-model/header-auth/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/header-auth/src/Core/Types/Union.php +++ b/seed/php-model/header-auth/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/header-auth/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/header-auth/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/header-auth/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/header-auth/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/header-auth/tests/Core/Json/EnumTest.php b/seed/php-model/header-auth/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/header-auth/tests/Core/Json/EnumTest.php +++ b/seed/php-model/header-auth/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/header-auth/tests/Core/Json/TraitTest.php b/seed/php-model/header-auth/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/header-auth/tests/Core/Json/TraitTest.php +++ b/seed/php-model/header-auth/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/header-auth/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/header-auth/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/header-auth/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/header-auth/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/http-head/src/Core/Json/JsonEncoder.php b/seed/php-model/http-head/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/http-head/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/http-head/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/http-head/src/Core/Json/JsonProperty.php b/seed/php-model/http-head/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/http-head/src/Core/Json/JsonProperty.php +++ b/seed/php-model/http-head/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/http-head/src/Core/Types/ArrayType.php b/seed/php-model/http-head/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/http-head/src/Core/Types/ArrayType.php +++ b/seed/php-model/http-head/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/http-head/src/Core/Types/Constant.php b/seed/php-model/http-head/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/http-head/src/Core/Types/Constant.php +++ b/seed/php-model/http-head/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/http-head/src/Core/Types/Union.php b/seed/php-model/http-head/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/http-head/src/Core/Types/Union.php +++ b/seed/php-model/http-head/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/http-head/src/User/User.php b/seed/php-model/http-head/src/User/User.php index 876319d611e3..6e276ede3a98 100644 --- a/seed/php-model/http-head/src/User/User.php +++ b/seed/php-model/http-head/src/User/User.php @@ -28,15 +28,16 @@ class User extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->tags = $values['tags']; + ) { + $this->name = $values['name']; + $this->tags = $values['tags']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/http-head/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/http-head/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/http-head/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/http-head/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/http-head/tests/Core/Json/EnumTest.php b/seed/php-model/http-head/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/http-head/tests/Core/Json/EnumTest.php +++ b/seed/php-model/http-head/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/http-head/tests/Core/Json/TraitTest.php b/seed/php-model/http-head/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/http-head/tests/Core/Json/TraitTest.php +++ b/seed/php-model/http-head/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/http-head/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/http-head/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/http-head/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/http-head/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/idempotency-headers/src/Core/Json/JsonEncoder.php b/seed/php-model/idempotency-headers/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/idempotency-headers/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/idempotency-headers/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/idempotency-headers/src/Core/Json/JsonProperty.php b/seed/php-model/idempotency-headers/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/idempotency-headers/src/Core/Json/JsonProperty.php +++ b/seed/php-model/idempotency-headers/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/idempotency-headers/src/Core/Types/ArrayType.php b/seed/php-model/idempotency-headers/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/idempotency-headers/src/Core/Types/ArrayType.php +++ b/seed/php-model/idempotency-headers/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/idempotency-headers/src/Core/Types/Constant.php b/seed/php-model/idempotency-headers/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/idempotency-headers/src/Core/Types/Constant.php +++ b/seed/php-model/idempotency-headers/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/idempotency-headers/src/Core/Types/Union.php b/seed/php-model/idempotency-headers/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/idempotency-headers/src/Core/Types/Union.php +++ b/seed/php-model/idempotency-headers/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/idempotency-headers/src/Payment/Currency.php b/seed/php-model/idempotency-headers/src/Payment/Currency.php index 701b10f120c6..efdf732c496e 100644 --- a/seed/php-model/idempotency-headers/src/Payment/Currency.php +++ b/seed/php-model/idempotency-headers/src/Payment/Currency.php @@ -2,8 +2,8 @@ namespace Seed\Payment; -enum Currency - : string { +enum Currency: string +{ case Usd = "USD"; case Yen = "YEN"; } diff --git a/seed/php-model/idempotency-headers/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/idempotency-headers/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/idempotency-headers/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/idempotency-headers/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/idempotency-headers/tests/Core/Json/EnumTest.php b/seed/php-model/idempotency-headers/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/idempotency-headers/tests/Core/Json/EnumTest.php +++ b/seed/php-model/idempotency-headers/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/idempotency-headers/tests/Core/Json/TraitTest.php b/seed/php-model/idempotency-headers/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/idempotency-headers/tests/Core/Json/TraitTest.php +++ b/seed/php-model/idempotency-headers/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/idempotency-headers/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/idempotency-headers/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/idempotency-headers/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/idempotency-headers/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/imdb/src/Core/Json/JsonEncoder.php b/seed/php-model/imdb/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/imdb/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/imdb/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/imdb/src/Core/Json/JsonProperty.php b/seed/php-model/imdb/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/imdb/src/Core/Json/JsonProperty.php +++ b/seed/php-model/imdb/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/imdb/src/Core/Types/ArrayType.php b/seed/php-model/imdb/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/imdb/src/Core/Types/ArrayType.php +++ b/seed/php-model/imdb/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/imdb/src/Core/Types/Constant.php b/seed/php-model/imdb/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/imdb/src/Core/Types/Constant.php +++ b/seed/php-model/imdb/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/imdb/src/Core/Types/Union.php b/seed/php-model/imdb/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/imdb/src/Core/Types/Union.php +++ b/seed/php-model/imdb/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/imdb/src/Imdb/CreateMovieRequest.php b/seed/php-model/imdb/src/Imdb/CreateMovieRequest.php index d6e1d137aa59..7add86f4a2fe 100644 --- a/seed/php-model/imdb/src/Imdb/CreateMovieRequest.php +++ b/seed/php-model/imdb/src/Imdb/CreateMovieRequest.php @@ -27,15 +27,16 @@ class CreateMovieRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->title = $values['title'];$this->rating = $values['rating']; + ) { + $this->title = $values['title']; + $this->rating = $values['rating']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/imdb/src/Imdb/Movie.php b/seed/php-model/imdb/src/Imdb/Movie.php index 86b4da8b2931..d3b89988ecf3 100644 --- a/seed/php-model/imdb/src/Imdb/Movie.php +++ b/seed/php-model/imdb/src/Imdb/Movie.php @@ -34,15 +34,17 @@ class Movie extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->title = $values['title'];$this->rating = $values['rating']; + ) { + $this->id = $values['id']; + $this->title = $values['title']; + $this->rating = $values['rating']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/imdb/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/imdb/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/imdb/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/imdb/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/imdb/tests/Core/Json/EnumTest.php b/seed/php-model/imdb/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/imdb/tests/Core/Json/EnumTest.php +++ b/seed/php-model/imdb/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/imdb/tests/Core/Json/TraitTest.php b/seed/php-model/imdb/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/imdb/tests/Core/Json/TraitTest.php +++ b/seed/php-model/imdb/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/imdb/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/imdb/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/imdb/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/imdb/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/inferred-auth-explicit/src/Auth/TokenResponse.php b/seed/php-model/inferred-auth-explicit/src/Auth/TokenResponse.php index 56fbe31ea0c2..9222a65c62ca 100644 --- a/seed/php-model/inferred-auth-explicit/src/Auth/TokenResponse.php +++ b/seed/php-model/inferred-auth-explicit/src/Auth/TokenResponse.php @@ -37,15 +37,17 @@ class TokenResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->accessToken = $values['accessToken'];$this->expiresIn = $values['expiresIn'];$this->refreshToken = $values['refreshToken'] ?? null; + ) { + $this->accessToken = $values['accessToken']; + $this->expiresIn = $values['expiresIn']; + $this->refreshToken = $values['refreshToken'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/inferred-auth-explicit/src/Core/Json/JsonEncoder.php b/seed/php-model/inferred-auth-explicit/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/inferred-auth-explicit/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/inferred-auth-explicit/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/inferred-auth-explicit/src/Core/Json/JsonProperty.php b/seed/php-model/inferred-auth-explicit/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/inferred-auth-explicit/src/Core/Json/JsonProperty.php +++ b/seed/php-model/inferred-auth-explicit/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/inferred-auth-explicit/src/Core/Types/ArrayType.php b/seed/php-model/inferred-auth-explicit/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/inferred-auth-explicit/src/Core/Types/ArrayType.php +++ b/seed/php-model/inferred-auth-explicit/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/inferred-auth-explicit/src/Core/Types/Constant.php b/seed/php-model/inferred-auth-explicit/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/inferred-auth-explicit/src/Core/Types/Constant.php +++ b/seed/php-model/inferred-auth-explicit/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/inferred-auth-explicit/src/Core/Types/Union.php b/seed/php-model/inferred-auth-explicit/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/inferred-auth-explicit/src/Core/Types/Union.php +++ b/seed/php-model/inferred-auth-explicit/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/inferred-auth-explicit/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/inferred-auth-explicit/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/inferred-auth-explicit/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/inferred-auth-explicit/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/inferred-auth-explicit/tests/Core/Json/EnumTest.php b/seed/php-model/inferred-auth-explicit/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/inferred-auth-explicit/tests/Core/Json/EnumTest.php +++ b/seed/php-model/inferred-auth-explicit/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/inferred-auth-explicit/tests/Core/Json/TraitTest.php b/seed/php-model/inferred-auth-explicit/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/inferred-auth-explicit/tests/Core/Json/TraitTest.php +++ b/seed/php-model/inferred-auth-explicit/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/inferred-auth-explicit/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/inferred-auth-explicit/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/inferred-auth-explicit/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/inferred-auth-explicit/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/inferred-auth-implicit-api-key/.fern/metadata.json b/seed/php-model/inferred-auth-implicit-api-key/.fern/metadata.json new file mode 100644 index 000000000000..90046b58049c --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-api-key/.fern/metadata.json @@ -0,0 +1,5 @@ +{ + "cliVersion": "DUMMY", + "generatorName": "fernapi/fern-php-model", + "generatorVersion": "latest" +} \ No newline at end of file diff --git a/seed/php-model/inferred-auth-implicit-api-key/.github/workflows/ci.yml b/seed/php-model/inferred-auth-implicit-api-key/.github/workflows/ci.yml new file mode 100644 index 000000000000..ba5836ae6967 --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-api-key/.github/workflows/ci.yml @@ -0,0 +1,48 @@ +name: ci + +on: [push] + +jobs: + compile: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "8.1" + + - name: Install tools + run: | + composer install + + - name: Build + run: | + composer build + + - name: Analyze + run: | + composer analyze + + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "8.1" + + - name: Install tools + run: | + composer install + + - name: Run Tests + run: | + composer test diff --git a/seed/php-model/inferred-auth-implicit-api-key/.gitignore b/seed/php-model/inferred-auth-implicit-api-key/.gitignore new file mode 100644 index 000000000000..31a1aeb14f35 --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-api-key/.gitignore @@ -0,0 +1,5 @@ +.idea +.php-cs-fixer.cache +.phpunit.result.cache +composer.lock +vendor/ \ No newline at end of file diff --git a/seed/php-model/inferred-auth-implicit-api-key/composer.json b/seed/php-model/inferred-auth-implicit-api-key/composer.json new file mode 100644 index 000000000000..c6a5d9f87477 --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-api-key/composer.json @@ -0,0 +1,39 @@ +{ + "name": "seed/seed", + "version": "0.0.1", + "description": "Seed PHP Library", + "keywords": [ + "seed", + "api", + "sdk" + ], + "license": [], + "require": { + "php": "^8.1", + "ext-json": "*", + "guzzlehttp/guzzle": "^7.4" + }, + "require-dev": { + "phpunit/phpunit": "^9.0", + "friendsofphp/php-cs-fixer": "3.5.0", + "phpstan/phpstan": "^1.12" + }, + "autoload": { + "psr-4": { + "Seed\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Seed\\Tests\\": "tests/" + } + }, + "scripts": { + "build": [ + "@php -l src", + "@php -l tests" + ], + "test": "phpunit", + "analyze": "phpstan analyze src tests --memory-limit=1G" + } +} \ No newline at end of file diff --git a/seed/php-model/inferred-auth-implicit-api-key/phpstan.neon b/seed/php-model/inferred-auth-implicit-api-key/phpstan.neon new file mode 100644 index 000000000000..780706b8f8a2 --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-api-key/phpstan.neon @@ -0,0 +1,6 @@ +parameters: + level: max + reportUnmatchedIgnoredErrors: false + paths: + - src + - tests \ No newline at end of file diff --git a/seed/php-model/inferred-auth-implicit-api-key/phpunit.xml b/seed/php-model/inferred-auth-implicit-api-key/phpunit.xml new file mode 100644 index 000000000000..54630a51163c --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-api-key/phpunit.xml @@ -0,0 +1,7 @@ + + + + tests + + + \ No newline at end of file diff --git a/seed/php-model/inferred-auth-implicit-api-key/snippet.json b/seed/php-model/inferred-auth-implicit-api-key/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/php-model/inferred-auth-implicit-api-key/src/Auth/TokenResponse.php b/seed/php-model/inferred-auth-implicit-api-key/src/Auth/TokenResponse.php new file mode 100644 index 000000000000..178d6a61c188 --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-api-key/src/Auth/TokenResponse.php @@ -0,0 +1,61 @@ +accessToken = $values['accessToken']; + $this->tokenType = $values['tokenType']; + $this->expiresIn = $values['expiresIn']; + $this->scope = $values['scope'] ?? null; + } + + /** + * @return string + */ + public function __toString(): string + { + return $this->toJson(); + } +} diff --git a/seed/php-model/inferred-auth-implicit-api-key/src/Core/Json/JsonDecoder.php b/seed/php-model/inferred-auth-implicit-api-key/src/Core/Json/JsonDecoder.php new file mode 100644 index 000000000000..2ddff0273482 --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-api-key/src/Core/Json/JsonDecoder.php @@ -0,0 +1,161 @@ + $type The type definition for deserialization. + * @return mixed[]|array The deserialized array. + * @throws JsonException If the decoded value is not an array. + */ + public static function decodeArray(string $json, array $type): array + { + $decoded = self::decode($json); + if (!is_array($decoded)) { + throw new JsonException("Unexpected non-array json value: " . $json); + } + return JsonDeserializer::deserializeArray($decoded, $type); + } + + /** + * Decodes a JSON string and deserializes it based on the provided union type definition. + * + * @param string $json The JSON string to decode. + * @param Union $union The union type definition for deserialization. + * @return mixed The deserialized value. + * @throws JsonException If the deserialization for all types in the union fails. + */ + public static function decodeUnion(string $json, Union $union): mixed + { + $decoded = self::decode($json); + return JsonDeserializer::deserializeUnion($decoded, $union); + } + /** + * Decodes a JSON string and returns a mixed. + * + * @param string $json The JSON string to decode. + * @return mixed The decoded mixed. + * @throws JsonException If the decoded value is not an mixed. + */ + public static function decodeMixed(string $json): mixed + { + return self::decode($json); + } + + /** + * Decodes a JSON string into a PHP value. + * + * @param string $json The JSON string to decode. + * @return mixed The decoded value. + * @throws JsonException If an error occurs during JSON decoding. + */ + public static function decode(string $json): mixed + { + return json_decode($json, associative: true, flags: JSON_THROW_ON_ERROR); + } +} diff --git a/seed/php-model/inferred-auth-implicit-api-key/src/Core/Json/JsonDeserializer.php b/seed/php-model/inferred-auth-implicit-api-key/src/Core/Json/JsonDeserializer.php new file mode 100644 index 000000000000..ab6a3f826238 --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-api-key/src/Core/Json/JsonDeserializer.php @@ -0,0 +1,206 @@ + $data The array to be deserialized. + * @param mixed[]|array $type The type definition from the annotation. + * @return mixed[]|array The deserialized array. + * @throws JsonException If deserialization fails. + */ + public static function deserializeArray(array $data, array $type): array + { + return Utils::isMapType($type) + ? self::deserializeMap($data, $type) + : self::deserializeList($data, $type); + } + + /** + * Deserializes a value based on its type definition. + * + * @param mixed $data The data to deserialize. + * @param mixed $type The type definition. + * @return mixed The deserialized value. + * @throws JsonException If deserialization fails. + */ + private static function deserializeValue(mixed $data, mixed $type): mixed + { + if ($type instanceof Union) { + return self::deserializeUnion($data, $type); + } + + if (is_array($type)) { + return self::deserializeArray((array)$data, $type); + } + + if (gettype($type) != "string") { + throw new JsonException("Unexpected non-string type."); + } + + return self::deserializeSingleValue($data, $type); + } + + /** + * Deserializes a value based on the possible types in a union type definition. + * + * @param mixed $data The data to deserialize. + * @param Union $type The union type definition. + * @return mixed The deserialized value. + * @throws JsonException If none of the union types can successfully deserialize the value. + */ + public static function deserializeUnion(mixed $data, Union $type): mixed + { + foreach ($type->types as $unionType) { + try { + return self::deserializeValue($data, $unionType); + } catch (\Throwable) { + // Catching Throwable instead of Exception to handle TypeError + // that occurs when assigning null to non-nullable typed properties + continue; + } + } + $readableType = Utils::getReadableType($data); + throw new JsonException( + "Cannot deserialize value of type $readableType with any of the union types: " . $type + ); + } + + /** + * Deserializes a single value based on its expected type. + * + * @param mixed $data The data to deserialize. + * @param string $type The expected type. + * @return mixed The deserialized value. + * @throws JsonException If deserialization fails. + */ + private static function deserializeSingleValue(mixed $data, string $type): mixed + { + if ($type === 'null' && $data === null) { + return null; + } + + if ($type === 'date' && is_string($data)) { + return self::deserializeDate($data); + } + + if ($type === 'datetime' && is_string($data)) { + return self::deserializeDateTime($data); + } + + if ($type === 'mixed') { + return $data; + } + + if (class_exists($type) && is_array($data)) { + return self::deserializeObject($data, $type); + } + + // Handle floats as a special case since gettype($data) returns "double" for float values in PHP, and because + // floats make come through from json_decoded as integers + if ($type === 'float' && (is_numeric($data))) { + return (float) $data; + } + + if (gettype($data) === $type) { + return $data; + } + + throw new JsonException("Unable to deserialize value of type '" . gettype($data) . "' as '$type'."); + } + + /** + * Deserializes an array into an object of the given type. + * + * @param array $data The data to deserialize. + * @param string $type The class name of the object to deserialize into. + * + * @return object The deserialized object. + * + * @throws JsonException If the type does not implement JsonSerializableType. + */ + public static function deserializeObject(array $data, string $type): object + { + if (!is_subclass_of($type, JsonSerializableType::class)) { + throw new JsonException("$type is not a subclass of JsonSerializableType."); + } + return $type::jsonDeserialize($data); + } + + /** + * Deserializes a map (associative array) with defined key and value types. + * + * @param array $data The associative array to deserialize. + * @param array $type The type definition for the map. + * @return array The deserialized map. + * @throws JsonException If deserialization fails. + */ + private static function deserializeMap(array $data, array $type): array + { + $keyType = array_key_first($type); + $valueType = $type[$keyType]; + $result = []; + + foreach ($data as $key => $item) { + $key = Utils::castKey($key, (string)$keyType); + $result[$key] = self::deserializeValue($item, $valueType); + } + + return $result; + } + + /** + * Deserializes a list (indexed array) with a defined value type. + * + * @param array $data The list to deserialize. + * @param array $type The type definition for the list. + * @return array The deserialized list. + * @throws JsonException If deserialization fails. + */ + private static function deserializeList(array $data, array $type): array + { + $valueType = $type[0]; + return array_map(fn ($item) => self::deserializeValue($item, $valueType), $data); + } +} diff --git a/seed/php-model/inferred-auth-implicit-api-key/src/Core/Json/JsonEncoder.php b/seed/php-model/inferred-auth-implicit-api-key/src/Core/Json/JsonEncoder.php new file mode 100644 index 000000000000..0dbf3fcc9948 --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-api-key/src/Core/Json/JsonEncoder.php @@ -0,0 +1,20 @@ + Extra properties from JSON that don't map to class properties */ + private array $__additionalProperties = []; + + /** + * Serializes the object to a JSON string. + * + * @return string JSON-encoded string representation of the object. + * @throws Exception If encoding fails. + */ + public function toJson(): string + { + $serializedObject = $this->jsonSerialize(); + $encoded = JsonEncoder::encode($serializedObject); + if (!$encoded) { + throw new Exception("Could not encode type"); + } + return $encoded; + } + + /** + * Serializes the object to an array. + * + * @return mixed[] Array representation of the object. + * @throws JsonException If serialization fails. + */ + public function jsonSerialize(): array + { + $result = []; + $reflectionClass = new \ReflectionClass($this); + foreach ($reflectionClass->getProperties() as $property) { + $jsonKey = self::getJsonKey($property); + if ($jsonKey == null) { + continue; + } + $value = $property->getValue($this); + + // Handle DateTime properties + $dateTypeAttr = $property->getAttributes(Date::class)[0] ?? null; + if ($dateTypeAttr && $value instanceof DateTime) { + $dateType = $dateTypeAttr->newInstance()->type; + $value = ($dateType === Date::TYPE_DATE) + ? JsonSerializer::serializeDate($value) + : JsonSerializer::serializeDateTime($value); + } + + // Handle Union annotations + $unionTypeAttr = $property->getAttributes(Union::class)[0] ?? null; + if ($unionTypeAttr) { + $unionType = $unionTypeAttr->newInstance(); + $value = JsonSerializer::serializeUnion($value, $unionType); + } + + // Handle arrays with type annotations + $arrayTypeAttr = $property->getAttributes(ArrayType::class)[0] ?? null; + if ($arrayTypeAttr && is_array($value)) { + $arrayType = $arrayTypeAttr->newInstance()->type; + $value = JsonSerializer::serializeArray($value, $arrayType); + } + + // Handle object + if (is_object($value)) { + $value = JsonSerializer::serializeObject($value); + } + + if ($value !== null) { + $result[$jsonKey] = $value; + } + } + return $result; + } + + /** + * Deserializes a JSON string into an instance of the calling class. + * + * @param string $json JSON string to deserialize. + * @return static Deserialized object. + * @throws JsonException If decoding fails or the result is not an array. + * @throws Exception If deserialization fails. + */ + public static function fromJson(string $json): static + { + $decodedJson = JsonDecoder::decode($json); + if (!is_array($decodedJson)) { + throw new JsonException("Unexpected non-array decoded type: " . gettype($decodedJson)); + } + return self::jsonDeserialize($decodedJson); + } + + /** + * Deserializes an array into an instance of the calling class. + * + * @param array $data Array data to deserialize. + * @return static Deserialized object. + * @throws JsonException If deserialization fails. + */ + public static function jsonDeserialize(array $data): static + { + $reflectionClass = new \ReflectionClass(static::class); + $constructor = $reflectionClass->getConstructor(); + if ($constructor === null) { + throw new JsonException("No constructor found."); + } + + $args = []; + $properties = []; + $additionalProperties = []; + foreach ($reflectionClass->getProperties() as $property) { + $jsonKey = self::getJsonKey($property) ?? $property->getName(); + $properties[$jsonKey] = $property; + } + + foreach ($data as $jsonKey => $value) { + if (!isset($properties[$jsonKey])) { + // This JSON key doesn't map to any class property - add it to additionalProperties + $additionalProperties[$jsonKey] = $value; + continue; + } + + $property = $properties[$jsonKey]; + + // Handle Date annotation + $dateTypeAttr = $property->getAttributes(Date::class)[0] ?? null; + if ($dateTypeAttr) { + $dateType = $dateTypeAttr->newInstance()->type; + if (!is_string($value)) { + throw new JsonException("Unexpected non-string type for date."); + } + $value = ($dateType === Date::TYPE_DATE) + ? JsonDeserializer::deserializeDate($value) + : JsonDeserializer::deserializeDateTime($value); + } + + // Handle Array annotation + $arrayTypeAttr = $property->getAttributes(ArrayType::class)[0] ?? null; + if (is_array($value) && $arrayTypeAttr) { + $arrayType = $arrayTypeAttr->newInstance()->type; + $value = JsonDeserializer::deserializeArray($value, $arrayType); + } + + // Handle Union annotations + $unionTypeAttr = $property->getAttributes(Union::class)[0] ?? null; + if ($unionTypeAttr) { + $unionType = $unionTypeAttr->newInstance(); + $value = JsonDeserializer::deserializeUnion($value, $unionType); + } + + // Handle object + $type = $property->getType(); + if (is_array($value) && $type instanceof ReflectionNamedType && !$type->isBuiltin()) { + $value = JsonDeserializer::deserializeObject($value, $type->getName()); + } + + $args[$property->getName()] = $value; + } + + // Fill in any missing properties with defaults + foreach ($properties as $property) { + if (!isset($args[$property->getName()])) { + $args[$property->getName()] = $property->getDefaultValue() ?? null; + } + } + + // @phpstan-ignore-next-line + $result = new static($args); + $result->__additionalProperties = $additionalProperties; + return $result; + } + + /** + * Get properties from JSON that weren't mapped to class fields + * @return array + */ + public function getAdditionalProperties(): array + { + return $this->__additionalProperties; + } + + /** + * Retrieves the JSON key associated with a property. + * + * @param ReflectionProperty $property The reflection property. + * @return ?string The JSON key, or null if not available. + */ + private static function getJsonKey(ReflectionProperty $property): ?string + { + $jsonPropertyAttr = $property->getAttributes(JsonProperty::class)[0] ?? null; + return $jsonPropertyAttr?->newInstance()?->name; + } +} diff --git a/seed/php-model/inferred-auth-implicit-api-key/src/Core/Json/JsonSerializer.php b/seed/php-model/inferred-auth-implicit-api-key/src/Core/Json/JsonSerializer.php new file mode 100644 index 000000000000..36dc06dfcf50 --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-api-key/src/Core/Json/JsonSerializer.php @@ -0,0 +1,197 @@ +format(Constant::DateFormat); + } + + /** + * Serializes a DateTime object into a string using the date-time format. + * Normalizes UTC times to use 'Z' suffix instead of '+00:00'. + * + * @param DateTime $date The DateTime object to serialize. + * @return string The serialized date-time string. + */ + public static function serializeDateTime(DateTime $date): string + { + $formatted = $date->format(Constant::DateTimeFormat); + if (str_ends_with($formatted, '+00:00')) { + return substr($formatted, 0, -6) . 'Z'; + } + return $formatted; + } + + /** + * Serializes an array based on type annotations (either a list or map). + * + * @param mixed[]|array $data The array to be serialized. + * @param mixed[]|array $type The type definition from the annotation. + * @return mixed[]|array The serialized array. + * @throws JsonException If serialization fails. + */ + public static function serializeArray(array $data, array $type): array + { + return Utils::isMapType($type) + ? self::serializeMap($data, $type) + : self::serializeList($data, $type); + } + + /** + * Serializes a value based on its type definition. + * + * @param mixed $data The value to serialize. + * @param mixed $type The type definition. + * @return mixed The serialized value. + * @throws JsonException If serialization fails. + */ + private static function serializeValue(mixed $data, mixed $type): mixed + { + if ($type instanceof Union) { + return self::serializeUnion($data, $type); + } + + if (is_array($type)) { + return self::serializeArray((array)$data, $type); + } + + if (gettype($type) != "string") { + throw new JsonException("Unexpected non-string type."); + } + + return self::serializeSingleValue($data, $type); + } + + /** + * Serializes a value for a union type definition. + * + * @param mixed $data The value to serialize. + * @param Union $unionType The union type definition. + * @return mixed The serialized value. + * @throws JsonException If serialization fails for all union types. + */ + public static function serializeUnion(mixed $data, Union $unionType): mixed + { + foreach ($unionType->types as $type) { + try { + return self::serializeValue($data, $type); + } catch (Exception) { + // Try the next type in the union + continue; + } + } + $readableType = Utils::getReadableType($data); + throw new JsonException( + "Cannot serialize value of type $readableType with any of the union types: " . $unionType + ); + } + + /** + * Serializes a single value based on its type. + * + * @param mixed $data The value to serialize. + * @param string $type The expected type. + * @return mixed The serialized value. + * @throws JsonException If serialization fails. + */ + private static function serializeSingleValue(mixed $data, string $type): mixed + { + if ($type === 'null' && $data === null) { + return null; + } + + if (($type === 'date' || $type === 'datetime') && $data instanceof DateTime) { + return $type === 'date' ? self::serializeDate($data) : self::serializeDateTime($data); + } + + if ($type === 'mixed') { + return $data; + } + + if (class_exists($type) && $data instanceof $type) { + return self::serializeObject($data); + } + + // Handle floats as a special case since gettype($data) returns "double" for float values in PHP. + if ($type === 'float' && is_float($data)) { + return $data; + } + + if (gettype($data) === $type) { + return $data; + } + + throw new JsonException("Unable to serialize value of type '" . gettype($data) . "' as '$type'."); + } + + /** + * Serializes an object to a JSON-serializable format. + * + * @param object $data The object to serialize. + * @return mixed The serialized data. + * @throws JsonException If the object does not implement JsonSerializable. + */ + public static function serializeObject(object $data): mixed + { + if (!is_subclass_of($data, JsonSerializable::class)) { + $type = get_class($data); + throw new JsonException("Class $type must implement JsonSerializable."); + } + return $data->jsonSerialize(); + } + + /** + * Serializes a map (associative array) with defined key and value types. + * + * @param array $data The associative array to serialize. + * @param array $type The type definition for the map. + * @return array The serialized map. + * @throws JsonException If serialization fails. + */ + private static function serializeMap(array $data, array $type): array + { + $keyType = array_key_first($type); + if ($keyType === null) { + throw new JsonException("Unexpected no key in ArrayType."); + } + $valueType = $type[$keyType]; + $result = []; + + foreach ($data as $key => $item) { + $key = Utils::castKey($key, $keyType); + $result[$key] = self::serializeValue($item, $valueType); + } + + return $result; + } + + /** + * Serializes a list (indexed array) where only the value type is defined. + * + * @param array $data The list to serialize. + * @param array $type The type definition for the list. + * @return array The serialized list. + * @throws JsonException If serialization fails. + */ + private static function serializeList(array $data, array $type): array + { + $valueType = $type[0]; + return array_map(fn ($item) => self::serializeValue($item, $valueType), $data); + } +} diff --git a/seed/php-model/inferred-auth-implicit-api-key/src/Core/Json/Utils.php b/seed/php-model/inferred-auth-implicit-api-key/src/Core/Json/Utils.php new file mode 100644 index 000000000000..7577c058916d --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-api-key/src/Core/Json/Utils.php @@ -0,0 +1,61 @@ + $type The type definition from the annotation. + * @return bool True if the type is a map, false if it's a list. + */ + public static function isMapType(array $type): bool + { + return count($type) === 1 && !array_is_list($type); + } + + /** + * Casts the key to the appropriate type based on the key type. + * + * @param mixed $key The key to be cast. + * @param string $keyType The type to cast the key to ('string', 'integer', 'float'). + * @return mixed The casted key. + * @throws JsonException + */ + public static function castKey(mixed $key, string $keyType): mixed + { + if (!is_scalar($key)) { + throw new JsonException("Key must be a scalar type."); + } + return match ($keyType) { + 'integer' => (int)$key, + 'float' => (float)$key, + 'string' => (string)$key, + default => $key, + }; + } + + /** + * Returns a human-readable representation of the input's type. + * + * @param mixed $input The input value to determine the type of. + * @return string A readable description of the input type. + */ + public static function getReadableType(mixed $input): string + { + if (is_object($input)) { + return get_class($input); + } elseif (is_array($input)) { + return 'array(' . count($input) . ' items)'; + } elseif (is_null($input)) { + return 'null'; + } else { + return gettype($input); + } + } +} diff --git a/seed/php-model/inferred-auth-implicit-api-key/src/Core/Types/ArrayType.php b/seed/php-model/inferred-auth-implicit-api-key/src/Core/Types/ArrayType.php new file mode 100644 index 000000000000..a26d29008ec3 --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-api-key/src/Core/Types/ArrayType.php @@ -0,0 +1,16 @@ + 'valueType'] for maps, or ['valueType'] for lists + */ + public function __construct(public array $type) + { + } +} diff --git a/seed/php-model/inferred-auth-implicit-api-key/src/Core/Types/Constant.php b/seed/php-model/inferred-auth-implicit-api-key/src/Core/Types/Constant.php new file mode 100644 index 000000000000..5ac4518cc6d6 --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-api-key/src/Core/Types/Constant.php @@ -0,0 +1,12 @@ +> The types allowed for this property, which can be strings, arrays, or nested Union types. + */ + public array $types; + + /** + * Constructor for the Union attribute. + * + * @param string|Union|array ...$types The list of types that the property can accept. + * This can include primitive types (e.g., 'string', 'int'), arrays, or other Union instances. + * + * Example: + * ```php + * #[Union('string', 'null', 'date', new Union('boolean', 'int'))] + * ``` + */ + public function __construct(string|Union|array ...$types) + { + $this->types = $types; + } + + /** + * Converts the Union type to a string representation. + * + * @return string A string representation of the union types. + */ + public function __toString(): string + { + return implode(' | ', array_map(function ($type) { + if (is_string($type)) { + return $type; + } elseif ($type instanceof Union) { + return (string) $type; // Recursively handle nested unions + } elseif (is_array($type)) { + return 'array'; // Handle arrays + } + }, $this->types)); + } +} diff --git a/seed/php-model/inferred-auth-implicit-api-key/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/inferred-auth-implicit-api-key/tests/Core/Json/AdditionalPropertiesTest.php new file mode 100644 index 000000000000..5bcb7ba283a8 --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-api-key/tests/Core/Json/AdditionalPropertiesTest.php @@ -0,0 +1,76 @@ +name; + } + + /** + * @return string|null + */ + public function getEmail(): ?string + { + return $this->email; + } + + /** + * @param array{ + * name: string, + * email?: string|null, + * } $values + */ + public function __construct( + array $values, + ) { + $this->name = $values['name']; + $this->email = $values['email'] ?? null; + } +} + +class AdditionalPropertiesTest extends TestCase +{ + public function testExtraProperties(): void + { + $expectedJson = json_encode( + [ + 'name' => 'john.doe', + 'email' => 'john.doe@example.com', + 'age' => 42 + ], + JSON_THROW_ON_ERROR + ); + + $person = Person::fromJson($expectedJson); + $this->assertEquals('john.doe', $person->getName()); + $this->assertEquals('john.doe@example.com', $person->getEmail()); + $this->assertEquals( + [ + 'age' => 42 + ], + $person->getAdditionalProperties(), + ); + } +} diff --git a/seed/php-model/inferred-auth-implicit-api-key/tests/Core/Json/DateArrayTest.php b/seed/php-model/inferred-auth-implicit-api-key/tests/Core/Json/DateArrayTest.php new file mode 100644 index 000000000000..a72cfdbdd227 --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-api-key/tests/Core/Json/DateArrayTest.php @@ -0,0 +1,54 @@ +dates = $values['dates']; + } +} + +class DateArrayTest extends TestCase +{ + public function testDateTimeInArrays(): void + { + $expectedJson = json_encode( + [ + 'dates' => ['2023-01-01', '2023-02-01', '2023-03-01'] + ], + JSON_THROW_ON_ERROR + ); + + $object = DateArray::fromJson($expectedJson); + $this->assertInstanceOf(DateTime::class, $object->dates[0], 'dates[0] should be a DateTime instance.'); + $this->assertEquals('2023-01-01', $object->dates[0]->format('Y-m-d'), 'dates[0] should have the correct date.'); + $this->assertInstanceOf(DateTime::class, $object->dates[1], 'dates[1] should be a DateTime instance.'); + $this->assertEquals('2023-02-01', $object->dates[1]->format('Y-m-d'), 'dates[1] should have the correct date.'); + $this->assertInstanceOf(DateTime::class, $object->dates[2], 'dates[2] should be a DateTime instance.'); + $this->assertEquals('2023-03-01', $object->dates[2]->format('Y-m-d'), 'dates[2] should have the correct date.'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for dates array.'); + } +} diff --git a/seed/php-model/inferred-auth-implicit-api-key/tests/Core/Json/EmptyArrayTest.php b/seed/php-model/inferred-auth-implicit-api-key/tests/Core/Json/EmptyArrayTest.php new file mode 100644 index 000000000000..d243a08916d4 --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-api-key/tests/Core/Json/EmptyArrayTest.php @@ -0,0 +1,71 @@ + $emptyMapArray + */ + #[JsonProperty('empty_map_array')] + #[ArrayType(['integer' => new Union('string', 'null')])] + public array $emptyMapArray; + + /** + * @var array $emptyDatesArray + */ + #[ArrayType([new Union('date', 'null')])] + #[JsonProperty('empty_dates_array')] + public array $emptyDatesArray; + + /** + * @param array{ + * emptyStringArray: string[], + * emptyMapArray: array, + * emptyDatesArray: array, + * } $values + */ + public function __construct( + array $values, + ) { + $this->emptyStringArray = $values['emptyStringArray']; + $this->emptyMapArray = $values['emptyMapArray']; + $this->emptyDatesArray = $values['emptyDatesArray']; + } +} + +class EmptyArrayTest extends TestCase +{ + public function testEmptyArray(): void + { + $expectedJson = json_encode( + [ + 'empty_string_array' => [], + 'empty_map_array' => [], + 'empty_dates_array' => [] + ], + JSON_THROW_ON_ERROR + ); + + $object = EmptyArray::fromJson($expectedJson); + $this->assertEmpty($object->emptyStringArray, 'empty_string_array should be empty.'); + $this->assertEmpty($object->emptyMapArray, 'empty_map_array should be empty.'); + $this->assertEmpty($object->emptyDatesArray, 'empty_dates_array should be empty.'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for EmptyArraysType.'); + } +} diff --git a/seed/php-model/inferred-auth-implicit-api-key/tests/Core/Json/EnumTest.php b/seed/php-model/inferred-auth-implicit-api-key/tests/Core/Json/EnumTest.php new file mode 100644 index 000000000000..bf83d5b8ab0f --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-api-key/tests/Core/Json/EnumTest.php @@ -0,0 +1,76 @@ +value; + } +} + +class ShapeType extends JsonSerializableType +{ + /** + * @var Shape $shape + */ + #[JsonProperty('shape')] + public Shape $shape; + + /** + * @var Shape[] $shapes + */ + #[ArrayType([Shape::class])] + #[JsonProperty('shapes')] + public array $shapes; + + /** + * @param Shape $shape + * @param Shape[] $shapes + */ + public function __construct( + Shape $shape, + array $shapes, + ) { + $this->shape = $shape; + $this->shapes = $shapes; + } +} + +class EnumTest extends TestCase +{ + public function testEnumSerialization(): void + { + $object = new ShapeType( + Shape::Circle, + [Shape::Square, Shape::Circle, Shape::Triangle] + ); + + $expectedJson = json_encode([ + 'shape' => 'CIRCLE', + 'shapes' => ['SQUARE', 'CIRCLE', 'TRIANGLE'] + ], JSON_THROW_ON_ERROR); + + $actualJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString( + $expectedJson, + $actualJson, + 'Serialized JSON does not match expected JSON for shape and shapes properties.' + ); + } +} diff --git a/seed/php-model/inferred-auth-implicit-api-key/tests/Core/Json/ExhaustiveTest.php b/seed/php-model/inferred-auth-implicit-api-key/tests/Core/Json/ExhaustiveTest.php new file mode 100644 index 000000000000..ed1330eda3e4 --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-api-key/tests/Core/Json/ExhaustiveTest.php @@ -0,0 +1,197 @@ +nestedProperty = $values['nestedProperty']; + } +} + +class Type extends JsonSerializableType +{ + /** + * @var Nested nestedType + */ + #[JsonProperty('nested_type')] + public Nested $nestedType; /** + + * @var string $simpleProperty + */ + #[JsonProperty('simple_property')] + public string $simpleProperty; + + /** + * @var DateTime $dateProperty + */ + #[Date(Date::TYPE_DATE)] + #[JsonProperty('date_property')] + public DateTime $dateProperty; + + /** + * @var DateTime $datetimeProperty + */ + #[Date(Date::TYPE_DATETIME)] + #[JsonProperty('datetime_property')] + public DateTime $datetimeProperty; + + /** + * @var array $stringArray + */ + #[ArrayType(['string'])] + #[JsonProperty('string_array')] + public array $stringArray; + + /** + * @var array $mapProperty + */ + #[ArrayType(['string' => 'integer'])] + #[JsonProperty('map_property')] + public array $mapProperty; + + /** + * @var array $objectArray + */ + #[ArrayType(['integer' => new Union(Nested::class, 'null')])] + #[JsonProperty('object_array')] + public array $objectArray; + + /** + * @var array> $nestedArray + */ + #[ArrayType(['integer' => ['integer' => new Union('string', 'null')]])] + #[JsonProperty('nested_array')] + public array $nestedArray; + + /** + * @var array $datesArray + */ + #[ArrayType([new Union('date', 'null')])] + #[JsonProperty('dates_array')] + public array $datesArray; + + /** + * @var string|null $nullableProperty + */ + #[JsonProperty('nullable_property')] + public ?string $nullableProperty; + + /** + * @param array{ + * nestedType: Nested, + * simpleProperty: string, + * dateProperty: DateTime, + * datetimeProperty: DateTime, + * stringArray: array, + * mapProperty: array, + * objectArray: array, + * nestedArray: array>, + * datesArray: array, + * nullableProperty?: string|null, + * } $values + */ + public function __construct( + array $values, + ) { + $this->nestedType = $values['nestedType']; + $this->simpleProperty = $values['simpleProperty']; + $this->dateProperty = $values['dateProperty']; + $this->datetimeProperty = $values['datetimeProperty']; + $this->stringArray = $values['stringArray']; + $this->mapProperty = $values['mapProperty']; + $this->objectArray = $values['objectArray']; + $this->nestedArray = $values['nestedArray']; + $this->datesArray = $values['datesArray']; + $this->nullableProperty = $values['nullableProperty'] ?? null; + } +} + +class ExhaustiveTest extends TestCase +{ + /** + * Test serialization and deserialization of all types in Type. + */ + public function testExhaustive(): void + { + $expectedJson = json_encode( + [ + 'nested_type' => ['nested_property' => '1995-07-20'], + 'simple_property' => 'Test String', + // Omit 'nullable_property' to test null serialization + 'date_property' => '2023-01-01', + 'datetime_property' => '2023-01-01T12:34:56Z', + 'string_array' => ['one', 'two', 'three'], + 'map_property' => ['key1' => 1, 'key2' => 2], + 'object_array' => [ + 1 => ['nested_property' => '2021-07-20'], + 2 => null, // Testing nullable objects in array + ], + 'nested_array' => [ + 1 => [1 => 'value1', 2 => null], // Testing nullable strings in nested array + 2 => [3 => 'value3', 4 => 'value4'] + ], + 'dates_array' => ['2023-01-01', null, '2023-03-01'] // Testing nullable dates in array> + ], + JSON_THROW_ON_ERROR + ); + + $object = Type::fromJson($expectedJson); + + // Check that nullable property is null and not included in JSON + $this->assertNull($object->nullableProperty, 'Nullable property should be null.'); + + // Check date properties + $this->assertInstanceOf(DateTime::class, $object->dateProperty, 'date_property should be a DateTime instance.'); + $this->assertEquals('2023-01-01', $object->dateProperty->format('Y-m-d'), 'date_property should have the correct date.'); + $this->assertInstanceOf(DateTime::class, $object->datetimeProperty, 'datetime_property should be a DateTime instance.'); + $this->assertEquals('2023-01-01 12:34:56', $object->datetimeProperty->format('Y-m-d H:i:s'), 'datetime_property should have the correct datetime.'); + + // Check scalar arrays + $this->assertEquals(['one', 'two', 'three'], $object->stringArray, 'string_array should match the original data.'); + $this->assertEquals(['key1' => 1, 'key2' => 2], $object->mapProperty, 'map_property should match the original data.'); + + // Check object array with nullable elements + $this->assertInstanceOf(Nested::class, $object->objectArray[1], 'object_array[1] should be an instance of TestNestedType1.'); + $this->assertEquals('2021-07-20', $object->objectArray[1]->nestedProperty->format('Y-m-d'), 'object_array[1]->nestedProperty should match the original data.'); + $this->assertNull($object->objectArray[2], 'object_array[2] should be null.'); + + // Check nested array with nullable strings + $this->assertEquals('value1', $object->nestedArray[1][1], 'nested_array[1][1] should match the original data.'); + $this->assertNull($object->nestedArray[1][2], 'nested_array[1][2] should be null.'); + $this->assertEquals('value3', $object->nestedArray[2][3], 'nested_array[2][3] should match the original data.'); + $this->assertEquals('value4', $object->nestedArray[2][4], 'nested_array[2][4] should match the original data.'); + + // Check dates array with nullable DateTime objects + $this->assertInstanceOf(DateTime::class, $object->datesArray[0], 'dates_array[0] should be a DateTime instance.'); + $this->assertEquals('2023-01-01', $object->datesArray[0]->format('Y-m-d'), 'dates_array[0] should have the correct date.'); + $this->assertNull($object->datesArray[1], 'dates_array[1] should be null.'); + $this->assertInstanceOf(DateTime::class, $object->datesArray[2], 'dates_array[2] should be a DateTime instance.'); + $this->assertEquals('2023-03-01', $object->datesArray[2]->format('Y-m-d'), 'dates_array[2] should have the correct date.'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'The serialized JSON does not match the original JSON.'); + } +} diff --git a/seed/php-model/inferred-auth-implicit-api-key/tests/Core/Json/InvalidTest.php b/seed/php-model/inferred-auth-implicit-api-key/tests/Core/Json/InvalidTest.php new file mode 100644 index 000000000000..7d1d79406a5a --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-api-key/tests/Core/Json/InvalidTest.php @@ -0,0 +1,42 @@ +integerProperty = $values['integerProperty']; + } +} + +class InvalidTest extends TestCase +{ + public function testInvalidJsonThrowsException(): void + { + $this->expectException(\TypeError::class); + $json = json_encode( + [ + 'integer_property' => 'not_an_integer' + ], + JSON_THROW_ON_ERROR + ); + Invalid::fromJson($json); + } +} diff --git a/seed/php-model/inferred-auth-implicit-api-key/tests/Core/Json/NestedUnionArrayTest.php b/seed/php-model/inferred-auth-implicit-api-key/tests/Core/Json/NestedUnionArrayTest.php new file mode 100644 index 000000000000..0fcdd06667e5 --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-api-key/tests/Core/Json/NestedUnionArrayTest.php @@ -0,0 +1,89 @@ +nestedProperty = $values['nestedProperty']; + } +} + +class NestedUnionArray extends JsonSerializableType +{ + /** + * @var array> $nestedArray + */ + #[ArrayType(['integer' => ['integer' => new Union(UnionObject::class, 'null', 'date')]])] + #[JsonProperty('nested_array')] + public array $nestedArray; + + /** + * @param array{ + * nestedArray: array>, + * } $values + */ + public function __construct( + array $values, + ) { + $this->nestedArray = $values['nestedArray']; + } +} + +class NestedUnionArrayTest extends TestCase +{ + public function testNestedUnionArray(): void + { + $expectedJson = json_encode( + [ + 'nested_array' => [ + 1 => [ + 1 => ['nested_property' => 'Nested One'], + 2 => null, + 4 => '2023-01-02' + ], + 2 => [ + 5 => ['nested_property' => 'Nested Two'], + 7 => '2023-02-02' + ] + ] + ], + JSON_THROW_ON_ERROR + ); + + $object = NestedUnionArray::fromJson($expectedJson); + $this->assertInstanceOf(UnionObject::class, $object->nestedArray[1][1], 'nested_array[1][1] should be an instance of Object.'); + $this->assertEquals('Nested One', $object->nestedArray[1][1]->nestedProperty, 'nested_array[1][1]->nestedProperty should match the original data.'); + $this->assertNull($object->nestedArray[1][2], 'nested_array[1][2] should be null.'); + $this->assertInstanceOf(DateTime::class, $object->nestedArray[1][4], 'nested_array[1][4] should be a DateTime instance.'); + $this->assertEquals('2023-01-02T00:00:00+00:00', $object->nestedArray[1][4]->format(Constant::DateTimeFormat), 'nested_array[1][4] should have the correct datetime.'); + $this->assertInstanceOf(UnionObject::class, $object->nestedArray[2][5], 'nested_array[2][5] should be an instance of Object.'); + $this->assertEquals('Nested Two', $object->nestedArray[2][5]->nestedProperty, 'nested_array[2][5]->nestedProperty should match the original data.'); + $this->assertInstanceOf(DateTime::class, $object->nestedArray[2][7], 'nested_array[1][4] should be a DateTime instance.'); + $this->assertEquals('2023-02-02', $object->nestedArray[2][7]->format('Y-m-d'), 'nested_array[1][4] should have the correct date.'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for nested_array.'); + } +} diff --git a/seed/php-model/inferred-auth-implicit-api-key/tests/Core/Json/NullPropertyTest.php b/seed/php-model/inferred-auth-implicit-api-key/tests/Core/Json/NullPropertyTest.php new file mode 100644 index 000000000000..ce20a2442825 --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-api-key/tests/Core/Json/NullPropertyTest.php @@ -0,0 +1,53 @@ +nonNullProperty = $values['nonNullProperty']; + $this->nullProperty = $values['nullProperty'] ?? null; + } +} + +class NullPropertyTest extends TestCase +{ + public function testNullPropertiesAreOmitted(): void + { + $object = new NullProperty( + [ + "nonNullProperty" => "Test String", + "nullProperty" => null + ] + ); + + $serialized = $object->jsonSerialize(); + $this->assertArrayHasKey('non_null_property', $serialized, 'non_null_property should be present in the serialized JSON.'); + $this->assertArrayNotHasKey('null_property', $serialized, 'null_property should be omitted from the serialized JSON.'); + $this->assertEquals('Test String', $serialized['non_null_property'], 'non_null_property should have the correct value.'); + } +} diff --git a/seed/php-model/inferred-auth-implicit-api-key/tests/Core/Json/NullableArrayTest.php b/seed/php-model/inferred-auth-implicit-api-key/tests/Core/Json/NullableArrayTest.php new file mode 100644 index 000000000000..fe0f19de6b13 --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-api-key/tests/Core/Json/NullableArrayTest.php @@ -0,0 +1,49 @@ + $nullableStringArray + */ + #[ArrayType([new Union('string', 'null')])] + #[JsonProperty('nullable_string_array')] + public array $nullableStringArray; + + /** + * @param array{ + * nullableStringArray: array, + * } $values + */ + public function __construct( + array $values, + ) { + $this->nullableStringArray = $values['nullableStringArray']; + } +} + +class NullableArrayTest extends TestCase +{ + public function testNullableArray(): void + { + $expectedJson = json_encode( + [ + 'nullable_string_array' => ['one', null, 'three'] + ], + JSON_THROW_ON_ERROR + ); + + $object = NullableArray::fromJson($expectedJson); + $this->assertEquals(['one', null, 'three'], $object->nullableStringArray, 'nullable_string_array should match the original data.'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for nullable_string_array.'); + } +} diff --git a/seed/php-model/inferred-auth-implicit-api-key/tests/Core/Json/ScalarTest.php b/seed/php-model/inferred-auth-implicit-api-key/tests/Core/Json/ScalarTest.php new file mode 100644 index 000000000000..604b7c0b959b --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-api-key/tests/Core/Json/ScalarTest.php @@ -0,0 +1,116 @@ + $intFloatArray + */ + #[ArrayType([new Union('integer', 'float')])] + #[JsonProperty('int_float_array')] + public array $intFloatArray; + + /** + * @var array $floatArray + */ + #[ArrayType(['float'])] + #[JsonProperty('float_array')] + public array $floatArray; + + /** + * @var bool|null $nullableBooleanProperty + */ + #[JsonProperty('nullable_boolean_property')] + public ?bool $nullableBooleanProperty; + + /** + * @param array{ + * integerProperty: int, + * floatProperty: float, + * otherFloatProperty: float, + * booleanProperty: bool, + * stringProperty: string, + * intFloatArray: array, + * floatArray: array, + * nullableBooleanProperty?: bool|null, + * } $values + */ + public function __construct( + array $values, + ) { + $this->integerProperty = $values['integerProperty']; + $this->floatProperty = $values['floatProperty']; + $this->otherFloatProperty = $values['otherFloatProperty']; + $this->booleanProperty = $values['booleanProperty']; + $this->stringProperty = $values['stringProperty']; + $this->intFloatArray = $values['intFloatArray']; + $this->floatArray = $values['floatArray']; + $this->nullableBooleanProperty = $values['nullableBooleanProperty'] ?? null; + } +} + +class ScalarTest extends TestCase +{ + public function testAllScalarTypesIncludingFloat(): void + { + $expectedJson = json_encode( + [ + 'integer_property' => 42, + 'float_property' => 3.14159, + 'other_float_property' => 3, + 'boolean_property' => true, + 'string_property' => 'Hello, World!', + 'int_float_array' => [1, 2.5, 3, 4.75], + 'float_array' => [1, 2, 3, 4] // Ensure we handle "integer-looking" floats + ], + JSON_THROW_ON_ERROR + ); + + $object = Scalar::fromJson($expectedJson); + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals(3.14159, $object->floatProperty, 'float_property should be 3.14159.'); + $this->assertTrue($object->booleanProperty, 'boolean_property should be true.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + $this->assertNull($object->nullableBooleanProperty, 'nullable_boolean_property should be null.'); + $this->assertEquals([1, 2.5, 3, 4.75], $object->intFloatArray, 'int_float_array should match the original data.'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTest.'); + } +} diff --git a/seed/php-model/inferred-auth-implicit-api-key/tests/Core/Json/TraitTest.php b/seed/php-model/inferred-auth-implicit-api-key/tests/Core/Json/TraitTest.php new file mode 100644 index 000000000000..837f239115f7 --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-api-key/tests/Core/Json/TraitTest.php @@ -0,0 +1,60 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $expectedJson = json_encode( + [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ], + JSON_THROW_ON_ERROR + ); + + $object = TypeWithTrait::fromJson($expectedJson); + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + } +} diff --git a/seed/php-model/inferred-auth-implicit-api-key/tests/Core/Json/UnionArrayTest.php b/seed/php-model/inferred-auth-implicit-api-key/tests/Core/Json/UnionArrayTest.php new file mode 100644 index 000000000000..33899cd3f051 --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-api-key/tests/Core/Json/UnionArrayTest.php @@ -0,0 +1,57 @@ + $mixedDates + */ + #[ArrayType(['integer' => new Union('datetime', 'string', 'null')])] + #[JsonProperty('mixed_dates')] + public array $mixedDates; + + /** + * @param array{ + * mixedDates: array, + * } $values + */ + public function __construct( + array $values, + ) { + $this->mixedDates = $values['mixedDates']; + } +} + +class UnionArrayTest extends TestCase +{ + public function testUnionArray(): void + { + $expectedJson = json_encode( + [ + 'mixed_dates' => [ + 1 => '2023-01-01T12:00:00Z', + 2 => null, + 3 => 'Some String' + ] + ], + JSON_THROW_ON_ERROR + ); + + $object = UnionArray::fromJson($expectedJson); + $this->assertInstanceOf(DateTime::class, $object->mixedDates[1], 'mixed_dates[1] should be a DateTime instance.'); + $this->assertEquals('2023-01-01 12:00:00', $object->mixedDates[1]->format('Y-m-d H:i:s'), 'mixed_dates[1] should have the correct datetime.'); + $this->assertNull($object->mixedDates[2], 'mixed_dates[2] should be null.'); + $this->assertEquals('Some String', $object->mixedDates[3], 'mixed_dates[3] should be "Some String".'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for mixed_dates.'); + } +} diff --git a/seed/php-model/inferred-auth-implicit-api-key/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/inferred-auth-implicit-api-key/tests/Core/Json/UnionPropertyTest.php new file mode 100644 index 000000000000..3119baace627 --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-api-key/tests/Core/Json/UnionPropertyTest.php @@ -0,0 +1,115 @@ + 'integer'], UnionProperty::class)] + #[JsonProperty('complexUnion')] + public mixed $complexUnion; + + /** + * @param array{ + * complexUnion: string|int|null|array|UnionProperty + * } $values + */ + public function __construct( + array $values, + ) { + $this->complexUnion = $values['complexUnion']; + } +} + +class UnionPropertyTest extends TestCase +{ + public function testWithMapOfIntToInt(): void + { + $expectedJson = json_encode( + [ + 'complexUnion' => [1 => 100, 2 => 200] + ], + JSON_THROW_ON_ERROR + ); + + $object = UnionProperty::fromJson($expectedJson); + $this->assertIsArray($object->complexUnion, 'complexUnion should be an array.'); + $this->assertEquals([1 => 100, 2 => 200], $object->complexUnion, 'complexUnion should match the original map of int => int.'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); + } + + public function testWithNestedUnionPropertyType(): void + { + $expectedJson = json_encode( + [ + 'complexUnion' => new UnionProperty( + [ + 'complexUnion' => 'Nested String' + ] + ) + ], + JSON_THROW_ON_ERROR + ); + + $object = UnionProperty::fromJson($expectedJson); + $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); + $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); + } + + public function testWithNull(): void + { + $expectedJson = json_encode( + [], + JSON_THROW_ON_ERROR + ); + + $object = UnionProperty::fromJson($expectedJson); + $this->assertNull($object->complexUnion, 'complexUnion should be null.'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); + } + + public function testWithInteger(): void + { + $expectedJson = json_encode( + [ + 'complexUnion' => 42 + ], + JSON_THROW_ON_ERROR + ); + + $object = UnionProperty::fromJson($expectedJson); + $this->assertIsInt($object->complexUnion, 'complexUnion should be an integer.'); + $this->assertEquals(42, $object->complexUnion, 'complexUnion should match the original integer.'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); + } + + public function testWithString(): void + { + $expectedJson = json_encode( + [ + 'complexUnion' => 'Some String' + ], + JSON_THROW_ON_ERROR + ); + + $object = UnionProperty::fromJson($expectedJson); + $this->assertIsString($object->complexUnion, 'complexUnion should be a string.'); + $this->assertEquals('Some String', $object->complexUnion, 'complexUnion should match the original string.'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); + } +} diff --git a/seed/php-model/inferred-auth-implicit-no-expiry/src/Auth/TokenResponse.php b/seed/php-model/inferred-auth-implicit-no-expiry/src/Auth/TokenResponse.php index b30f2443d472..ac86f738cbfc 100644 --- a/seed/php-model/inferred-auth-implicit-no-expiry/src/Auth/TokenResponse.php +++ b/seed/php-model/inferred-auth-implicit-no-expiry/src/Auth/TokenResponse.php @@ -30,15 +30,16 @@ class TokenResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->accessToken = $values['accessToken'];$this->refreshToken = $values['refreshToken'] ?? null; + ) { + $this->accessToken = $values['accessToken']; + $this->refreshToken = $values['refreshToken'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/inferred-auth-implicit-no-expiry/src/Core/Json/JsonEncoder.php b/seed/php-model/inferred-auth-implicit-no-expiry/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/inferred-auth-implicit-no-expiry/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/inferred-auth-implicit-no-expiry/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/inferred-auth-implicit-no-expiry/src/Core/Json/JsonProperty.php b/seed/php-model/inferred-auth-implicit-no-expiry/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/inferred-auth-implicit-no-expiry/src/Core/Json/JsonProperty.php +++ b/seed/php-model/inferred-auth-implicit-no-expiry/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/inferred-auth-implicit-no-expiry/src/Core/Types/ArrayType.php b/seed/php-model/inferred-auth-implicit-no-expiry/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/inferred-auth-implicit-no-expiry/src/Core/Types/ArrayType.php +++ b/seed/php-model/inferred-auth-implicit-no-expiry/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/inferred-auth-implicit-no-expiry/src/Core/Types/Constant.php b/seed/php-model/inferred-auth-implicit-no-expiry/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/inferred-auth-implicit-no-expiry/src/Core/Types/Constant.php +++ b/seed/php-model/inferred-auth-implicit-no-expiry/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/inferred-auth-implicit-no-expiry/src/Core/Types/Union.php b/seed/php-model/inferred-auth-implicit-no-expiry/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/inferred-auth-implicit-no-expiry/src/Core/Types/Union.php +++ b/seed/php-model/inferred-auth-implicit-no-expiry/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/inferred-auth-implicit-no-expiry/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/inferred-auth-implicit-no-expiry/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/inferred-auth-implicit-no-expiry/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/inferred-auth-implicit-no-expiry/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/inferred-auth-implicit-no-expiry/tests/Core/Json/EnumTest.php b/seed/php-model/inferred-auth-implicit-no-expiry/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/inferred-auth-implicit-no-expiry/tests/Core/Json/EnumTest.php +++ b/seed/php-model/inferred-auth-implicit-no-expiry/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/inferred-auth-implicit-no-expiry/tests/Core/Json/TraitTest.php b/seed/php-model/inferred-auth-implicit-no-expiry/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/inferred-auth-implicit-no-expiry/tests/Core/Json/TraitTest.php +++ b/seed/php-model/inferred-auth-implicit-no-expiry/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/inferred-auth-implicit-no-expiry/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/inferred-auth-implicit-no-expiry/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/inferred-auth-implicit-no-expiry/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/inferred-auth-implicit-no-expiry/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/inferred-auth-implicit-reference/.fern/metadata.json b/seed/php-model/inferred-auth-implicit-reference/.fern/metadata.json new file mode 100644 index 000000000000..90046b58049c --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-reference/.fern/metadata.json @@ -0,0 +1,5 @@ +{ + "cliVersion": "DUMMY", + "generatorName": "fernapi/fern-php-model", + "generatorVersion": "latest" +} \ No newline at end of file diff --git a/seed/php-model/inferred-auth-implicit-reference/.github/workflows/ci.yml b/seed/php-model/inferred-auth-implicit-reference/.github/workflows/ci.yml new file mode 100644 index 000000000000..ba5836ae6967 --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-reference/.github/workflows/ci.yml @@ -0,0 +1,48 @@ +name: ci + +on: [push] + +jobs: + compile: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "8.1" + + - name: Install tools + run: | + composer install + + - name: Build + run: | + composer build + + - name: Analyze + run: | + composer analyze + + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "8.1" + + - name: Install tools + run: | + composer install + + - name: Run Tests + run: | + composer test diff --git a/seed/php-model/inferred-auth-implicit-reference/.gitignore b/seed/php-model/inferred-auth-implicit-reference/.gitignore new file mode 100644 index 000000000000..31a1aeb14f35 --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-reference/.gitignore @@ -0,0 +1,5 @@ +.idea +.php-cs-fixer.cache +.phpunit.result.cache +composer.lock +vendor/ \ No newline at end of file diff --git a/seed/php-model/inferred-auth-implicit-reference/composer.json b/seed/php-model/inferred-auth-implicit-reference/composer.json new file mode 100644 index 000000000000..c6a5d9f87477 --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-reference/composer.json @@ -0,0 +1,39 @@ +{ + "name": "seed/seed", + "version": "0.0.1", + "description": "Seed PHP Library", + "keywords": [ + "seed", + "api", + "sdk" + ], + "license": [], + "require": { + "php": "^8.1", + "ext-json": "*", + "guzzlehttp/guzzle": "^7.4" + }, + "require-dev": { + "phpunit/phpunit": "^9.0", + "friendsofphp/php-cs-fixer": "3.5.0", + "phpstan/phpstan": "^1.12" + }, + "autoload": { + "psr-4": { + "Seed\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Seed\\Tests\\": "tests/" + } + }, + "scripts": { + "build": [ + "@php -l src", + "@php -l tests" + ], + "test": "phpunit", + "analyze": "phpstan analyze src tests --memory-limit=1G" + } +} \ No newline at end of file diff --git a/seed/php-model/inferred-auth-implicit-reference/phpstan.neon b/seed/php-model/inferred-auth-implicit-reference/phpstan.neon new file mode 100644 index 000000000000..780706b8f8a2 --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-reference/phpstan.neon @@ -0,0 +1,6 @@ +parameters: + level: max + reportUnmatchedIgnoredErrors: false + paths: + - src + - tests \ No newline at end of file diff --git a/seed/php-model/inferred-auth-implicit-reference/phpunit.xml b/seed/php-model/inferred-auth-implicit-reference/phpunit.xml new file mode 100644 index 000000000000..54630a51163c --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-reference/phpunit.xml @@ -0,0 +1,7 @@ + + + + tests + + + \ No newline at end of file diff --git a/seed/php-model/inferred-auth-implicit-reference/snippet.json b/seed/php-model/inferred-auth-implicit-reference/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/php-model/inferred-auth-implicit-reference/src/Auth/GetTokenRequest.php b/seed/php-model/inferred-auth-implicit-reference/src/Auth/GetTokenRequest.php new file mode 100644 index 000000000000..d7b18c51e4a3 --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-reference/src/Auth/GetTokenRequest.php @@ -0,0 +1,69 @@ +clientId = $values['clientId']; + $this->clientSecret = $values['clientSecret']; + $this->audience = $values['audience']; + $this->grantType = $values['grantType']; + $this->scope = $values['scope'] ?? null; + } + + /** + * @return string + */ + public function __toString(): string + { + return $this->toJson(); + } +} diff --git a/seed/php-model/inferred-auth-implicit-reference/src/Auth/RefreshTokenRequest.php b/seed/php-model/inferred-auth-implicit-reference/src/Auth/RefreshTokenRequest.php new file mode 100644 index 000000000000..74628bdeafd4 --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-reference/src/Auth/RefreshTokenRequest.php @@ -0,0 +1,77 @@ +clientId = $values['clientId']; + $this->clientSecret = $values['clientSecret']; + $this->refreshToken = $values['refreshToken']; + $this->audience = $values['audience']; + $this->grantType = $values['grantType']; + $this->scope = $values['scope'] ?? null; + } + + /** + * @return string + */ + public function __toString(): string + { + return $this->toJson(); + } +} diff --git a/seed/php-model/inferred-auth-implicit-reference/src/Auth/TokenResponse.php b/seed/php-model/inferred-auth-implicit-reference/src/Auth/TokenResponse.php new file mode 100644 index 000000000000..9222a65c62ca --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-reference/src/Auth/TokenResponse.php @@ -0,0 +1,53 @@ +accessToken = $values['accessToken']; + $this->expiresIn = $values['expiresIn']; + $this->refreshToken = $values['refreshToken'] ?? null; + } + + /** + * @return string + */ + public function __toString(): string + { + return $this->toJson(); + } +} diff --git a/seed/php-model/inferred-auth-implicit-reference/src/Core/Json/JsonDecoder.php b/seed/php-model/inferred-auth-implicit-reference/src/Core/Json/JsonDecoder.php new file mode 100644 index 000000000000..2ddff0273482 --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-reference/src/Core/Json/JsonDecoder.php @@ -0,0 +1,161 @@ + $type The type definition for deserialization. + * @return mixed[]|array The deserialized array. + * @throws JsonException If the decoded value is not an array. + */ + public static function decodeArray(string $json, array $type): array + { + $decoded = self::decode($json); + if (!is_array($decoded)) { + throw new JsonException("Unexpected non-array json value: " . $json); + } + return JsonDeserializer::deserializeArray($decoded, $type); + } + + /** + * Decodes a JSON string and deserializes it based on the provided union type definition. + * + * @param string $json The JSON string to decode. + * @param Union $union The union type definition for deserialization. + * @return mixed The deserialized value. + * @throws JsonException If the deserialization for all types in the union fails. + */ + public static function decodeUnion(string $json, Union $union): mixed + { + $decoded = self::decode($json); + return JsonDeserializer::deserializeUnion($decoded, $union); + } + /** + * Decodes a JSON string and returns a mixed. + * + * @param string $json The JSON string to decode. + * @return mixed The decoded mixed. + * @throws JsonException If the decoded value is not an mixed. + */ + public static function decodeMixed(string $json): mixed + { + return self::decode($json); + } + + /** + * Decodes a JSON string into a PHP value. + * + * @param string $json The JSON string to decode. + * @return mixed The decoded value. + * @throws JsonException If an error occurs during JSON decoding. + */ + public static function decode(string $json): mixed + { + return json_decode($json, associative: true, flags: JSON_THROW_ON_ERROR); + } +} diff --git a/seed/php-model/inferred-auth-implicit-reference/src/Core/Json/JsonDeserializer.php b/seed/php-model/inferred-auth-implicit-reference/src/Core/Json/JsonDeserializer.php new file mode 100644 index 000000000000..ab6a3f826238 --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-reference/src/Core/Json/JsonDeserializer.php @@ -0,0 +1,206 @@ + $data The array to be deserialized. + * @param mixed[]|array $type The type definition from the annotation. + * @return mixed[]|array The deserialized array. + * @throws JsonException If deserialization fails. + */ + public static function deserializeArray(array $data, array $type): array + { + return Utils::isMapType($type) + ? self::deserializeMap($data, $type) + : self::deserializeList($data, $type); + } + + /** + * Deserializes a value based on its type definition. + * + * @param mixed $data The data to deserialize. + * @param mixed $type The type definition. + * @return mixed The deserialized value. + * @throws JsonException If deserialization fails. + */ + private static function deserializeValue(mixed $data, mixed $type): mixed + { + if ($type instanceof Union) { + return self::deserializeUnion($data, $type); + } + + if (is_array($type)) { + return self::deserializeArray((array)$data, $type); + } + + if (gettype($type) != "string") { + throw new JsonException("Unexpected non-string type."); + } + + return self::deserializeSingleValue($data, $type); + } + + /** + * Deserializes a value based on the possible types in a union type definition. + * + * @param mixed $data The data to deserialize. + * @param Union $type The union type definition. + * @return mixed The deserialized value. + * @throws JsonException If none of the union types can successfully deserialize the value. + */ + public static function deserializeUnion(mixed $data, Union $type): mixed + { + foreach ($type->types as $unionType) { + try { + return self::deserializeValue($data, $unionType); + } catch (\Throwable) { + // Catching Throwable instead of Exception to handle TypeError + // that occurs when assigning null to non-nullable typed properties + continue; + } + } + $readableType = Utils::getReadableType($data); + throw new JsonException( + "Cannot deserialize value of type $readableType with any of the union types: " . $type + ); + } + + /** + * Deserializes a single value based on its expected type. + * + * @param mixed $data The data to deserialize. + * @param string $type The expected type. + * @return mixed The deserialized value. + * @throws JsonException If deserialization fails. + */ + private static function deserializeSingleValue(mixed $data, string $type): mixed + { + if ($type === 'null' && $data === null) { + return null; + } + + if ($type === 'date' && is_string($data)) { + return self::deserializeDate($data); + } + + if ($type === 'datetime' && is_string($data)) { + return self::deserializeDateTime($data); + } + + if ($type === 'mixed') { + return $data; + } + + if (class_exists($type) && is_array($data)) { + return self::deserializeObject($data, $type); + } + + // Handle floats as a special case since gettype($data) returns "double" for float values in PHP, and because + // floats make come through from json_decoded as integers + if ($type === 'float' && (is_numeric($data))) { + return (float) $data; + } + + if (gettype($data) === $type) { + return $data; + } + + throw new JsonException("Unable to deserialize value of type '" . gettype($data) . "' as '$type'."); + } + + /** + * Deserializes an array into an object of the given type. + * + * @param array $data The data to deserialize. + * @param string $type The class name of the object to deserialize into. + * + * @return object The deserialized object. + * + * @throws JsonException If the type does not implement JsonSerializableType. + */ + public static function deserializeObject(array $data, string $type): object + { + if (!is_subclass_of($type, JsonSerializableType::class)) { + throw new JsonException("$type is not a subclass of JsonSerializableType."); + } + return $type::jsonDeserialize($data); + } + + /** + * Deserializes a map (associative array) with defined key and value types. + * + * @param array $data The associative array to deserialize. + * @param array $type The type definition for the map. + * @return array The deserialized map. + * @throws JsonException If deserialization fails. + */ + private static function deserializeMap(array $data, array $type): array + { + $keyType = array_key_first($type); + $valueType = $type[$keyType]; + $result = []; + + foreach ($data as $key => $item) { + $key = Utils::castKey($key, (string)$keyType); + $result[$key] = self::deserializeValue($item, $valueType); + } + + return $result; + } + + /** + * Deserializes a list (indexed array) with a defined value type. + * + * @param array $data The list to deserialize. + * @param array $type The type definition for the list. + * @return array The deserialized list. + * @throws JsonException If deserialization fails. + */ + private static function deserializeList(array $data, array $type): array + { + $valueType = $type[0]; + return array_map(fn ($item) => self::deserializeValue($item, $valueType), $data); + } +} diff --git a/seed/php-model/inferred-auth-implicit-reference/src/Core/Json/JsonEncoder.php b/seed/php-model/inferred-auth-implicit-reference/src/Core/Json/JsonEncoder.php new file mode 100644 index 000000000000..0dbf3fcc9948 --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-reference/src/Core/Json/JsonEncoder.php @@ -0,0 +1,20 @@ + Extra properties from JSON that don't map to class properties */ + private array $__additionalProperties = []; + + /** + * Serializes the object to a JSON string. + * + * @return string JSON-encoded string representation of the object. + * @throws Exception If encoding fails. + */ + public function toJson(): string + { + $serializedObject = $this->jsonSerialize(); + $encoded = JsonEncoder::encode($serializedObject); + if (!$encoded) { + throw new Exception("Could not encode type"); + } + return $encoded; + } + + /** + * Serializes the object to an array. + * + * @return mixed[] Array representation of the object. + * @throws JsonException If serialization fails. + */ + public function jsonSerialize(): array + { + $result = []; + $reflectionClass = new \ReflectionClass($this); + foreach ($reflectionClass->getProperties() as $property) { + $jsonKey = self::getJsonKey($property); + if ($jsonKey == null) { + continue; + } + $value = $property->getValue($this); + + // Handle DateTime properties + $dateTypeAttr = $property->getAttributes(Date::class)[0] ?? null; + if ($dateTypeAttr && $value instanceof DateTime) { + $dateType = $dateTypeAttr->newInstance()->type; + $value = ($dateType === Date::TYPE_DATE) + ? JsonSerializer::serializeDate($value) + : JsonSerializer::serializeDateTime($value); + } + + // Handle Union annotations + $unionTypeAttr = $property->getAttributes(Union::class)[0] ?? null; + if ($unionTypeAttr) { + $unionType = $unionTypeAttr->newInstance(); + $value = JsonSerializer::serializeUnion($value, $unionType); + } + + // Handle arrays with type annotations + $arrayTypeAttr = $property->getAttributes(ArrayType::class)[0] ?? null; + if ($arrayTypeAttr && is_array($value)) { + $arrayType = $arrayTypeAttr->newInstance()->type; + $value = JsonSerializer::serializeArray($value, $arrayType); + } + + // Handle object + if (is_object($value)) { + $value = JsonSerializer::serializeObject($value); + } + + if ($value !== null) { + $result[$jsonKey] = $value; + } + } + return $result; + } + + /** + * Deserializes a JSON string into an instance of the calling class. + * + * @param string $json JSON string to deserialize. + * @return static Deserialized object. + * @throws JsonException If decoding fails or the result is not an array. + * @throws Exception If deserialization fails. + */ + public static function fromJson(string $json): static + { + $decodedJson = JsonDecoder::decode($json); + if (!is_array($decodedJson)) { + throw new JsonException("Unexpected non-array decoded type: " . gettype($decodedJson)); + } + return self::jsonDeserialize($decodedJson); + } + + /** + * Deserializes an array into an instance of the calling class. + * + * @param array $data Array data to deserialize. + * @return static Deserialized object. + * @throws JsonException If deserialization fails. + */ + public static function jsonDeserialize(array $data): static + { + $reflectionClass = new \ReflectionClass(static::class); + $constructor = $reflectionClass->getConstructor(); + if ($constructor === null) { + throw new JsonException("No constructor found."); + } + + $args = []; + $properties = []; + $additionalProperties = []; + foreach ($reflectionClass->getProperties() as $property) { + $jsonKey = self::getJsonKey($property) ?? $property->getName(); + $properties[$jsonKey] = $property; + } + + foreach ($data as $jsonKey => $value) { + if (!isset($properties[$jsonKey])) { + // This JSON key doesn't map to any class property - add it to additionalProperties + $additionalProperties[$jsonKey] = $value; + continue; + } + + $property = $properties[$jsonKey]; + + // Handle Date annotation + $dateTypeAttr = $property->getAttributes(Date::class)[0] ?? null; + if ($dateTypeAttr) { + $dateType = $dateTypeAttr->newInstance()->type; + if (!is_string($value)) { + throw new JsonException("Unexpected non-string type for date."); + } + $value = ($dateType === Date::TYPE_DATE) + ? JsonDeserializer::deserializeDate($value) + : JsonDeserializer::deserializeDateTime($value); + } + + // Handle Array annotation + $arrayTypeAttr = $property->getAttributes(ArrayType::class)[0] ?? null; + if (is_array($value) && $arrayTypeAttr) { + $arrayType = $arrayTypeAttr->newInstance()->type; + $value = JsonDeserializer::deserializeArray($value, $arrayType); + } + + // Handle Union annotations + $unionTypeAttr = $property->getAttributes(Union::class)[0] ?? null; + if ($unionTypeAttr) { + $unionType = $unionTypeAttr->newInstance(); + $value = JsonDeserializer::deserializeUnion($value, $unionType); + } + + // Handle object + $type = $property->getType(); + if (is_array($value) && $type instanceof ReflectionNamedType && !$type->isBuiltin()) { + $value = JsonDeserializer::deserializeObject($value, $type->getName()); + } + + $args[$property->getName()] = $value; + } + + // Fill in any missing properties with defaults + foreach ($properties as $property) { + if (!isset($args[$property->getName()])) { + $args[$property->getName()] = $property->getDefaultValue() ?? null; + } + } + + // @phpstan-ignore-next-line + $result = new static($args); + $result->__additionalProperties = $additionalProperties; + return $result; + } + + /** + * Get properties from JSON that weren't mapped to class fields + * @return array + */ + public function getAdditionalProperties(): array + { + return $this->__additionalProperties; + } + + /** + * Retrieves the JSON key associated with a property. + * + * @param ReflectionProperty $property The reflection property. + * @return ?string The JSON key, or null if not available. + */ + private static function getJsonKey(ReflectionProperty $property): ?string + { + $jsonPropertyAttr = $property->getAttributes(JsonProperty::class)[0] ?? null; + return $jsonPropertyAttr?->newInstance()?->name; + } +} diff --git a/seed/php-model/inferred-auth-implicit-reference/src/Core/Json/JsonSerializer.php b/seed/php-model/inferred-auth-implicit-reference/src/Core/Json/JsonSerializer.php new file mode 100644 index 000000000000..36dc06dfcf50 --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-reference/src/Core/Json/JsonSerializer.php @@ -0,0 +1,197 @@ +format(Constant::DateFormat); + } + + /** + * Serializes a DateTime object into a string using the date-time format. + * Normalizes UTC times to use 'Z' suffix instead of '+00:00'. + * + * @param DateTime $date The DateTime object to serialize. + * @return string The serialized date-time string. + */ + public static function serializeDateTime(DateTime $date): string + { + $formatted = $date->format(Constant::DateTimeFormat); + if (str_ends_with($formatted, '+00:00')) { + return substr($formatted, 0, -6) . 'Z'; + } + return $formatted; + } + + /** + * Serializes an array based on type annotations (either a list or map). + * + * @param mixed[]|array $data The array to be serialized. + * @param mixed[]|array $type The type definition from the annotation. + * @return mixed[]|array The serialized array. + * @throws JsonException If serialization fails. + */ + public static function serializeArray(array $data, array $type): array + { + return Utils::isMapType($type) + ? self::serializeMap($data, $type) + : self::serializeList($data, $type); + } + + /** + * Serializes a value based on its type definition. + * + * @param mixed $data The value to serialize. + * @param mixed $type The type definition. + * @return mixed The serialized value. + * @throws JsonException If serialization fails. + */ + private static function serializeValue(mixed $data, mixed $type): mixed + { + if ($type instanceof Union) { + return self::serializeUnion($data, $type); + } + + if (is_array($type)) { + return self::serializeArray((array)$data, $type); + } + + if (gettype($type) != "string") { + throw new JsonException("Unexpected non-string type."); + } + + return self::serializeSingleValue($data, $type); + } + + /** + * Serializes a value for a union type definition. + * + * @param mixed $data The value to serialize. + * @param Union $unionType The union type definition. + * @return mixed The serialized value. + * @throws JsonException If serialization fails for all union types. + */ + public static function serializeUnion(mixed $data, Union $unionType): mixed + { + foreach ($unionType->types as $type) { + try { + return self::serializeValue($data, $type); + } catch (Exception) { + // Try the next type in the union + continue; + } + } + $readableType = Utils::getReadableType($data); + throw new JsonException( + "Cannot serialize value of type $readableType with any of the union types: " . $unionType + ); + } + + /** + * Serializes a single value based on its type. + * + * @param mixed $data The value to serialize. + * @param string $type The expected type. + * @return mixed The serialized value. + * @throws JsonException If serialization fails. + */ + private static function serializeSingleValue(mixed $data, string $type): mixed + { + if ($type === 'null' && $data === null) { + return null; + } + + if (($type === 'date' || $type === 'datetime') && $data instanceof DateTime) { + return $type === 'date' ? self::serializeDate($data) : self::serializeDateTime($data); + } + + if ($type === 'mixed') { + return $data; + } + + if (class_exists($type) && $data instanceof $type) { + return self::serializeObject($data); + } + + // Handle floats as a special case since gettype($data) returns "double" for float values in PHP. + if ($type === 'float' && is_float($data)) { + return $data; + } + + if (gettype($data) === $type) { + return $data; + } + + throw new JsonException("Unable to serialize value of type '" . gettype($data) . "' as '$type'."); + } + + /** + * Serializes an object to a JSON-serializable format. + * + * @param object $data The object to serialize. + * @return mixed The serialized data. + * @throws JsonException If the object does not implement JsonSerializable. + */ + public static function serializeObject(object $data): mixed + { + if (!is_subclass_of($data, JsonSerializable::class)) { + $type = get_class($data); + throw new JsonException("Class $type must implement JsonSerializable."); + } + return $data->jsonSerialize(); + } + + /** + * Serializes a map (associative array) with defined key and value types. + * + * @param array $data The associative array to serialize. + * @param array $type The type definition for the map. + * @return array The serialized map. + * @throws JsonException If serialization fails. + */ + private static function serializeMap(array $data, array $type): array + { + $keyType = array_key_first($type); + if ($keyType === null) { + throw new JsonException("Unexpected no key in ArrayType."); + } + $valueType = $type[$keyType]; + $result = []; + + foreach ($data as $key => $item) { + $key = Utils::castKey($key, $keyType); + $result[$key] = self::serializeValue($item, $valueType); + } + + return $result; + } + + /** + * Serializes a list (indexed array) where only the value type is defined. + * + * @param array $data The list to serialize. + * @param array $type The type definition for the list. + * @return array The serialized list. + * @throws JsonException If serialization fails. + */ + private static function serializeList(array $data, array $type): array + { + $valueType = $type[0]; + return array_map(fn ($item) => self::serializeValue($item, $valueType), $data); + } +} diff --git a/seed/php-model/inferred-auth-implicit-reference/src/Core/Json/Utils.php b/seed/php-model/inferred-auth-implicit-reference/src/Core/Json/Utils.php new file mode 100644 index 000000000000..7577c058916d --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-reference/src/Core/Json/Utils.php @@ -0,0 +1,61 @@ + $type The type definition from the annotation. + * @return bool True if the type is a map, false if it's a list. + */ + public static function isMapType(array $type): bool + { + return count($type) === 1 && !array_is_list($type); + } + + /** + * Casts the key to the appropriate type based on the key type. + * + * @param mixed $key The key to be cast. + * @param string $keyType The type to cast the key to ('string', 'integer', 'float'). + * @return mixed The casted key. + * @throws JsonException + */ + public static function castKey(mixed $key, string $keyType): mixed + { + if (!is_scalar($key)) { + throw new JsonException("Key must be a scalar type."); + } + return match ($keyType) { + 'integer' => (int)$key, + 'float' => (float)$key, + 'string' => (string)$key, + default => $key, + }; + } + + /** + * Returns a human-readable representation of the input's type. + * + * @param mixed $input The input value to determine the type of. + * @return string A readable description of the input type. + */ + public static function getReadableType(mixed $input): string + { + if (is_object($input)) { + return get_class($input); + } elseif (is_array($input)) { + return 'array(' . count($input) . ' items)'; + } elseif (is_null($input)) { + return 'null'; + } else { + return gettype($input); + } + } +} diff --git a/seed/php-model/inferred-auth-implicit-reference/src/Core/Types/ArrayType.php b/seed/php-model/inferred-auth-implicit-reference/src/Core/Types/ArrayType.php new file mode 100644 index 000000000000..a26d29008ec3 --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-reference/src/Core/Types/ArrayType.php @@ -0,0 +1,16 @@ + 'valueType'] for maps, or ['valueType'] for lists + */ + public function __construct(public array $type) + { + } +} diff --git a/seed/php-model/inferred-auth-implicit-reference/src/Core/Types/Constant.php b/seed/php-model/inferred-auth-implicit-reference/src/Core/Types/Constant.php new file mode 100644 index 000000000000..5ac4518cc6d6 --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-reference/src/Core/Types/Constant.php @@ -0,0 +1,12 @@ +> The types allowed for this property, which can be strings, arrays, or nested Union types. + */ + public array $types; + + /** + * Constructor for the Union attribute. + * + * @param string|Union|array ...$types The list of types that the property can accept. + * This can include primitive types (e.g., 'string', 'int'), arrays, or other Union instances. + * + * Example: + * ```php + * #[Union('string', 'null', 'date', new Union('boolean', 'int'))] + * ``` + */ + public function __construct(string|Union|array ...$types) + { + $this->types = $types; + } + + /** + * Converts the Union type to a string representation. + * + * @return string A string representation of the union types. + */ + public function __toString(): string + { + return implode(' | ', array_map(function ($type) { + if (is_string($type)) { + return $type; + } elseif ($type instanceof Union) { + return (string) $type; // Recursively handle nested unions + } elseif (is_array($type)) { + return 'array'; // Handle arrays + } + }, $this->types)); + } +} diff --git a/seed/php-model/inferred-auth-implicit-reference/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/inferred-auth-implicit-reference/tests/Core/Json/AdditionalPropertiesTest.php new file mode 100644 index 000000000000..5bcb7ba283a8 --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-reference/tests/Core/Json/AdditionalPropertiesTest.php @@ -0,0 +1,76 @@ +name; + } + + /** + * @return string|null + */ + public function getEmail(): ?string + { + return $this->email; + } + + /** + * @param array{ + * name: string, + * email?: string|null, + * } $values + */ + public function __construct( + array $values, + ) { + $this->name = $values['name']; + $this->email = $values['email'] ?? null; + } +} + +class AdditionalPropertiesTest extends TestCase +{ + public function testExtraProperties(): void + { + $expectedJson = json_encode( + [ + 'name' => 'john.doe', + 'email' => 'john.doe@example.com', + 'age' => 42 + ], + JSON_THROW_ON_ERROR + ); + + $person = Person::fromJson($expectedJson); + $this->assertEquals('john.doe', $person->getName()); + $this->assertEquals('john.doe@example.com', $person->getEmail()); + $this->assertEquals( + [ + 'age' => 42 + ], + $person->getAdditionalProperties(), + ); + } +} diff --git a/seed/php-model/inferred-auth-implicit-reference/tests/Core/Json/DateArrayTest.php b/seed/php-model/inferred-auth-implicit-reference/tests/Core/Json/DateArrayTest.php new file mode 100644 index 000000000000..a72cfdbdd227 --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-reference/tests/Core/Json/DateArrayTest.php @@ -0,0 +1,54 @@ +dates = $values['dates']; + } +} + +class DateArrayTest extends TestCase +{ + public function testDateTimeInArrays(): void + { + $expectedJson = json_encode( + [ + 'dates' => ['2023-01-01', '2023-02-01', '2023-03-01'] + ], + JSON_THROW_ON_ERROR + ); + + $object = DateArray::fromJson($expectedJson); + $this->assertInstanceOf(DateTime::class, $object->dates[0], 'dates[0] should be a DateTime instance.'); + $this->assertEquals('2023-01-01', $object->dates[0]->format('Y-m-d'), 'dates[0] should have the correct date.'); + $this->assertInstanceOf(DateTime::class, $object->dates[1], 'dates[1] should be a DateTime instance.'); + $this->assertEquals('2023-02-01', $object->dates[1]->format('Y-m-d'), 'dates[1] should have the correct date.'); + $this->assertInstanceOf(DateTime::class, $object->dates[2], 'dates[2] should be a DateTime instance.'); + $this->assertEquals('2023-03-01', $object->dates[2]->format('Y-m-d'), 'dates[2] should have the correct date.'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for dates array.'); + } +} diff --git a/seed/php-model/inferred-auth-implicit-reference/tests/Core/Json/EmptyArrayTest.php b/seed/php-model/inferred-auth-implicit-reference/tests/Core/Json/EmptyArrayTest.php new file mode 100644 index 000000000000..d243a08916d4 --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-reference/tests/Core/Json/EmptyArrayTest.php @@ -0,0 +1,71 @@ + $emptyMapArray + */ + #[JsonProperty('empty_map_array')] + #[ArrayType(['integer' => new Union('string', 'null')])] + public array $emptyMapArray; + + /** + * @var array $emptyDatesArray + */ + #[ArrayType([new Union('date', 'null')])] + #[JsonProperty('empty_dates_array')] + public array $emptyDatesArray; + + /** + * @param array{ + * emptyStringArray: string[], + * emptyMapArray: array, + * emptyDatesArray: array, + * } $values + */ + public function __construct( + array $values, + ) { + $this->emptyStringArray = $values['emptyStringArray']; + $this->emptyMapArray = $values['emptyMapArray']; + $this->emptyDatesArray = $values['emptyDatesArray']; + } +} + +class EmptyArrayTest extends TestCase +{ + public function testEmptyArray(): void + { + $expectedJson = json_encode( + [ + 'empty_string_array' => [], + 'empty_map_array' => [], + 'empty_dates_array' => [] + ], + JSON_THROW_ON_ERROR + ); + + $object = EmptyArray::fromJson($expectedJson); + $this->assertEmpty($object->emptyStringArray, 'empty_string_array should be empty.'); + $this->assertEmpty($object->emptyMapArray, 'empty_map_array should be empty.'); + $this->assertEmpty($object->emptyDatesArray, 'empty_dates_array should be empty.'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for EmptyArraysType.'); + } +} diff --git a/seed/php-model/inferred-auth-implicit-reference/tests/Core/Json/EnumTest.php b/seed/php-model/inferred-auth-implicit-reference/tests/Core/Json/EnumTest.php new file mode 100644 index 000000000000..bf83d5b8ab0f --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-reference/tests/Core/Json/EnumTest.php @@ -0,0 +1,76 @@ +value; + } +} + +class ShapeType extends JsonSerializableType +{ + /** + * @var Shape $shape + */ + #[JsonProperty('shape')] + public Shape $shape; + + /** + * @var Shape[] $shapes + */ + #[ArrayType([Shape::class])] + #[JsonProperty('shapes')] + public array $shapes; + + /** + * @param Shape $shape + * @param Shape[] $shapes + */ + public function __construct( + Shape $shape, + array $shapes, + ) { + $this->shape = $shape; + $this->shapes = $shapes; + } +} + +class EnumTest extends TestCase +{ + public function testEnumSerialization(): void + { + $object = new ShapeType( + Shape::Circle, + [Shape::Square, Shape::Circle, Shape::Triangle] + ); + + $expectedJson = json_encode([ + 'shape' => 'CIRCLE', + 'shapes' => ['SQUARE', 'CIRCLE', 'TRIANGLE'] + ], JSON_THROW_ON_ERROR); + + $actualJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString( + $expectedJson, + $actualJson, + 'Serialized JSON does not match expected JSON for shape and shapes properties.' + ); + } +} diff --git a/seed/php-model/inferred-auth-implicit-reference/tests/Core/Json/ExhaustiveTest.php b/seed/php-model/inferred-auth-implicit-reference/tests/Core/Json/ExhaustiveTest.php new file mode 100644 index 000000000000..ed1330eda3e4 --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-reference/tests/Core/Json/ExhaustiveTest.php @@ -0,0 +1,197 @@ +nestedProperty = $values['nestedProperty']; + } +} + +class Type extends JsonSerializableType +{ + /** + * @var Nested nestedType + */ + #[JsonProperty('nested_type')] + public Nested $nestedType; /** + + * @var string $simpleProperty + */ + #[JsonProperty('simple_property')] + public string $simpleProperty; + + /** + * @var DateTime $dateProperty + */ + #[Date(Date::TYPE_DATE)] + #[JsonProperty('date_property')] + public DateTime $dateProperty; + + /** + * @var DateTime $datetimeProperty + */ + #[Date(Date::TYPE_DATETIME)] + #[JsonProperty('datetime_property')] + public DateTime $datetimeProperty; + + /** + * @var array $stringArray + */ + #[ArrayType(['string'])] + #[JsonProperty('string_array')] + public array $stringArray; + + /** + * @var array $mapProperty + */ + #[ArrayType(['string' => 'integer'])] + #[JsonProperty('map_property')] + public array $mapProperty; + + /** + * @var array $objectArray + */ + #[ArrayType(['integer' => new Union(Nested::class, 'null')])] + #[JsonProperty('object_array')] + public array $objectArray; + + /** + * @var array> $nestedArray + */ + #[ArrayType(['integer' => ['integer' => new Union('string', 'null')]])] + #[JsonProperty('nested_array')] + public array $nestedArray; + + /** + * @var array $datesArray + */ + #[ArrayType([new Union('date', 'null')])] + #[JsonProperty('dates_array')] + public array $datesArray; + + /** + * @var string|null $nullableProperty + */ + #[JsonProperty('nullable_property')] + public ?string $nullableProperty; + + /** + * @param array{ + * nestedType: Nested, + * simpleProperty: string, + * dateProperty: DateTime, + * datetimeProperty: DateTime, + * stringArray: array, + * mapProperty: array, + * objectArray: array, + * nestedArray: array>, + * datesArray: array, + * nullableProperty?: string|null, + * } $values + */ + public function __construct( + array $values, + ) { + $this->nestedType = $values['nestedType']; + $this->simpleProperty = $values['simpleProperty']; + $this->dateProperty = $values['dateProperty']; + $this->datetimeProperty = $values['datetimeProperty']; + $this->stringArray = $values['stringArray']; + $this->mapProperty = $values['mapProperty']; + $this->objectArray = $values['objectArray']; + $this->nestedArray = $values['nestedArray']; + $this->datesArray = $values['datesArray']; + $this->nullableProperty = $values['nullableProperty'] ?? null; + } +} + +class ExhaustiveTest extends TestCase +{ + /** + * Test serialization and deserialization of all types in Type. + */ + public function testExhaustive(): void + { + $expectedJson = json_encode( + [ + 'nested_type' => ['nested_property' => '1995-07-20'], + 'simple_property' => 'Test String', + // Omit 'nullable_property' to test null serialization + 'date_property' => '2023-01-01', + 'datetime_property' => '2023-01-01T12:34:56Z', + 'string_array' => ['one', 'two', 'three'], + 'map_property' => ['key1' => 1, 'key2' => 2], + 'object_array' => [ + 1 => ['nested_property' => '2021-07-20'], + 2 => null, // Testing nullable objects in array + ], + 'nested_array' => [ + 1 => [1 => 'value1', 2 => null], // Testing nullable strings in nested array + 2 => [3 => 'value3', 4 => 'value4'] + ], + 'dates_array' => ['2023-01-01', null, '2023-03-01'] // Testing nullable dates in array> + ], + JSON_THROW_ON_ERROR + ); + + $object = Type::fromJson($expectedJson); + + // Check that nullable property is null and not included in JSON + $this->assertNull($object->nullableProperty, 'Nullable property should be null.'); + + // Check date properties + $this->assertInstanceOf(DateTime::class, $object->dateProperty, 'date_property should be a DateTime instance.'); + $this->assertEquals('2023-01-01', $object->dateProperty->format('Y-m-d'), 'date_property should have the correct date.'); + $this->assertInstanceOf(DateTime::class, $object->datetimeProperty, 'datetime_property should be a DateTime instance.'); + $this->assertEquals('2023-01-01 12:34:56', $object->datetimeProperty->format('Y-m-d H:i:s'), 'datetime_property should have the correct datetime.'); + + // Check scalar arrays + $this->assertEquals(['one', 'two', 'three'], $object->stringArray, 'string_array should match the original data.'); + $this->assertEquals(['key1' => 1, 'key2' => 2], $object->mapProperty, 'map_property should match the original data.'); + + // Check object array with nullable elements + $this->assertInstanceOf(Nested::class, $object->objectArray[1], 'object_array[1] should be an instance of TestNestedType1.'); + $this->assertEquals('2021-07-20', $object->objectArray[1]->nestedProperty->format('Y-m-d'), 'object_array[1]->nestedProperty should match the original data.'); + $this->assertNull($object->objectArray[2], 'object_array[2] should be null.'); + + // Check nested array with nullable strings + $this->assertEquals('value1', $object->nestedArray[1][1], 'nested_array[1][1] should match the original data.'); + $this->assertNull($object->nestedArray[1][2], 'nested_array[1][2] should be null.'); + $this->assertEquals('value3', $object->nestedArray[2][3], 'nested_array[2][3] should match the original data.'); + $this->assertEquals('value4', $object->nestedArray[2][4], 'nested_array[2][4] should match the original data.'); + + // Check dates array with nullable DateTime objects + $this->assertInstanceOf(DateTime::class, $object->datesArray[0], 'dates_array[0] should be a DateTime instance.'); + $this->assertEquals('2023-01-01', $object->datesArray[0]->format('Y-m-d'), 'dates_array[0] should have the correct date.'); + $this->assertNull($object->datesArray[1], 'dates_array[1] should be null.'); + $this->assertInstanceOf(DateTime::class, $object->datesArray[2], 'dates_array[2] should be a DateTime instance.'); + $this->assertEquals('2023-03-01', $object->datesArray[2]->format('Y-m-d'), 'dates_array[2] should have the correct date.'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'The serialized JSON does not match the original JSON.'); + } +} diff --git a/seed/php-model/inferred-auth-implicit-reference/tests/Core/Json/InvalidTest.php b/seed/php-model/inferred-auth-implicit-reference/tests/Core/Json/InvalidTest.php new file mode 100644 index 000000000000..7d1d79406a5a --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-reference/tests/Core/Json/InvalidTest.php @@ -0,0 +1,42 @@ +integerProperty = $values['integerProperty']; + } +} + +class InvalidTest extends TestCase +{ + public function testInvalidJsonThrowsException(): void + { + $this->expectException(\TypeError::class); + $json = json_encode( + [ + 'integer_property' => 'not_an_integer' + ], + JSON_THROW_ON_ERROR + ); + Invalid::fromJson($json); + } +} diff --git a/seed/php-model/inferred-auth-implicit-reference/tests/Core/Json/NestedUnionArrayTest.php b/seed/php-model/inferred-auth-implicit-reference/tests/Core/Json/NestedUnionArrayTest.php new file mode 100644 index 000000000000..0fcdd06667e5 --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-reference/tests/Core/Json/NestedUnionArrayTest.php @@ -0,0 +1,89 @@ +nestedProperty = $values['nestedProperty']; + } +} + +class NestedUnionArray extends JsonSerializableType +{ + /** + * @var array> $nestedArray + */ + #[ArrayType(['integer' => ['integer' => new Union(UnionObject::class, 'null', 'date')]])] + #[JsonProperty('nested_array')] + public array $nestedArray; + + /** + * @param array{ + * nestedArray: array>, + * } $values + */ + public function __construct( + array $values, + ) { + $this->nestedArray = $values['nestedArray']; + } +} + +class NestedUnionArrayTest extends TestCase +{ + public function testNestedUnionArray(): void + { + $expectedJson = json_encode( + [ + 'nested_array' => [ + 1 => [ + 1 => ['nested_property' => 'Nested One'], + 2 => null, + 4 => '2023-01-02' + ], + 2 => [ + 5 => ['nested_property' => 'Nested Two'], + 7 => '2023-02-02' + ] + ] + ], + JSON_THROW_ON_ERROR + ); + + $object = NestedUnionArray::fromJson($expectedJson); + $this->assertInstanceOf(UnionObject::class, $object->nestedArray[1][1], 'nested_array[1][1] should be an instance of Object.'); + $this->assertEquals('Nested One', $object->nestedArray[1][1]->nestedProperty, 'nested_array[1][1]->nestedProperty should match the original data.'); + $this->assertNull($object->nestedArray[1][2], 'nested_array[1][2] should be null.'); + $this->assertInstanceOf(DateTime::class, $object->nestedArray[1][4], 'nested_array[1][4] should be a DateTime instance.'); + $this->assertEquals('2023-01-02T00:00:00+00:00', $object->nestedArray[1][4]->format(Constant::DateTimeFormat), 'nested_array[1][4] should have the correct datetime.'); + $this->assertInstanceOf(UnionObject::class, $object->nestedArray[2][5], 'nested_array[2][5] should be an instance of Object.'); + $this->assertEquals('Nested Two', $object->nestedArray[2][5]->nestedProperty, 'nested_array[2][5]->nestedProperty should match the original data.'); + $this->assertInstanceOf(DateTime::class, $object->nestedArray[2][7], 'nested_array[1][4] should be a DateTime instance.'); + $this->assertEquals('2023-02-02', $object->nestedArray[2][7]->format('Y-m-d'), 'nested_array[1][4] should have the correct date.'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for nested_array.'); + } +} diff --git a/seed/php-model/inferred-auth-implicit-reference/tests/Core/Json/NullPropertyTest.php b/seed/php-model/inferred-auth-implicit-reference/tests/Core/Json/NullPropertyTest.php new file mode 100644 index 000000000000..ce20a2442825 --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-reference/tests/Core/Json/NullPropertyTest.php @@ -0,0 +1,53 @@ +nonNullProperty = $values['nonNullProperty']; + $this->nullProperty = $values['nullProperty'] ?? null; + } +} + +class NullPropertyTest extends TestCase +{ + public function testNullPropertiesAreOmitted(): void + { + $object = new NullProperty( + [ + "nonNullProperty" => "Test String", + "nullProperty" => null + ] + ); + + $serialized = $object->jsonSerialize(); + $this->assertArrayHasKey('non_null_property', $serialized, 'non_null_property should be present in the serialized JSON.'); + $this->assertArrayNotHasKey('null_property', $serialized, 'null_property should be omitted from the serialized JSON.'); + $this->assertEquals('Test String', $serialized['non_null_property'], 'non_null_property should have the correct value.'); + } +} diff --git a/seed/php-model/inferred-auth-implicit-reference/tests/Core/Json/NullableArrayTest.php b/seed/php-model/inferred-auth-implicit-reference/tests/Core/Json/NullableArrayTest.php new file mode 100644 index 000000000000..fe0f19de6b13 --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-reference/tests/Core/Json/NullableArrayTest.php @@ -0,0 +1,49 @@ + $nullableStringArray + */ + #[ArrayType([new Union('string', 'null')])] + #[JsonProperty('nullable_string_array')] + public array $nullableStringArray; + + /** + * @param array{ + * nullableStringArray: array, + * } $values + */ + public function __construct( + array $values, + ) { + $this->nullableStringArray = $values['nullableStringArray']; + } +} + +class NullableArrayTest extends TestCase +{ + public function testNullableArray(): void + { + $expectedJson = json_encode( + [ + 'nullable_string_array' => ['one', null, 'three'] + ], + JSON_THROW_ON_ERROR + ); + + $object = NullableArray::fromJson($expectedJson); + $this->assertEquals(['one', null, 'three'], $object->nullableStringArray, 'nullable_string_array should match the original data.'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for nullable_string_array.'); + } +} diff --git a/seed/php-model/inferred-auth-implicit-reference/tests/Core/Json/ScalarTest.php b/seed/php-model/inferred-auth-implicit-reference/tests/Core/Json/ScalarTest.php new file mode 100644 index 000000000000..604b7c0b959b --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-reference/tests/Core/Json/ScalarTest.php @@ -0,0 +1,116 @@ + $intFloatArray + */ + #[ArrayType([new Union('integer', 'float')])] + #[JsonProperty('int_float_array')] + public array $intFloatArray; + + /** + * @var array $floatArray + */ + #[ArrayType(['float'])] + #[JsonProperty('float_array')] + public array $floatArray; + + /** + * @var bool|null $nullableBooleanProperty + */ + #[JsonProperty('nullable_boolean_property')] + public ?bool $nullableBooleanProperty; + + /** + * @param array{ + * integerProperty: int, + * floatProperty: float, + * otherFloatProperty: float, + * booleanProperty: bool, + * stringProperty: string, + * intFloatArray: array, + * floatArray: array, + * nullableBooleanProperty?: bool|null, + * } $values + */ + public function __construct( + array $values, + ) { + $this->integerProperty = $values['integerProperty']; + $this->floatProperty = $values['floatProperty']; + $this->otherFloatProperty = $values['otherFloatProperty']; + $this->booleanProperty = $values['booleanProperty']; + $this->stringProperty = $values['stringProperty']; + $this->intFloatArray = $values['intFloatArray']; + $this->floatArray = $values['floatArray']; + $this->nullableBooleanProperty = $values['nullableBooleanProperty'] ?? null; + } +} + +class ScalarTest extends TestCase +{ + public function testAllScalarTypesIncludingFloat(): void + { + $expectedJson = json_encode( + [ + 'integer_property' => 42, + 'float_property' => 3.14159, + 'other_float_property' => 3, + 'boolean_property' => true, + 'string_property' => 'Hello, World!', + 'int_float_array' => [1, 2.5, 3, 4.75], + 'float_array' => [1, 2, 3, 4] // Ensure we handle "integer-looking" floats + ], + JSON_THROW_ON_ERROR + ); + + $object = Scalar::fromJson($expectedJson); + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals(3.14159, $object->floatProperty, 'float_property should be 3.14159.'); + $this->assertTrue($object->booleanProperty, 'boolean_property should be true.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + $this->assertNull($object->nullableBooleanProperty, 'nullable_boolean_property should be null.'); + $this->assertEquals([1, 2.5, 3, 4.75], $object->intFloatArray, 'int_float_array should match the original data.'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTest.'); + } +} diff --git a/seed/php-model/inferred-auth-implicit-reference/tests/Core/Json/TraitTest.php b/seed/php-model/inferred-auth-implicit-reference/tests/Core/Json/TraitTest.php new file mode 100644 index 000000000000..837f239115f7 --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-reference/tests/Core/Json/TraitTest.php @@ -0,0 +1,60 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $expectedJson = json_encode( + [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ], + JSON_THROW_ON_ERROR + ); + + $object = TypeWithTrait::fromJson($expectedJson); + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + } +} diff --git a/seed/php-model/inferred-auth-implicit-reference/tests/Core/Json/UnionArrayTest.php b/seed/php-model/inferred-auth-implicit-reference/tests/Core/Json/UnionArrayTest.php new file mode 100644 index 000000000000..33899cd3f051 --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-reference/tests/Core/Json/UnionArrayTest.php @@ -0,0 +1,57 @@ + $mixedDates + */ + #[ArrayType(['integer' => new Union('datetime', 'string', 'null')])] + #[JsonProperty('mixed_dates')] + public array $mixedDates; + + /** + * @param array{ + * mixedDates: array, + * } $values + */ + public function __construct( + array $values, + ) { + $this->mixedDates = $values['mixedDates']; + } +} + +class UnionArrayTest extends TestCase +{ + public function testUnionArray(): void + { + $expectedJson = json_encode( + [ + 'mixed_dates' => [ + 1 => '2023-01-01T12:00:00Z', + 2 => null, + 3 => 'Some String' + ] + ], + JSON_THROW_ON_ERROR + ); + + $object = UnionArray::fromJson($expectedJson); + $this->assertInstanceOf(DateTime::class, $object->mixedDates[1], 'mixed_dates[1] should be a DateTime instance.'); + $this->assertEquals('2023-01-01 12:00:00', $object->mixedDates[1]->format('Y-m-d H:i:s'), 'mixed_dates[1] should have the correct datetime.'); + $this->assertNull($object->mixedDates[2], 'mixed_dates[2] should be null.'); + $this->assertEquals('Some String', $object->mixedDates[3], 'mixed_dates[3] should be "Some String".'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for mixed_dates.'); + } +} diff --git a/seed/php-model/inferred-auth-implicit-reference/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/inferred-auth-implicit-reference/tests/Core/Json/UnionPropertyTest.php new file mode 100644 index 000000000000..3119baace627 --- /dev/null +++ b/seed/php-model/inferred-auth-implicit-reference/tests/Core/Json/UnionPropertyTest.php @@ -0,0 +1,115 @@ + 'integer'], UnionProperty::class)] + #[JsonProperty('complexUnion')] + public mixed $complexUnion; + + /** + * @param array{ + * complexUnion: string|int|null|array|UnionProperty + * } $values + */ + public function __construct( + array $values, + ) { + $this->complexUnion = $values['complexUnion']; + } +} + +class UnionPropertyTest extends TestCase +{ + public function testWithMapOfIntToInt(): void + { + $expectedJson = json_encode( + [ + 'complexUnion' => [1 => 100, 2 => 200] + ], + JSON_THROW_ON_ERROR + ); + + $object = UnionProperty::fromJson($expectedJson); + $this->assertIsArray($object->complexUnion, 'complexUnion should be an array.'); + $this->assertEquals([1 => 100, 2 => 200], $object->complexUnion, 'complexUnion should match the original map of int => int.'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); + } + + public function testWithNestedUnionPropertyType(): void + { + $expectedJson = json_encode( + [ + 'complexUnion' => new UnionProperty( + [ + 'complexUnion' => 'Nested String' + ] + ) + ], + JSON_THROW_ON_ERROR + ); + + $object = UnionProperty::fromJson($expectedJson); + $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); + $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); + } + + public function testWithNull(): void + { + $expectedJson = json_encode( + [], + JSON_THROW_ON_ERROR + ); + + $object = UnionProperty::fromJson($expectedJson); + $this->assertNull($object->complexUnion, 'complexUnion should be null.'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); + } + + public function testWithInteger(): void + { + $expectedJson = json_encode( + [ + 'complexUnion' => 42 + ], + JSON_THROW_ON_ERROR + ); + + $object = UnionProperty::fromJson($expectedJson); + $this->assertIsInt($object->complexUnion, 'complexUnion should be an integer.'); + $this->assertEquals(42, $object->complexUnion, 'complexUnion should match the original integer.'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); + } + + public function testWithString(): void + { + $expectedJson = json_encode( + [ + 'complexUnion' => 'Some String' + ], + JSON_THROW_ON_ERROR + ); + + $object = UnionProperty::fromJson($expectedJson); + $this->assertIsString($object->complexUnion, 'complexUnion should be a string.'); + $this->assertEquals('Some String', $object->complexUnion, 'complexUnion should match the original string.'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); + } +} diff --git a/seed/php-model/inferred-auth-implicit/src/Auth/TokenResponse.php b/seed/php-model/inferred-auth-implicit/src/Auth/TokenResponse.php index 56fbe31ea0c2..9222a65c62ca 100644 --- a/seed/php-model/inferred-auth-implicit/src/Auth/TokenResponse.php +++ b/seed/php-model/inferred-auth-implicit/src/Auth/TokenResponse.php @@ -37,15 +37,17 @@ class TokenResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->accessToken = $values['accessToken'];$this->expiresIn = $values['expiresIn'];$this->refreshToken = $values['refreshToken'] ?? null; + ) { + $this->accessToken = $values['accessToken']; + $this->expiresIn = $values['expiresIn']; + $this->refreshToken = $values['refreshToken'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/inferred-auth-implicit/src/Core/Json/JsonEncoder.php b/seed/php-model/inferred-auth-implicit/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/inferred-auth-implicit/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/inferred-auth-implicit/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/inferred-auth-implicit/src/Core/Json/JsonProperty.php b/seed/php-model/inferred-auth-implicit/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/inferred-auth-implicit/src/Core/Json/JsonProperty.php +++ b/seed/php-model/inferred-auth-implicit/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/inferred-auth-implicit/src/Core/Types/ArrayType.php b/seed/php-model/inferred-auth-implicit/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/inferred-auth-implicit/src/Core/Types/ArrayType.php +++ b/seed/php-model/inferred-auth-implicit/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/inferred-auth-implicit/src/Core/Types/Constant.php b/seed/php-model/inferred-auth-implicit/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/inferred-auth-implicit/src/Core/Types/Constant.php +++ b/seed/php-model/inferred-auth-implicit/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/inferred-auth-implicit/src/Core/Types/Union.php b/seed/php-model/inferred-auth-implicit/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/inferred-auth-implicit/src/Core/Types/Union.php +++ b/seed/php-model/inferred-auth-implicit/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/inferred-auth-implicit/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/inferred-auth-implicit/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/inferred-auth-implicit/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/inferred-auth-implicit/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/inferred-auth-implicit/tests/Core/Json/EnumTest.php b/seed/php-model/inferred-auth-implicit/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/inferred-auth-implicit/tests/Core/Json/EnumTest.php +++ b/seed/php-model/inferred-auth-implicit/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/inferred-auth-implicit/tests/Core/Json/TraitTest.php b/seed/php-model/inferred-auth-implicit/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/inferred-auth-implicit/tests/Core/Json/TraitTest.php +++ b/seed/php-model/inferred-auth-implicit/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/inferred-auth-implicit/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/inferred-auth-implicit/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/inferred-auth-implicit/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/inferred-auth-implicit/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/license/src/Core/Json/JsonEncoder.php b/seed/php-model/license/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/license/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/license/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/license/src/Core/Json/JsonProperty.php b/seed/php-model/license/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/license/src/Core/Json/JsonProperty.php +++ b/seed/php-model/license/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/license/src/Core/Types/ArrayType.php b/seed/php-model/license/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/license/src/Core/Types/ArrayType.php +++ b/seed/php-model/license/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/license/src/Core/Types/Constant.php b/seed/php-model/license/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/license/src/Core/Types/Constant.php +++ b/seed/php-model/license/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/license/src/Core/Types/Union.php b/seed/php-model/license/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/license/src/Core/Types/Union.php +++ b/seed/php-model/license/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/license/src/Type.php b/seed/php-model/license/src/Type.php index c87f9d9ef1ba..10f53fb570ae 100644 --- a/seed/php-model/license/src/Type.php +++ b/seed/php-model/license/src/Type.php @@ -23,15 +23,15 @@ class Type extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/license/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/license/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/license/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/license/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/license/tests/Core/Json/EnumTest.php b/seed/php-model/license/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/license/tests/Core/Json/EnumTest.php +++ b/seed/php-model/license/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/license/tests/Core/Json/TraitTest.php b/seed/php-model/license/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/license/tests/Core/Json/TraitTest.php +++ b/seed/php-model/license/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/license/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/license/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/license/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/license/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/literal/src/Core/Json/JsonEncoder.php b/seed/php-model/literal/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/literal/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/literal/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/literal/src/Core/Json/JsonProperty.php b/seed/php-model/literal/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/literal/src/Core/Json/JsonProperty.php +++ b/seed/php-model/literal/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/literal/src/Core/Types/ArrayType.php b/seed/php-model/literal/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/literal/src/Core/Types/ArrayType.php +++ b/seed/php-model/literal/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/literal/src/Core/Types/Constant.php b/seed/php-model/literal/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/literal/src/Core/Types/Constant.php +++ b/seed/php-model/literal/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/literal/src/Core/Types/Union.php b/seed/php-model/literal/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/literal/src/Core/Types/Union.php +++ b/seed/php-model/literal/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/literal/src/Inlined/ANestedLiteral.php b/seed/php-model/literal/src/Inlined/ANestedLiteral.php index c6418d1371f1..ce4c42528a69 100644 --- a/seed/php-model/literal/src/Inlined/ANestedLiteral.php +++ b/seed/php-model/literal/src/Inlined/ANestedLiteral.php @@ -20,15 +20,15 @@ class ANestedLiteral extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->myLiteral = $values['myLiteral']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/literal/src/Inlined/ATopLevelLiteral.php b/seed/php-model/literal/src/Inlined/ATopLevelLiteral.php index 30eda28d85cb..08f4f16d7657 100644 --- a/seed/php-model/literal/src/Inlined/ATopLevelLiteral.php +++ b/seed/php-model/literal/src/Inlined/ATopLevelLiteral.php @@ -20,15 +20,15 @@ class ATopLevelLiteral extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->nestedLiteral = $values['nestedLiteral']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/literal/src/Inlined/DiscriminatedLiteral.php b/seed/php-model/literal/src/Inlined/DiscriminatedLiteral.php index f9b83ea5a12e..39c6df34796b 100644 --- a/seed/php-model/literal/src/Inlined/DiscriminatedLiteral.php +++ b/seed/php-model/literal/src/Inlined/DiscriminatedLiteral.php @@ -50,16 +50,17 @@ class DiscriminatedLiteral extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param string $customName * @return DiscriminatedLiteral */ - public static function customName(string $customName): DiscriminatedLiteral { + public static function customName(string $customName): DiscriminatedLiteral + { return new DiscriminatedLiteral([ 'type' => 'customName', 'value' => $customName, @@ -70,7 +71,8 @@ public static function customName(string $customName): DiscriminatedLiteral { * @param 'Bob' $defaultName * @return DiscriminatedLiteral */ - public static function defaultName(string $defaultName): DiscriminatedLiteral { + public static function defaultName(string $defaultName): DiscriminatedLiteral + { return new DiscriminatedLiteral([ 'type' => 'defaultName', 'value' => $defaultName, @@ -81,7 +83,8 @@ public static function defaultName(string $defaultName): DiscriminatedLiteral { * @param bool $george * @return DiscriminatedLiteral */ - public static function george(bool $george): DiscriminatedLiteral { + public static function george(bool $george): DiscriminatedLiteral + { return new DiscriminatedLiteral([ 'type' => 'george', 'value' => $george, @@ -92,7 +95,8 @@ public static function george(bool $george): DiscriminatedLiteral { * @param true $literalGeorge * @return DiscriminatedLiteral */ - public static function literalGeorge(bool $literalGeorge): DiscriminatedLiteral { + public static function literalGeorge(bool $literalGeorge): DiscriminatedLiteral + { return new DiscriminatedLiteral([ 'type' => 'literalGeorge', 'value' => $literalGeorge, @@ -102,101 +106,111 @@ public static function literalGeorge(bool $literalGeorge): DiscriminatedLiteral /** * @return bool */ - public function isCustomName(): bool { - return is_string($this->value)&& $this->type === 'customName'; + public function isCustomName(): bool + { + return is_string($this->value) && $this->type === 'customName'; } /** * @return string */ - public function asCustomName(): string { - if (!(is_string($this->value)&& $this->type === 'customName')){ + public function asCustomName(): string + { + if (!(is_string($this->value) && $this->type === 'customName')) { throw new Exception( "Expected customName; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isDefaultName(): bool { - return $this->value === 'Bob'&& $this->type === 'defaultName'; + public function isDefaultName(): bool + { + return $this->value === 'Bob' && $this->type === 'defaultName'; } /** * @return 'Bob' */ - public function asDefaultName(): string { - if (!($this->value === 'Bob'&& $this->type === 'defaultName')){ + public function asDefaultName(): string + { + if (!($this->value === 'Bob' && $this->type === 'defaultName')) { throw new Exception( "Expected defaultName; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isGeorge(): bool { - return is_bool($this->value)&& $this->type === 'george'; + public function isGeorge(): bool + { + return is_bool($this->value) && $this->type === 'george'; } /** * @return bool */ - public function asGeorge(): bool { - if (!(is_bool($this->value)&& $this->type === 'george')){ + public function asGeorge(): bool + { + if (!(is_bool($this->value) && $this->type === 'george')) { throw new Exception( "Expected george; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isLiteralGeorge(): bool { - return $this->value === true&& $this->type === 'literalGeorge'; + public function isLiteralGeorge(): bool + { + return $this->value === true && $this->type === 'literalGeorge'; } /** * @return true */ - public function asLiteralGeorge(): bool { - if (!($this->value === true&& $this->type === 'literalGeorge')){ + public function asLiteralGeorge(): bool + { + if (!($this->value === true && $this->type === 'literalGeorge')) { throw new Exception( "Expected literalGeorge; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'customName': $value = $this->value; $result['customName'] = $value; @@ -215,26 +229,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -243,56 +258,57 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'customName': - if (!array_key_exists('customName', $data)){ + if (!array_key_exists('customName', $data)) { throw new Exception( "JSON data is missing property 'customName'", ); } - + $args['value'] = $data['customName']; break; case 'defaultName': - if (!array_key_exists('defaultName', $data)){ + if (!array_key_exists('defaultName', $data)) { throw new Exception( "JSON data is missing property 'defaultName'", ); } - + $args['value'] = $data['defaultName']; break; case 'george': - if (!array_key_exists('george', $data)){ + if (!array_key_exists('george', $data)) { throw new Exception( "JSON data is missing property 'george'", ); } - + $args['value'] = $data['george']; break; case 'literalGeorge': - if (!array_key_exists('literalGeorge', $data)){ + if (!array_key_exists('literalGeorge', $data)) { throw new Exception( "JSON data is missing property 'literalGeorge'", ); } - + $args['value'] = $data['literalGeorge']; break; case '_unknown': @@ -300,7 +316,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/literal/src/Reference/ContainerObject.php b/seed/php-model/literal/src/Reference/ContainerObject.php index a397856d6bbd..30d932a13880 100644 --- a/seed/php-model/literal/src/Reference/ContainerObject.php +++ b/seed/php-model/literal/src/Reference/ContainerObject.php @@ -21,15 +21,15 @@ class ContainerObject extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->nestedObjects = $values['nestedObjects']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/literal/src/Reference/NestedObjectWithLiterals.php b/seed/php-model/literal/src/Reference/NestedObjectWithLiterals.php index 77b1841064ea..335900d88d4a 100644 --- a/seed/php-model/literal/src/Reference/NestedObjectWithLiterals.php +++ b/seed/php-model/literal/src/Reference/NestedObjectWithLiterals.php @@ -34,15 +34,17 @@ class NestedObjectWithLiterals extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->literal1 = $values['literal1'];$this->literal2 = $values['literal2'];$this->strProp = $values['strProp']; + ) { + $this->literal1 = $values['literal1']; + $this->literal2 = $values['literal2']; + $this->strProp = $values['strProp']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/literal/src/Reference/SendRequest.php b/seed/php-model/literal/src/Reference/SendRequest.php index 7d178b927f8c..bb7420c9fa6b 100644 --- a/seed/php-model/literal/src/Reference/SendRequest.php +++ b/seed/php-model/literal/src/Reference/SendRequest.php @@ -62,15 +62,21 @@ class SendRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->prompt = $values['prompt'];$this->query = $values['query'];$this->stream = $values['stream'];$this->ending = $values['ending'];$this->context = $values['context'];$this->maybeContext = $values['maybeContext'] ?? null;$this->containerObject = $values['containerObject']; + ) { + $this->prompt = $values['prompt']; + $this->query = $values['query']; + $this->stream = $values['stream']; + $this->ending = $values['ending']; + $this->context = $values['context']; + $this->maybeContext = $values['maybeContext'] ?? null; + $this->containerObject = $values['containerObject']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/literal/src/SendResponse.php b/seed/php-model/literal/src/SendResponse.php index 770377b821dc..740399f9f956 100644 --- a/seed/php-model/literal/src/SendResponse.php +++ b/seed/php-model/literal/src/SendResponse.php @@ -34,15 +34,17 @@ class SendResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->message = $values['message'];$this->status = $values['status'];$this->success = $values['success']; + ) { + $this->message = $values['message']; + $this->status = $values['status']; + $this->success = $values['success']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/literal/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/literal/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/literal/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/literal/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/literal/tests/Core/Json/EnumTest.php b/seed/php-model/literal/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/literal/tests/Core/Json/EnumTest.php +++ b/seed/php-model/literal/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/literal/tests/Core/Json/TraitTest.php b/seed/php-model/literal/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/literal/tests/Core/Json/TraitTest.php +++ b/seed/php-model/literal/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/literal/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/literal/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/literal/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/literal/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/literals-unions/src/Core/Json/JsonEncoder.php b/seed/php-model/literals-unions/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/literals-unions/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/literals-unions/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/literals-unions/src/Core/Json/JsonProperty.php b/seed/php-model/literals-unions/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/literals-unions/src/Core/Json/JsonProperty.php +++ b/seed/php-model/literals-unions/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/literals-unions/src/Core/Types/ArrayType.php b/seed/php-model/literals-unions/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/literals-unions/src/Core/Types/ArrayType.php +++ b/seed/php-model/literals-unions/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/literals-unions/src/Core/Types/Constant.php b/seed/php-model/literals-unions/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/literals-unions/src/Core/Types/Constant.php +++ b/seed/php-model/literals-unions/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/literals-unions/src/Core/Types/Union.php b/seed/php-model/literals-unions/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/literals-unions/src/Core/Types/Union.php +++ b/seed/php-model/literals-unions/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/literals-unions/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/literals-unions/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/literals-unions/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/literals-unions/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/literals-unions/tests/Core/Json/EnumTest.php b/seed/php-model/literals-unions/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/literals-unions/tests/Core/Json/EnumTest.php +++ b/seed/php-model/literals-unions/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/literals-unions/tests/Core/Json/TraitTest.php b/seed/php-model/literals-unions/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/literals-unions/tests/Core/Json/TraitTest.php +++ b/seed/php-model/literals-unions/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/literals-unions/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/literals-unions/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/literals-unions/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/literals-unions/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/mixed-case/src/Core/Json/JsonEncoder.php b/seed/php-model/mixed-case/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/mixed-case/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/mixed-case/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/mixed-case/src/Core/Json/JsonProperty.php b/seed/php-model/mixed-case/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/mixed-case/src/Core/Json/JsonProperty.php +++ b/seed/php-model/mixed-case/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/mixed-case/src/Core/Types/ArrayType.php b/seed/php-model/mixed-case/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/mixed-case/src/Core/Types/ArrayType.php +++ b/seed/php-model/mixed-case/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/mixed-case/src/Core/Types/Constant.php b/seed/php-model/mixed-case/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/mixed-case/src/Core/Types/Constant.php +++ b/seed/php-model/mixed-case/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/mixed-case/src/Core/Types/Union.php b/seed/php-model/mixed-case/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/mixed-case/src/Core/Types/Union.php +++ b/seed/php-model/mixed-case/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/mixed-case/src/Service/NestedUser.php b/seed/php-model/mixed-case/src/Service/NestedUser.php index eeff08339d67..6486de2016f3 100644 --- a/seed/php-model/mixed-case/src/Service/NestedUser.php +++ b/seed/php-model/mixed-case/src/Service/NestedUser.php @@ -27,15 +27,16 @@ class NestedUser extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->nestedUser = $values['nestedUser']; + ) { + $this->name = $values['name']; + $this->nestedUser = $values['nestedUser']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/mixed-case/src/Service/Organization.php b/seed/php-model/mixed-case/src/Service/Organization.php index 923ffca473f1..9b71e50b24a0 100644 --- a/seed/php-model/mixed-case/src/Service/Organization.php +++ b/seed/php-model/mixed-case/src/Service/Organization.php @@ -20,15 +20,15 @@ class Organization extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/mixed-case/src/Service/Resource.php b/seed/php-model/mixed-case/src/Service/Resource.php index ecac029e0b88..ec68120dbe48 100644 --- a/seed/php-model/mixed-case/src/Service/Resource.php +++ b/seed/php-model/mixed-case/src/Service/Resource.php @@ -50,9 +50,10 @@ class Resource extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->status = $values['status'];$this->resourceType = $values['resourceType'];$this->value = $values['value']; + ) { + $this->status = $values['status']; + $this->resourceType = $values['resourceType']; + $this->value = $values['value']; } /** @@ -60,7 +61,8 @@ private function __construct( * @param User $user * @return Resource */ - public static function user(string $status, User $user): Resource { + public static function user(string $status, User $user): Resource + { return new Resource([ 'status' => $status, 'resourceType' => 'user', @@ -73,7 +75,8 @@ public static function user(string $status, User $user): Resource { * @param Organization $organization * @return Resource */ - public static function organization(string $status, Organization $organization): Resource { + public static function organization(string $status, Organization $organization): Resource + { return new Resource([ 'status' => $status, 'resourceType' => 'Organization', @@ -84,61 +87,67 @@ public static function organization(string $status, Organization $organization): /** * @return bool */ - public function isUser(): bool { - return $this->value instanceof User&& $this->resourceType === 'user'; + public function isUser(): bool + { + return $this->value instanceof User && $this->resourceType === 'user'; } /** * @return User */ - public function asUser(): User { - if (!($this->value instanceof User&& $this->resourceType === 'user')){ + public function asUser(): User + { + if (!($this->value instanceof User && $this->resourceType === 'user')) { throw new Exception( "Expected user; got " . $this->resourceType . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isOrganization(): bool { - return $this->value instanceof Organization&& $this->resourceType === 'Organization'; + public function isOrganization(): bool + { + return $this->value instanceof Organization && $this->resourceType === 'Organization'; } /** * @return Organization */ - public function asOrganization(): Organization { - if (!($this->value instanceof Organization&& $this->resourceType === 'Organization')){ + public function asOrganization(): Organization + { + if (!($this->value instanceof Organization && $this->resourceType === 'Organization')) { throw new Exception( "Expected Organization; got " . $this->resourceType . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['resource_type'] = $this->resourceType; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->resourceType){ + + switch ($this->resourceType) { case 'user': $value = $this->asUser()->jsonSerialize(); $result = array_merge($value, $result); @@ -149,26 +158,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -177,34 +187,35 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('status', $data)){ + if (!array_key_exists('status', $data)) { throw new Exception( "JSON data is missing property 'status'", ); } - if (!($data['status'] instanceof ResourceStatus)){ + if (!($data['status'] instanceof ResourceStatus)) { throw new Exception( "Expected property 'status' in JSON data to be enumString, instead received " . get_debug_type($data['status']), ); } $args['status'] = $data['status']; - - if (!array_key_exists('resource_type', $data)){ + + if (!array_key_exists('resource_type', $data)) { throw new Exception( "JSON data is missing property 'resource_type'", ); } $resourceType = $data['resource_type']; - if (!(is_string($resourceType))){ + if (!(is_string($resourceType))) { throw new Exception( "Expected property 'resourceType' in JSON data to be string, instead received " . get_debug_type($data['resource_type']), ); } - + $args['resourceType'] = $resourceType; - switch ($resourceType){ + switch ($resourceType) { case 'user': $args['value'] = User::jsonDeserialize($data); break; @@ -216,7 +227,7 @@ public static function jsonDeserialize(array $data): static { $args['resourceType'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/mixed-case/src/Service/ResourceStatus.php b/seed/php-model/mixed-case/src/Service/ResourceStatus.php index fcac980b9dd9..906f67dd9e0a 100644 --- a/seed/php-model/mixed-case/src/Service/ResourceStatus.php +++ b/seed/php-model/mixed-case/src/Service/ResourceStatus.php @@ -2,8 +2,8 @@ namespace Seed\Service; -enum ResourceStatus - : string { +enum ResourceStatus: string +{ case Active = "ACTIVE"; case Inactive = "INACTIVE"; } diff --git a/seed/php-model/mixed-case/src/Service/User.php b/seed/php-model/mixed-case/src/Service/User.php index 26af59597b7b..073b63e1466e 100644 --- a/seed/php-model/mixed-case/src/Service/User.php +++ b/seed/php-model/mixed-case/src/Service/User.php @@ -35,15 +35,17 @@ class User extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->userName = $values['userName'];$this->metadataTags = $values['metadataTags'];$this->extraProperties = $values['extraProperties']; + ) { + $this->userName = $values['userName']; + $this->metadataTags = $values['metadataTags']; + $this->extraProperties = $values['extraProperties']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/mixed-case/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/mixed-case/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/mixed-case/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/mixed-case/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/mixed-case/tests/Core/Json/EnumTest.php b/seed/php-model/mixed-case/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/mixed-case/tests/Core/Json/EnumTest.php +++ b/seed/php-model/mixed-case/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/mixed-case/tests/Core/Json/TraitTest.php b/seed/php-model/mixed-case/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/mixed-case/tests/Core/Json/TraitTest.php +++ b/seed/php-model/mixed-case/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/mixed-case/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/mixed-case/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/mixed-case/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/mixed-case/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/mixed-file-directory/src/Core/Json/JsonEncoder.php b/seed/php-model/mixed-file-directory/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/mixed-file-directory/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/mixed-file-directory/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/mixed-file-directory/src/Core/Json/JsonProperty.php b/seed/php-model/mixed-file-directory/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/mixed-file-directory/src/Core/Json/JsonProperty.php +++ b/seed/php-model/mixed-file-directory/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/mixed-file-directory/src/Core/Types/ArrayType.php b/seed/php-model/mixed-file-directory/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/mixed-file-directory/src/Core/Types/ArrayType.php +++ b/seed/php-model/mixed-file-directory/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/mixed-file-directory/src/Core/Types/Constant.php b/seed/php-model/mixed-file-directory/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/mixed-file-directory/src/Core/Types/Constant.php +++ b/seed/php-model/mixed-file-directory/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/mixed-file-directory/src/Core/Types/Union.php b/seed/php-model/mixed-file-directory/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/mixed-file-directory/src/Core/Types/Union.php +++ b/seed/php-model/mixed-file-directory/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/mixed-file-directory/src/Organization/CreateOrganizationRequest.php b/seed/php-model/mixed-file-directory/src/Organization/CreateOrganizationRequest.php index b346e9844e88..598661437118 100644 --- a/seed/php-model/mixed-file-directory/src/Organization/CreateOrganizationRequest.php +++ b/seed/php-model/mixed-file-directory/src/Organization/CreateOrganizationRequest.php @@ -20,15 +20,15 @@ class CreateOrganizationRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/mixed-file-directory/src/Organization/Organization.php b/seed/php-model/mixed-file-directory/src/Organization/Organization.php index 1053eed4e9ab..b7a4289116b3 100644 --- a/seed/php-model/mixed-file-directory/src/Organization/Organization.php +++ b/seed/php-model/mixed-file-directory/src/Organization/Organization.php @@ -36,15 +36,17 @@ class Organization extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->name = $values['name'];$this->users = $values['users']; + ) { + $this->id = $values['id']; + $this->name = $values['name']; + $this->users = $values['users']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/mixed-file-directory/src/User/Events/Event.php b/seed/php-model/mixed-file-directory/src/User/Events/Event.php index 53e708176fa3..17eebdfc9183 100644 --- a/seed/php-model/mixed-file-directory/src/User/Events/Event.php +++ b/seed/php-model/mixed-file-directory/src/User/Events/Event.php @@ -27,15 +27,16 @@ class Event extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->name = $values['name']; + ) { + $this->id = $values['id']; + $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/mixed-file-directory/src/User/Events/Metadata/Metadata.php b/seed/php-model/mixed-file-directory/src/User/Events/Metadata/Metadata.php index 879bfe250694..47c6d472ebf7 100644 --- a/seed/php-model/mixed-file-directory/src/User/Events/Metadata/Metadata.php +++ b/seed/php-model/mixed-file-directory/src/User/Events/Metadata/Metadata.php @@ -27,15 +27,16 @@ class Metadata extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->value = $values['value']; + ) { + $this->id = $values['id']; + $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/mixed-file-directory/src/User/User.php b/seed/php-model/mixed-file-directory/src/User/User.php index da58f0602eed..6d6e5782d723 100644 --- a/seed/php-model/mixed-file-directory/src/User/User.php +++ b/seed/php-model/mixed-file-directory/src/User/User.php @@ -34,15 +34,17 @@ class User extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->name = $values['name'];$this->age = $values['age']; + ) { + $this->id = $values['id']; + $this->name = $values['name']; + $this->age = $values['age']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/mixed-file-directory/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/mixed-file-directory/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/mixed-file-directory/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/mixed-file-directory/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/mixed-file-directory/tests/Core/Json/EnumTest.php b/seed/php-model/mixed-file-directory/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/mixed-file-directory/tests/Core/Json/EnumTest.php +++ b/seed/php-model/mixed-file-directory/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/mixed-file-directory/tests/Core/Json/TraitTest.php b/seed/php-model/mixed-file-directory/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/mixed-file-directory/tests/Core/Json/TraitTest.php +++ b/seed/php-model/mixed-file-directory/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/mixed-file-directory/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/mixed-file-directory/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/mixed-file-directory/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/mixed-file-directory/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/multi-line-docs/src/Core/Json/JsonEncoder.php b/seed/php-model/multi-line-docs/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/multi-line-docs/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/multi-line-docs/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/multi-line-docs/src/Core/Json/JsonProperty.php b/seed/php-model/multi-line-docs/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/multi-line-docs/src/Core/Json/JsonProperty.php +++ b/seed/php-model/multi-line-docs/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/multi-line-docs/src/Core/Types/ArrayType.php b/seed/php-model/multi-line-docs/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/multi-line-docs/src/Core/Types/ArrayType.php +++ b/seed/php-model/multi-line-docs/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/multi-line-docs/src/Core/Types/Constant.php b/seed/php-model/multi-line-docs/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/multi-line-docs/src/Core/Types/Constant.php +++ b/seed/php-model/multi-line-docs/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/multi-line-docs/src/Core/Types/Union.php b/seed/php-model/multi-line-docs/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/multi-line-docs/src/Core/Types/Union.php +++ b/seed/php-model/multi-line-docs/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/multi-line-docs/src/Operand.php b/seed/php-model/multi-line-docs/src/Operand.php index d3a020021299..f312669e7994 100644 --- a/seed/php-model/multi-line-docs/src/Operand.php +++ b/seed/php-model/multi-line-docs/src/Operand.php @@ -2,8 +2,8 @@ namespace Seed; -enum Operand - : string { +enum Operand: string +{ case GreaterThan = ">"; case EqualTo = "="; case LessThan = "less_than"; diff --git a/seed/php-model/multi-line-docs/src/User/User.php b/seed/php-model/multi-line-docs/src/User/User.php index f216a69e96d6..73f4b0887ad8 100644 --- a/seed/php-model/multi-line-docs/src/User/User.php +++ b/seed/php-model/multi-line-docs/src/User/User.php @@ -44,15 +44,17 @@ class User extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->name = $values['name'];$this->age = $values['age'] ?? null; + ) { + $this->id = $values['id']; + $this->name = $values['name']; + $this->age = $values['age'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/multi-line-docs/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/multi-line-docs/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/multi-line-docs/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/multi-line-docs/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/multi-line-docs/tests/Core/Json/EnumTest.php b/seed/php-model/multi-line-docs/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/multi-line-docs/tests/Core/Json/EnumTest.php +++ b/seed/php-model/multi-line-docs/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/multi-line-docs/tests/Core/Json/TraitTest.php b/seed/php-model/multi-line-docs/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/multi-line-docs/tests/Core/Json/TraitTest.php +++ b/seed/php-model/multi-line-docs/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/multi-line-docs/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/multi-line-docs/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/multi-line-docs/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/multi-line-docs/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/multi-url-environment-no-default/src/Core/Json/JsonEncoder.php b/seed/php-model/multi-url-environment-no-default/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/multi-url-environment-no-default/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/multi-url-environment-no-default/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/multi-url-environment-no-default/src/Core/Json/JsonProperty.php b/seed/php-model/multi-url-environment-no-default/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/multi-url-environment-no-default/src/Core/Json/JsonProperty.php +++ b/seed/php-model/multi-url-environment-no-default/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/multi-url-environment-no-default/src/Core/Types/ArrayType.php b/seed/php-model/multi-url-environment-no-default/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/multi-url-environment-no-default/src/Core/Types/ArrayType.php +++ b/seed/php-model/multi-url-environment-no-default/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/multi-url-environment-no-default/src/Core/Types/Constant.php b/seed/php-model/multi-url-environment-no-default/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/multi-url-environment-no-default/src/Core/Types/Constant.php +++ b/seed/php-model/multi-url-environment-no-default/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/multi-url-environment-no-default/src/Core/Types/Union.php b/seed/php-model/multi-url-environment-no-default/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/multi-url-environment-no-default/src/Core/Types/Union.php +++ b/seed/php-model/multi-url-environment-no-default/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/multi-url-environment-no-default/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/multi-url-environment-no-default/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/multi-url-environment-no-default/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/multi-url-environment-no-default/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/multi-url-environment-no-default/tests/Core/Json/EnumTest.php b/seed/php-model/multi-url-environment-no-default/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/multi-url-environment-no-default/tests/Core/Json/EnumTest.php +++ b/seed/php-model/multi-url-environment-no-default/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/multi-url-environment-no-default/tests/Core/Json/TraitTest.php b/seed/php-model/multi-url-environment-no-default/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/multi-url-environment-no-default/tests/Core/Json/TraitTest.php +++ b/seed/php-model/multi-url-environment-no-default/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/multi-url-environment-no-default/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/multi-url-environment-no-default/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/multi-url-environment-no-default/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/multi-url-environment-no-default/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/multi-url-environment/src/Core/Json/JsonEncoder.php b/seed/php-model/multi-url-environment/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/multi-url-environment/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/multi-url-environment/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/multi-url-environment/src/Core/Json/JsonProperty.php b/seed/php-model/multi-url-environment/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/multi-url-environment/src/Core/Json/JsonProperty.php +++ b/seed/php-model/multi-url-environment/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/multi-url-environment/src/Core/Types/ArrayType.php b/seed/php-model/multi-url-environment/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/multi-url-environment/src/Core/Types/ArrayType.php +++ b/seed/php-model/multi-url-environment/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/multi-url-environment/src/Core/Types/Constant.php b/seed/php-model/multi-url-environment/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/multi-url-environment/src/Core/Types/Constant.php +++ b/seed/php-model/multi-url-environment/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/multi-url-environment/src/Core/Types/Union.php b/seed/php-model/multi-url-environment/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/multi-url-environment/src/Core/Types/Union.php +++ b/seed/php-model/multi-url-environment/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/multi-url-environment/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/multi-url-environment/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/multi-url-environment/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/multi-url-environment/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/multi-url-environment/tests/Core/Json/EnumTest.php b/seed/php-model/multi-url-environment/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/multi-url-environment/tests/Core/Json/EnumTest.php +++ b/seed/php-model/multi-url-environment/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/multi-url-environment/tests/Core/Json/TraitTest.php b/seed/php-model/multi-url-environment/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/multi-url-environment/tests/Core/Json/TraitTest.php +++ b/seed/php-model/multi-url-environment/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/multi-url-environment/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/multi-url-environment/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/multi-url-environment/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/multi-url-environment/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/multiple-request-bodies/src/Core/Json/JsonEncoder.php b/seed/php-model/multiple-request-bodies/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/multiple-request-bodies/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/multiple-request-bodies/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/multiple-request-bodies/src/Core/Json/JsonProperty.php b/seed/php-model/multiple-request-bodies/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/multiple-request-bodies/src/Core/Json/JsonProperty.php +++ b/seed/php-model/multiple-request-bodies/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/multiple-request-bodies/src/Core/Types/ArrayType.php b/seed/php-model/multiple-request-bodies/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/multiple-request-bodies/src/Core/Types/ArrayType.php +++ b/seed/php-model/multiple-request-bodies/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/multiple-request-bodies/src/Core/Types/Constant.php b/seed/php-model/multiple-request-bodies/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/multiple-request-bodies/src/Core/Types/Constant.php +++ b/seed/php-model/multiple-request-bodies/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/multiple-request-bodies/src/Core/Types/Union.php b/seed/php-model/multiple-request-bodies/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/multiple-request-bodies/src/Core/Types/Union.php +++ b/seed/php-model/multiple-request-bodies/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/multiple-request-bodies/src/DocumentMetadata.php b/seed/php-model/multiple-request-bodies/src/DocumentMetadata.php index 05dba26f9f28..0812981aa7b8 100644 --- a/seed/php-model/multiple-request-bodies/src/DocumentMetadata.php +++ b/seed/php-model/multiple-request-bodies/src/DocumentMetadata.php @@ -42,15 +42,18 @@ class DocumentMetadata extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->author = $values['author'] ?? null;$this->id = $values['id'] ?? null;$this->tags = $values['tags'] ?? null;$this->title = $values['title'] ?? null; + ) { + $this->author = $values['author'] ?? null; + $this->id = $values['id'] ?? null; + $this->tags = $values['tags'] ?? null; + $this->title = $values['title'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/multiple-request-bodies/src/DocumentUploadResult.php b/seed/php-model/multiple-request-bodies/src/DocumentUploadResult.php index 140c9b093fd1..1263036b943d 100644 --- a/seed/php-model/multiple-request-bodies/src/DocumentUploadResult.php +++ b/seed/php-model/multiple-request-bodies/src/DocumentUploadResult.php @@ -27,15 +27,16 @@ class DocumentUploadResult extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->fileId = $values['fileId'] ?? null;$this->status = $values['status'] ?? null; + ) { + $this->fileId = $values['fileId'] ?? null; + $this->status = $values['status'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/multiple-request-bodies/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/multiple-request-bodies/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/multiple-request-bodies/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/multiple-request-bodies/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/multiple-request-bodies/tests/Core/Json/EnumTest.php b/seed/php-model/multiple-request-bodies/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/multiple-request-bodies/tests/Core/Json/EnumTest.php +++ b/seed/php-model/multiple-request-bodies/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/multiple-request-bodies/tests/Core/Json/TraitTest.php b/seed/php-model/multiple-request-bodies/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/multiple-request-bodies/tests/Core/Json/TraitTest.php +++ b/seed/php-model/multiple-request-bodies/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/multiple-request-bodies/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/multiple-request-bodies/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/multiple-request-bodies/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/multiple-request-bodies/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/no-environment/src/Core/Json/JsonEncoder.php b/seed/php-model/no-environment/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/no-environment/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/no-environment/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/no-environment/src/Core/Json/JsonProperty.php b/seed/php-model/no-environment/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/no-environment/src/Core/Json/JsonProperty.php +++ b/seed/php-model/no-environment/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/no-environment/src/Core/Types/ArrayType.php b/seed/php-model/no-environment/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/no-environment/src/Core/Types/ArrayType.php +++ b/seed/php-model/no-environment/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/no-environment/src/Core/Types/Constant.php b/seed/php-model/no-environment/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/no-environment/src/Core/Types/Constant.php +++ b/seed/php-model/no-environment/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/no-environment/src/Core/Types/Union.php b/seed/php-model/no-environment/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/no-environment/src/Core/Types/Union.php +++ b/seed/php-model/no-environment/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/no-environment/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/no-environment/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/no-environment/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/no-environment/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/no-environment/tests/Core/Json/EnumTest.php b/seed/php-model/no-environment/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/no-environment/tests/Core/Json/EnumTest.php +++ b/seed/php-model/no-environment/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/no-environment/tests/Core/Json/TraitTest.php b/seed/php-model/no-environment/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/no-environment/tests/Core/Json/TraitTest.php +++ b/seed/php-model/no-environment/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/no-environment/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/no-environment/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/no-environment/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/no-environment/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/no-retries/src/Core/Json/JsonEncoder.php b/seed/php-model/no-retries/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/no-retries/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/no-retries/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/no-retries/src/Core/Json/JsonProperty.php b/seed/php-model/no-retries/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/no-retries/src/Core/Json/JsonProperty.php +++ b/seed/php-model/no-retries/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/no-retries/src/Core/Types/ArrayType.php b/seed/php-model/no-retries/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/no-retries/src/Core/Types/ArrayType.php +++ b/seed/php-model/no-retries/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/no-retries/src/Core/Types/Constant.php b/seed/php-model/no-retries/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/no-retries/src/Core/Types/Constant.php +++ b/seed/php-model/no-retries/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/no-retries/src/Core/Types/Union.php b/seed/php-model/no-retries/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/no-retries/src/Core/Types/Union.php +++ b/seed/php-model/no-retries/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/no-retries/src/Retries/User.php b/seed/php-model/no-retries/src/Retries/User.php index c88dcee57268..b4c40e4b9de3 100644 --- a/seed/php-model/no-retries/src/Retries/User.php +++ b/seed/php-model/no-retries/src/Retries/User.php @@ -27,15 +27,16 @@ class User extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->name = $values['name']; + ) { + $this->id = $values['id']; + $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/no-retries/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/no-retries/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/no-retries/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/no-retries/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/no-retries/tests/Core/Json/EnumTest.php b/seed/php-model/no-retries/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/no-retries/tests/Core/Json/EnumTest.php +++ b/seed/php-model/no-retries/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/no-retries/tests/Core/Json/TraitTest.php b/seed/php-model/no-retries/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/no-retries/tests/Core/Json/TraitTest.php +++ b/seed/php-model/no-retries/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/no-retries/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/no-retries/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/no-retries/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/no-retries/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/nullable-allof-extends/.fern/metadata.json b/seed/php-model/nullable-allof-extends/.fern/metadata.json new file mode 100644 index 000000000000..90046b58049c --- /dev/null +++ b/seed/php-model/nullable-allof-extends/.fern/metadata.json @@ -0,0 +1,5 @@ +{ + "cliVersion": "DUMMY", + "generatorName": "fernapi/fern-php-model", + "generatorVersion": "latest" +} \ No newline at end of file diff --git a/seed/php-model/nullable-allof-extends/.github/workflows/ci.yml b/seed/php-model/nullable-allof-extends/.github/workflows/ci.yml new file mode 100644 index 000000000000..ba5836ae6967 --- /dev/null +++ b/seed/php-model/nullable-allof-extends/.github/workflows/ci.yml @@ -0,0 +1,48 @@ +name: ci + +on: [push] + +jobs: + compile: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "8.1" + + - name: Install tools + run: | + composer install + + - name: Build + run: | + composer build + + - name: Analyze + run: | + composer analyze + + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "8.1" + + - name: Install tools + run: | + composer install + + - name: Run Tests + run: | + composer test diff --git a/seed/php-model/nullable-allof-extends/.gitignore b/seed/php-model/nullable-allof-extends/.gitignore new file mode 100644 index 000000000000..31a1aeb14f35 --- /dev/null +++ b/seed/php-model/nullable-allof-extends/.gitignore @@ -0,0 +1,5 @@ +.idea +.php-cs-fixer.cache +.phpunit.result.cache +composer.lock +vendor/ \ No newline at end of file diff --git a/seed/php-model/nullable-allof-extends/composer.json b/seed/php-model/nullable-allof-extends/composer.json new file mode 100644 index 000000000000..c6a5d9f87477 --- /dev/null +++ b/seed/php-model/nullable-allof-extends/composer.json @@ -0,0 +1,39 @@ +{ + "name": "seed/seed", + "version": "0.0.1", + "description": "Seed PHP Library", + "keywords": [ + "seed", + "api", + "sdk" + ], + "license": [], + "require": { + "php": "^8.1", + "ext-json": "*", + "guzzlehttp/guzzle": "^7.4" + }, + "require-dev": { + "phpunit/phpunit": "^9.0", + "friendsofphp/php-cs-fixer": "3.5.0", + "phpstan/phpstan": "^1.12" + }, + "autoload": { + "psr-4": { + "Seed\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Seed\\Tests\\": "tests/" + } + }, + "scripts": { + "build": [ + "@php -l src", + "@php -l tests" + ], + "test": "phpunit", + "analyze": "phpstan analyze src tests --memory-limit=1G" + } +} \ No newline at end of file diff --git a/seed/php-model/nullable-allof-extends/phpstan.neon b/seed/php-model/nullable-allof-extends/phpstan.neon new file mode 100644 index 000000000000..780706b8f8a2 --- /dev/null +++ b/seed/php-model/nullable-allof-extends/phpstan.neon @@ -0,0 +1,6 @@ +parameters: + level: max + reportUnmatchedIgnoredErrors: false + paths: + - src + - tests \ No newline at end of file diff --git a/seed/php-model/nullable-allof-extends/phpunit.xml b/seed/php-model/nullable-allof-extends/phpunit.xml new file mode 100644 index 000000000000..54630a51163c --- /dev/null +++ b/seed/php-model/nullable-allof-extends/phpunit.xml @@ -0,0 +1,7 @@ + + + + tests + + + \ No newline at end of file diff --git a/seed/php-model/nullable-allof-extends/snippet.json b/seed/php-model/nullable-allof-extends/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/php-model/nullable-allof-extends/src/Core/Json/JsonDecoder.php b/seed/php-model/nullable-allof-extends/src/Core/Json/JsonDecoder.php new file mode 100644 index 000000000000..2ddff0273482 --- /dev/null +++ b/seed/php-model/nullable-allof-extends/src/Core/Json/JsonDecoder.php @@ -0,0 +1,161 @@ + $type The type definition for deserialization. + * @return mixed[]|array The deserialized array. + * @throws JsonException If the decoded value is not an array. + */ + public static function decodeArray(string $json, array $type): array + { + $decoded = self::decode($json); + if (!is_array($decoded)) { + throw new JsonException("Unexpected non-array json value: " . $json); + } + return JsonDeserializer::deserializeArray($decoded, $type); + } + + /** + * Decodes a JSON string and deserializes it based on the provided union type definition. + * + * @param string $json The JSON string to decode. + * @param Union $union The union type definition for deserialization. + * @return mixed The deserialized value. + * @throws JsonException If the deserialization for all types in the union fails. + */ + public static function decodeUnion(string $json, Union $union): mixed + { + $decoded = self::decode($json); + return JsonDeserializer::deserializeUnion($decoded, $union); + } + /** + * Decodes a JSON string and returns a mixed. + * + * @param string $json The JSON string to decode. + * @return mixed The decoded mixed. + * @throws JsonException If the decoded value is not an mixed. + */ + public static function decodeMixed(string $json): mixed + { + return self::decode($json); + } + + /** + * Decodes a JSON string into a PHP value. + * + * @param string $json The JSON string to decode. + * @return mixed The decoded value. + * @throws JsonException If an error occurs during JSON decoding. + */ + public static function decode(string $json): mixed + { + return json_decode($json, associative: true, flags: JSON_THROW_ON_ERROR); + } +} diff --git a/seed/php-model/nullable-allof-extends/src/Core/Json/JsonDeserializer.php b/seed/php-model/nullable-allof-extends/src/Core/Json/JsonDeserializer.php new file mode 100644 index 000000000000..ab6a3f826238 --- /dev/null +++ b/seed/php-model/nullable-allof-extends/src/Core/Json/JsonDeserializer.php @@ -0,0 +1,206 @@ + $data The array to be deserialized. + * @param mixed[]|array $type The type definition from the annotation. + * @return mixed[]|array The deserialized array. + * @throws JsonException If deserialization fails. + */ + public static function deserializeArray(array $data, array $type): array + { + return Utils::isMapType($type) + ? self::deserializeMap($data, $type) + : self::deserializeList($data, $type); + } + + /** + * Deserializes a value based on its type definition. + * + * @param mixed $data The data to deserialize. + * @param mixed $type The type definition. + * @return mixed The deserialized value. + * @throws JsonException If deserialization fails. + */ + private static function deserializeValue(mixed $data, mixed $type): mixed + { + if ($type instanceof Union) { + return self::deserializeUnion($data, $type); + } + + if (is_array($type)) { + return self::deserializeArray((array)$data, $type); + } + + if (gettype($type) != "string") { + throw new JsonException("Unexpected non-string type."); + } + + return self::deserializeSingleValue($data, $type); + } + + /** + * Deserializes a value based on the possible types in a union type definition. + * + * @param mixed $data The data to deserialize. + * @param Union $type The union type definition. + * @return mixed The deserialized value. + * @throws JsonException If none of the union types can successfully deserialize the value. + */ + public static function deserializeUnion(mixed $data, Union $type): mixed + { + foreach ($type->types as $unionType) { + try { + return self::deserializeValue($data, $unionType); + } catch (\Throwable) { + // Catching Throwable instead of Exception to handle TypeError + // that occurs when assigning null to non-nullable typed properties + continue; + } + } + $readableType = Utils::getReadableType($data); + throw new JsonException( + "Cannot deserialize value of type $readableType with any of the union types: " . $type + ); + } + + /** + * Deserializes a single value based on its expected type. + * + * @param mixed $data The data to deserialize. + * @param string $type The expected type. + * @return mixed The deserialized value. + * @throws JsonException If deserialization fails. + */ + private static function deserializeSingleValue(mixed $data, string $type): mixed + { + if ($type === 'null' && $data === null) { + return null; + } + + if ($type === 'date' && is_string($data)) { + return self::deserializeDate($data); + } + + if ($type === 'datetime' && is_string($data)) { + return self::deserializeDateTime($data); + } + + if ($type === 'mixed') { + return $data; + } + + if (class_exists($type) && is_array($data)) { + return self::deserializeObject($data, $type); + } + + // Handle floats as a special case since gettype($data) returns "double" for float values in PHP, and because + // floats make come through from json_decoded as integers + if ($type === 'float' && (is_numeric($data))) { + return (float) $data; + } + + if (gettype($data) === $type) { + return $data; + } + + throw new JsonException("Unable to deserialize value of type '" . gettype($data) . "' as '$type'."); + } + + /** + * Deserializes an array into an object of the given type. + * + * @param array $data The data to deserialize. + * @param string $type The class name of the object to deserialize into. + * + * @return object The deserialized object. + * + * @throws JsonException If the type does not implement JsonSerializableType. + */ + public static function deserializeObject(array $data, string $type): object + { + if (!is_subclass_of($type, JsonSerializableType::class)) { + throw new JsonException("$type is not a subclass of JsonSerializableType."); + } + return $type::jsonDeserialize($data); + } + + /** + * Deserializes a map (associative array) with defined key and value types. + * + * @param array $data The associative array to deserialize. + * @param array $type The type definition for the map. + * @return array The deserialized map. + * @throws JsonException If deserialization fails. + */ + private static function deserializeMap(array $data, array $type): array + { + $keyType = array_key_first($type); + $valueType = $type[$keyType]; + $result = []; + + foreach ($data as $key => $item) { + $key = Utils::castKey($key, (string)$keyType); + $result[$key] = self::deserializeValue($item, $valueType); + } + + return $result; + } + + /** + * Deserializes a list (indexed array) with a defined value type. + * + * @param array $data The list to deserialize. + * @param array $type The type definition for the list. + * @return array The deserialized list. + * @throws JsonException If deserialization fails. + */ + private static function deserializeList(array $data, array $type): array + { + $valueType = $type[0]; + return array_map(fn ($item) => self::deserializeValue($item, $valueType), $data); + } +} diff --git a/seed/php-model/nullable-allof-extends/src/Core/Json/JsonEncoder.php b/seed/php-model/nullable-allof-extends/src/Core/Json/JsonEncoder.php new file mode 100644 index 000000000000..0dbf3fcc9948 --- /dev/null +++ b/seed/php-model/nullable-allof-extends/src/Core/Json/JsonEncoder.php @@ -0,0 +1,20 @@ + Extra properties from JSON that don't map to class properties */ + private array $__additionalProperties = []; + + /** + * Serializes the object to a JSON string. + * + * @return string JSON-encoded string representation of the object. + * @throws Exception If encoding fails. + */ + public function toJson(): string + { + $serializedObject = $this->jsonSerialize(); + $encoded = JsonEncoder::encode($serializedObject); + if (!$encoded) { + throw new Exception("Could not encode type"); + } + return $encoded; + } + + /** + * Serializes the object to an array. + * + * @return mixed[] Array representation of the object. + * @throws JsonException If serialization fails. + */ + public function jsonSerialize(): array + { + $result = []; + $reflectionClass = new \ReflectionClass($this); + foreach ($reflectionClass->getProperties() as $property) { + $jsonKey = self::getJsonKey($property); + if ($jsonKey == null) { + continue; + } + $value = $property->getValue($this); + + // Handle DateTime properties + $dateTypeAttr = $property->getAttributes(Date::class)[0] ?? null; + if ($dateTypeAttr && $value instanceof DateTime) { + $dateType = $dateTypeAttr->newInstance()->type; + $value = ($dateType === Date::TYPE_DATE) + ? JsonSerializer::serializeDate($value) + : JsonSerializer::serializeDateTime($value); + } + + // Handle Union annotations + $unionTypeAttr = $property->getAttributes(Union::class)[0] ?? null; + if ($unionTypeAttr) { + $unionType = $unionTypeAttr->newInstance(); + $value = JsonSerializer::serializeUnion($value, $unionType); + } + + // Handle arrays with type annotations + $arrayTypeAttr = $property->getAttributes(ArrayType::class)[0] ?? null; + if ($arrayTypeAttr && is_array($value)) { + $arrayType = $arrayTypeAttr->newInstance()->type; + $value = JsonSerializer::serializeArray($value, $arrayType); + } + + // Handle object + if (is_object($value)) { + $value = JsonSerializer::serializeObject($value); + } + + if ($value !== null) { + $result[$jsonKey] = $value; + } + } + return $result; + } + + /** + * Deserializes a JSON string into an instance of the calling class. + * + * @param string $json JSON string to deserialize. + * @return static Deserialized object. + * @throws JsonException If decoding fails or the result is not an array. + * @throws Exception If deserialization fails. + */ + public static function fromJson(string $json): static + { + $decodedJson = JsonDecoder::decode($json); + if (!is_array($decodedJson)) { + throw new JsonException("Unexpected non-array decoded type: " . gettype($decodedJson)); + } + return self::jsonDeserialize($decodedJson); + } + + /** + * Deserializes an array into an instance of the calling class. + * + * @param array $data Array data to deserialize. + * @return static Deserialized object. + * @throws JsonException If deserialization fails. + */ + public static function jsonDeserialize(array $data): static + { + $reflectionClass = new \ReflectionClass(static::class); + $constructor = $reflectionClass->getConstructor(); + if ($constructor === null) { + throw new JsonException("No constructor found."); + } + + $args = []; + $properties = []; + $additionalProperties = []; + foreach ($reflectionClass->getProperties() as $property) { + $jsonKey = self::getJsonKey($property) ?? $property->getName(); + $properties[$jsonKey] = $property; + } + + foreach ($data as $jsonKey => $value) { + if (!isset($properties[$jsonKey])) { + // This JSON key doesn't map to any class property - add it to additionalProperties + $additionalProperties[$jsonKey] = $value; + continue; + } + + $property = $properties[$jsonKey]; + + // Handle Date annotation + $dateTypeAttr = $property->getAttributes(Date::class)[0] ?? null; + if ($dateTypeAttr) { + $dateType = $dateTypeAttr->newInstance()->type; + if (!is_string($value)) { + throw new JsonException("Unexpected non-string type for date."); + } + $value = ($dateType === Date::TYPE_DATE) + ? JsonDeserializer::deserializeDate($value) + : JsonDeserializer::deserializeDateTime($value); + } + + // Handle Array annotation + $arrayTypeAttr = $property->getAttributes(ArrayType::class)[0] ?? null; + if (is_array($value) && $arrayTypeAttr) { + $arrayType = $arrayTypeAttr->newInstance()->type; + $value = JsonDeserializer::deserializeArray($value, $arrayType); + } + + // Handle Union annotations + $unionTypeAttr = $property->getAttributes(Union::class)[0] ?? null; + if ($unionTypeAttr) { + $unionType = $unionTypeAttr->newInstance(); + $value = JsonDeserializer::deserializeUnion($value, $unionType); + } + + // Handle object + $type = $property->getType(); + if (is_array($value) && $type instanceof ReflectionNamedType && !$type->isBuiltin()) { + $value = JsonDeserializer::deserializeObject($value, $type->getName()); + } + + $args[$property->getName()] = $value; + } + + // Fill in any missing properties with defaults + foreach ($properties as $property) { + if (!isset($args[$property->getName()])) { + $args[$property->getName()] = $property->getDefaultValue() ?? null; + } + } + + // @phpstan-ignore-next-line + $result = new static($args); + $result->__additionalProperties = $additionalProperties; + return $result; + } + + /** + * Get properties from JSON that weren't mapped to class fields + * @return array + */ + public function getAdditionalProperties(): array + { + return $this->__additionalProperties; + } + + /** + * Retrieves the JSON key associated with a property. + * + * @param ReflectionProperty $property The reflection property. + * @return ?string The JSON key, or null if not available. + */ + private static function getJsonKey(ReflectionProperty $property): ?string + { + $jsonPropertyAttr = $property->getAttributes(JsonProperty::class)[0] ?? null; + return $jsonPropertyAttr?->newInstance()?->name; + } +} diff --git a/seed/php-model/nullable-allof-extends/src/Core/Json/JsonSerializer.php b/seed/php-model/nullable-allof-extends/src/Core/Json/JsonSerializer.php new file mode 100644 index 000000000000..36dc06dfcf50 --- /dev/null +++ b/seed/php-model/nullable-allof-extends/src/Core/Json/JsonSerializer.php @@ -0,0 +1,197 @@ +format(Constant::DateFormat); + } + + /** + * Serializes a DateTime object into a string using the date-time format. + * Normalizes UTC times to use 'Z' suffix instead of '+00:00'. + * + * @param DateTime $date The DateTime object to serialize. + * @return string The serialized date-time string. + */ + public static function serializeDateTime(DateTime $date): string + { + $formatted = $date->format(Constant::DateTimeFormat); + if (str_ends_with($formatted, '+00:00')) { + return substr($formatted, 0, -6) . 'Z'; + } + return $formatted; + } + + /** + * Serializes an array based on type annotations (either a list or map). + * + * @param mixed[]|array $data The array to be serialized. + * @param mixed[]|array $type The type definition from the annotation. + * @return mixed[]|array The serialized array. + * @throws JsonException If serialization fails. + */ + public static function serializeArray(array $data, array $type): array + { + return Utils::isMapType($type) + ? self::serializeMap($data, $type) + : self::serializeList($data, $type); + } + + /** + * Serializes a value based on its type definition. + * + * @param mixed $data The value to serialize. + * @param mixed $type The type definition. + * @return mixed The serialized value. + * @throws JsonException If serialization fails. + */ + private static function serializeValue(mixed $data, mixed $type): mixed + { + if ($type instanceof Union) { + return self::serializeUnion($data, $type); + } + + if (is_array($type)) { + return self::serializeArray((array)$data, $type); + } + + if (gettype($type) != "string") { + throw new JsonException("Unexpected non-string type."); + } + + return self::serializeSingleValue($data, $type); + } + + /** + * Serializes a value for a union type definition. + * + * @param mixed $data The value to serialize. + * @param Union $unionType The union type definition. + * @return mixed The serialized value. + * @throws JsonException If serialization fails for all union types. + */ + public static function serializeUnion(mixed $data, Union $unionType): mixed + { + foreach ($unionType->types as $type) { + try { + return self::serializeValue($data, $type); + } catch (Exception) { + // Try the next type in the union + continue; + } + } + $readableType = Utils::getReadableType($data); + throw new JsonException( + "Cannot serialize value of type $readableType with any of the union types: " . $unionType + ); + } + + /** + * Serializes a single value based on its type. + * + * @param mixed $data The value to serialize. + * @param string $type The expected type. + * @return mixed The serialized value. + * @throws JsonException If serialization fails. + */ + private static function serializeSingleValue(mixed $data, string $type): mixed + { + if ($type === 'null' && $data === null) { + return null; + } + + if (($type === 'date' || $type === 'datetime') && $data instanceof DateTime) { + return $type === 'date' ? self::serializeDate($data) : self::serializeDateTime($data); + } + + if ($type === 'mixed') { + return $data; + } + + if (class_exists($type) && $data instanceof $type) { + return self::serializeObject($data); + } + + // Handle floats as a special case since gettype($data) returns "double" for float values in PHP. + if ($type === 'float' && is_float($data)) { + return $data; + } + + if (gettype($data) === $type) { + return $data; + } + + throw new JsonException("Unable to serialize value of type '" . gettype($data) . "' as '$type'."); + } + + /** + * Serializes an object to a JSON-serializable format. + * + * @param object $data The object to serialize. + * @return mixed The serialized data. + * @throws JsonException If the object does not implement JsonSerializable. + */ + public static function serializeObject(object $data): mixed + { + if (!is_subclass_of($data, JsonSerializable::class)) { + $type = get_class($data); + throw new JsonException("Class $type must implement JsonSerializable."); + } + return $data->jsonSerialize(); + } + + /** + * Serializes a map (associative array) with defined key and value types. + * + * @param array $data The associative array to serialize. + * @param array $type The type definition for the map. + * @return array The serialized map. + * @throws JsonException If serialization fails. + */ + private static function serializeMap(array $data, array $type): array + { + $keyType = array_key_first($type); + if ($keyType === null) { + throw new JsonException("Unexpected no key in ArrayType."); + } + $valueType = $type[$keyType]; + $result = []; + + foreach ($data as $key => $item) { + $key = Utils::castKey($key, $keyType); + $result[$key] = self::serializeValue($item, $valueType); + } + + return $result; + } + + /** + * Serializes a list (indexed array) where only the value type is defined. + * + * @param array $data The list to serialize. + * @param array $type The type definition for the list. + * @return array The serialized list. + * @throws JsonException If serialization fails. + */ + private static function serializeList(array $data, array $type): array + { + $valueType = $type[0]; + return array_map(fn ($item) => self::serializeValue($item, $valueType), $data); + } +} diff --git a/seed/php-model/nullable-allof-extends/src/Core/Json/Utils.php b/seed/php-model/nullable-allof-extends/src/Core/Json/Utils.php new file mode 100644 index 000000000000..7577c058916d --- /dev/null +++ b/seed/php-model/nullable-allof-extends/src/Core/Json/Utils.php @@ -0,0 +1,61 @@ + $type The type definition from the annotation. + * @return bool True if the type is a map, false if it's a list. + */ + public static function isMapType(array $type): bool + { + return count($type) === 1 && !array_is_list($type); + } + + /** + * Casts the key to the appropriate type based on the key type. + * + * @param mixed $key The key to be cast. + * @param string $keyType The type to cast the key to ('string', 'integer', 'float'). + * @return mixed The casted key. + * @throws JsonException + */ + public static function castKey(mixed $key, string $keyType): mixed + { + if (!is_scalar($key)) { + throw new JsonException("Key must be a scalar type."); + } + return match ($keyType) { + 'integer' => (int)$key, + 'float' => (float)$key, + 'string' => (string)$key, + default => $key, + }; + } + + /** + * Returns a human-readable representation of the input's type. + * + * @param mixed $input The input value to determine the type of. + * @return string A readable description of the input type. + */ + public static function getReadableType(mixed $input): string + { + if (is_object($input)) { + return get_class($input); + } elseif (is_array($input)) { + return 'array(' . count($input) . ' items)'; + } elseif (is_null($input)) { + return 'null'; + } else { + return gettype($input); + } + } +} diff --git a/seed/php-model/nullable-allof-extends/src/Core/Types/ArrayType.php b/seed/php-model/nullable-allof-extends/src/Core/Types/ArrayType.php new file mode 100644 index 000000000000..a26d29008ec3 --- /dev/null +++ b/seed/php-model/nullable-allof-extends/src/Core/Types/ArrayType.php @@ -0,0 +1,16 @@ + 'valueType'] for maps, or ['valueType'] for lists + */ + public function __construct(public array $type) + { + } +} diff --git a/seed/php-model/nullable-allof-extends/src/Core/Types/Constant.php b/seed/php-model/nullable-allof-extends/src/Core/Types/Constant.php new file mode 100644 index 000000000000..5ac4518cc6d6 --- /dev/null +++ b/seed/php-model/nullable-allof-extends/src/Core/Types/Constant.php @@ -0,0 +1,12 @@ +> The types allowed for this property, which can be strings, arrays, or nested Union types. + */ + public array $types; + + /** + * Constructor for the Union attribute. + * + * @param string|Union|array ...$types The list of types that the property can accept. + * This can include primitive types (e.g., 'string', 'int'), arrays, or other Union instances. + * + * Example: + * ```php + * #[Union('string', 'null', 'date', new Union('boolean', 'int'))] + * ``` + */ + public function __construct(string|Union|array ...$types) + { + $this->types = $types; + } + + /** + * Converts the Union type to a string representation. + * + * @return string A string representation of the union types. + */ + public function __toString(): string + { + return implode(' | ', array_map(function ($type) { + if (is_string($type)) { + return $type; + } elseif ($type instanceof Union) { + return (string) $type; // Recursively handle nested unions + } elseif (is_array($type)) { + return 'array'; // Handle arrays + } + }, $this->types)); + } +} diff --git a/seed/php-model/nullable-allof-extends/src/NormalObject.php b/seed/php-model/nullable-allof-extends/src/NormalObject.php new file mode 100644 index 000000000000..639911fc073a --- /dev/null +++ b/seed/php-model/nullable-allof-extends/src/NormalObject.php @@ -0,0 +1,37 @@ +normalField = $values['normalField'] ?? null; + } + + /** + * @return string + */ + public function __toString(): string + { + return $this->toJson(); + } +} diff --git a/seed/php-model/nullable-allof-extends/src/NullableObject.php b/seed/php-model/nullable-allof-extends/src/NullableObject.php new file mode 100644 index 000000000000..ef908003618a --- /dev/null +++ b/seed/php-model/nullable-allof-extends/src/NullableObject.php @@ -0,0 +1,37 @@ +nullableField = $values['nullableField'] ?? null; + } + + /** + * @return string + */ + public function __toString(): string + { + return $this->toJson(); + } +} diff --git a/seed/php-model/nullable-allof-extends/src/RootObject.php b/seed/php-model/nullable-allof-extends/src/RootObject.php new file mode 100644 index 000000000000..40a0a5d934b2 --- /dev/null +++ b/seed/php-model/nullable-allof-extends/src/RootObject.php @@ -0,0 +1,38 @@ +normalField = $values['normalField'] ?? null; + $this->nullableField = $values['nullableField'] ?? null; + } + + /** + * @return string + */ + public function __toString(): string + { + return $this->toJson(); + } +} diff --git a/seed/php-model/nullable-allof-extends/src/Traits/NormalObject.php b/seed/php-model/nullable-allof-extends/src/Traits/NormalObject.php new file mode 100644 index 000000000000..0f3680b5b55b --- /dev/null +++ b/seed/php-model/nullable-allof-extends/src/Traits/NormalObject.php @@ -0,0 +1,19 @@ +name; + } + + /** + * @return string|null + */ + public function getEmail(): ?string + { + return $this->email; + } + + /** + * @param array{ + * name: string, + * email?: string|null, + * } $values + */ + public function __construct( + array $values, + ) { + $this->name = $values['name']; + $this->email = $values['email'] ?? null; + } +} + +class AdditionalPropertiesTest extends TestCase +{ + public function testExtraProperties(): void + { + $expectedJson = json_encode( + [ + 'name' => 'john.doe', + 'email' => 'john.doe@example.com', + 'age' => 42 + ], + JSON_THROW_ON_ERROR + ); + + $person = Person::fromJson($expectedJson); + $this->assertEquals('john.doe', $person->getName()); + $this->assertEquals('john.doe@example.com', $person->getEmail()); + $this->assertEquals( + [ + 'age' => 42 + ], + $person->getAdditionalProperties(), + ); + } +} diff --git a/seed/php-model/nullable-allof-extends/tests/Core/Json/DateArrayTest.php b/seed/php-model/nullable-allof-extends/tests/Core/Json/DateArrayTest.php new file mode 100644 index 000000000000..a72cfdbdd227 --- /dev/null +++ b/seed/php-model/nullable-allof-extends/tests/Core/Json/DateArrayTest.php @@ -0,0 +1,54 @@ +dates = $values['dates']; + } +} + +class DateArrayTest extends TestCase +{ + public function testDateTimeInArrays(): void + { + $expectedJson = json_encode( + [ + 'dates' => ['2023-01-01', '2023-02-01', '2023-03-01'] + ], + JSON_THROW_ON_ERROR + ); + + $object = DateArray::fromJson($expectedJson); + $this->assertInstanceOf(DateTime::class, $object->dates[0], 'dates[0] should be a DateTime instance.'); + $this->assertEquals('2023-01-01', $object->dates[0]->format('Y-m-d'), 'dates[0] should have the correct date.'); + $this->assertInstanceOf(DateTime::class, $object->dates[1], 'dates[1] should be a DateTime instance.'); + $this->assertEquals('2023-02-01', $object->dates[1]->format('Y-m-d'), 'dates[1] should have the correct date.'); + $this->assertInstanceOf(DateTime::class, $object->dates[2], 'dates[2] should be a DateTime instance.'); + $this->assertEquals('2023-03-01', $object->dates[2]->format('Y-m-d'), 'dates[2] should have the correct date.'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for dates array.'); + } +} diff --git a/seed/php-model/nullable-allof-extends/tests/Core/Json/EmptyArrayTest.php b/seed/php-model/nullable-allof-extends/tests/Core/Json/EmptyArrayTest.php new file mode 100644 index 000000000000..d243a08916d4 --- /dev/null +++ b/seed/php-model/nullable-allof-extends/tests/Core/Json/EmptyArrayTest.php @@ -0,0 +1,71 @@ + $emptyMapArray + */ + #[JsonProperty('empty_map_array')] + #[ArrayType(['integer' => new Union('string', 'null')])] + public array $emptyMapArray; + + /** + * @var array $emptyDatesArray + */ + #[ArrayType([new Union('date', 'null')])] + #[JsonProperty('empty_dates_array')] + public array $emptyDatesArray; + + /** + * @param array{ + * emptyStringArray: string[], + * emptyMapArray: array, + * emptyDatesArray: array, + * } $values + */ + public function __construct( + array $values, + ) { + $this->emptyStringArray = $values['emptyStringArray']; + $this->emptyMapArray = $values['emptyMapArray']; + $this->emptyDatesArray = $values['emptyDatesArray']; + } +} + +class EmptyArrayTest extends TestCase +{ + public function testEmptyArray(): void + { + $expectedJson = json_encode( + [ + 'empty_string_array' => [], + 'empty_map_array' => [], + 'empty_dates_array' => [] + ], + JSON_THROW_ON_ERROR + ); + + $object = EmptyArray::fromJson($expectedJson); + $this->assertEmpty($object->emptyStringArray, 'empty_string_array should be empty.'); + $this->assertEmpty($object->emptyMapArray, 'empty_map_array should be empty.'); + $this->assertEmpty($object->emptyDatesArray, 'empty_dates_array should be empty.'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for EmptyArraysType.'); + } +} diff --git a/seed/php-model/nullable-allof-extends/tests/Core/Json/EnumTest.php b/seed/php-model/nullable-allof-extends/tests/Core/Json/EnumTest.php new file mode 100644 index 000000000000..bf83d5b8ab0f --- /dev/null +++ b/seed/php-model/nullable-allof-extends/tests/Core/Json/EnumTest.php @@ -0,0 +1,76 @@ +value; + } +} + +class ShapeType extends JsonSerializableType +{ + /** + * @var Shape $shape + */ + #[JsonProperty('shape')] + public Shape $shape; + + /** + * @var Shape[] $shapes + */ + #[ArrayType([Shape::class])] + #[JsonProperty('shapes')] + public array $shapes; + + /** + * @param Shape $shape + * @param Shape[] $shapes + */ + public function __construct( + Shape $shape, + array $shapes, + ) { + $this->shape = $shape; + $this->shapes = $shapes; + } +} + +class EnumTest extends TestCase +{ + public function testEnumSerialization(): void + { + $object = new ShapeType( + Shape::Circle, + [Shape::Square, Shape::Circle, Shape::Triangle] + ); + + $expectedJson = json_encode([ + 'shape' => 'CIRCLE', + 'shapes' => ['SQUARE', 'CIRCLE', 'TRIANGLE'] + ], JSON_THROW_ON_ERROR); + + $actualJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString( + $expectedJson, + $actualJson, + 'Serialized JSON does not match expected JSON for shape and shapes properties.' + ); + } +} diff --git a/seed/php-model/nullable-allof-extends/tests/Core/Json/ExhaustiveTest.php b/seed/php-model/nullable-allof-extends/tests/Core/Json/ExhaustiveTest.php new file mode 100644 index 000000000000..ed1330eda3e4 --- /dev/null +++ b/seed/php-model/nullable-allof-extends/tests/Core/Json/ExhaustiveTest.php @@ -0,0 +1,197 @@ +nestedProperty = $values['nestedProperty']; + } +} + +class Type extends JsonSerializableType +{ + /** + * @var Nested nestedType + */ + #[JsonProperty('nested_type')] + public Nested $nestedType; /** + + * @var string $simpleProperty + */ + #[JsonProperty('simple_property')] + public string $simpleProperty; + + /** + * @var DateTime $dateProperty + */ + #[Date(Date::TYPE_DATE)] + #[JsonProperty('date_property')] + public DateTime $dateProperty; + + /** + * @var DateTime $datetimeProperty + */ + #[Date(Date::TYPE_DATETIME)] + #[JsonProperty('datetime_property')] + public DateTime $datetimeProperty; + + /** + * @var array $stringArray + */ + #[ArrayType(['string'])] + #[JsonProperty('string_array')] + public array $stringArray; + + /** + * @var array $mapProperty + */ + #[ArrayType(['string' => 'integer'])] + #[JsonProperty('map_property')] + public array $mapProperty; + + /** + * @var array $objectArray + */ + #[ArrayType(['integer' => new Union(Nested::class, 'null')])] + #[JsonProperty('object_array')] + public array $objectArray; + + /** + * @var array> $nestedArray + */ + #[ArrayType(['integer' => ['integer' => new Union('string', 'null')]])] + #[JsonProperty('nested_array')] + public array $nestedArray; + + /** + * @var array $datesArray + */ + #[ArrayType([new Union('date', 'null')])] + #[JsonProperty('dates_array')] + public array $datesArray; + + /** + * @var string|null $nullableProperty + */ + #[JsonProperty('nullable_property')] + public ?string $nullableProperty; + + /** + * @param array{ + * nestedType: Nested, + * simpleProperty: string, + * dateProperty: DateTime, + * datetimeProperty: DateTime, + * stringArray: array, + * mapProperty: array, + * objectArray: array, + * nestedArray: array>, + * datesArray: array, + * nullableProperty?: string|null, + * } $values + */ + public function __construct( + array $values, + ) { + $this->nestedType = $values['nestedType']; + $this->simpleProperty = $values['simpleProperty']; + $this->dateProperty = $values['dateProperty']; + $this->datetimeProperty = $values['datetimeProperty']; + $this->stringArray = $values['stringArray']; + $this->mapProperty = $values['mapProperty']; + $this->objectArray = $values['objectArray']; + $this->nestedArray = $values['nestedArray']; + $this->datesArray = $values['datesArray']; + $this->nullableProperty = $values['nullableProperty'] ?? null; + } +} + +class ExhaustiveTest extends TestCase +{ + /** + * Test serialization and deserialization of all types in Type. + */ + public function testExhaustive(): void + { + $expectedJson = json_encode( + [ + 'nested_type' => ['nested_property' => '1995-07-20'], + 'simple_property' => 'Test String', + // Omit 'nullable_property' to test null serialization + 'date_property' => '2023-01-01', + 'datetime_property' => '2023-01-01T12:34:56Z', + 'string_array' => ['one', 'two', 'three'], + 'map_property' => ['key1' => 1, 'key2' => 2], + 'object_array' => [ + 1 => ['nested_property' => '2021-07-20'], + 2 => null, // Testing nullable objects in array + ], + 'nested_array' => [ + 1 => [1 => 'value1', 2 => null], // Testing nullable strings in nested array + 2 => [3 => 'value3', 4 => 'value4'] + ], + 'dates_array' => ['2023-01-01', null, '2023-03-01'] // Testing nullable dates in array> + ], + JSON_THROW_ON_ERROR + ); + + $object = Type::fromJson($expectedJson); + + // Check that nullable property is null and not included in JSON + $this->assertNull($object->nullableProperty, 'Nullable property should be null.'); + + // Check date properties + $this->assertInstanceOf(DateTime::class, $object->dateProperty, 'date_property should be a DateTime instance.'); + $this->assertEquals('2023-01-01', $object->dateProperty->format('Y-m-d'), 'date_property should have the correct date.'); + $this->assertInstanceOf(DateTime::class, $object->datetimeProperty, 'datetime_property should be a DateTime instance.'); + $this->assertEquals('2023-01-01 12:34:56', $object->datetimeProperty->format('Y-m-d H:i:s'), 'datetime_property should have the correct datetime.'); + + // Check scalar arrays + $this->assertEquals(['one', 'two', 'three'], $object->stringArray, 'string_array should match the original data.'); + $this->assertEquals(['key1' => 1, 'key2' => 2], $object->mapProperty, 'map_property should match the original data.'); + + // Check object array with nullable elements + $this->assertInstanceOf(Nested::class, $object->objectArray[1], 'object_array[1] should be an instance of TestNestedType1.'); + $this->assertEquals('2021-07-20', $object->objectArray[1]->nestedProperty->format('Y-m-d'), 'object_array[1]->nestedProperty should match the original data.'); + $this->assertNull($object->objectArray[2], 'object_array[2] should be null.'); + + // Check nested array with nullable strings + $this->assertEquals('value1', $object->nestedArray[1][1], 'nested_array[1][1] should match the original data.'); + $this->assertNull($object->nestedArray[1][2], 'nested_array[1][2] should be null.'); + $this->assertEquals('value3', $object->nestedArray[2][3], 'nested_array[2][3] should match the original data.'); + $this->assertEquals('value4', $object->nestedArray[2][4], 'nested_array[2][4] should match the original data.'); + + // Check dates array with nullable DateTime objects + $this->assertInstanceOf(DateTime::class, $object->datesArray[0], 'dates_array[0] should be a DateTime instance.'); + $this->assertEquals('2023-01-01', $object->datesArray[0]->format('Y-m-d'), 'dates_array[0] should have the correct date.'); + $this->assertNull($object->datesArray[1], 'dates_array[1] should be null.'); + $this->assertInstanceOf(DateTime::class, $object->datesArray[2], 'dates_array[2] should be a DateTime instance.'); + $this->assertEquals('2023-03-01', $object->datesArray[2]->format('Y-m-d'), 'dates_array[2] should have the correct date.'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'The serialized JSON does not match the original JSON.'); + } +} diff --git a/seed/php-model/nullable-allof-extends/tests/Core/Json/InvalidTest.php b/seed/php-model/nullable-allof-extends/tests/Core/Json/InvalidTest.php new file mode 100644 index 000000000000..7d1d79406a5a --- /dev/null +++ b/seed/php-model/nullable-allof-extends/tests/Core/Json/InvalidTest.php @@ -0,0 +1,42 @@ +integerProperty = $values['integerProperty']; + } +} + +class InvalidTest extends TestCase +{ + public function testInvalidJsonThrowsException(): void + { + $this->expectException(\TypeError::class); + $json = json_encode( + [ + 'integer_property' => 'not_an_integer' + ], + JSON_THROW_ON_ERROR + ); + Invalid::fromJson($json); + } +} diff --git a/seed/php-model/nullable-allof-extends/tests/Core/Json/NestedUnionArrayTest.php b/seed/php-model/nullable-allof-extends/tests/Core/Json/NestedUnionArrayTest.php new file mode 100644 index 000000000000..0fcdd06667e5 --- /dev/null +++ b/seed/php-model/nullable-allof-extends/tests/Core/Json/NestedUnionArrayTest.php @@ -0,0 +1,89 @@ +nestedProperty = $values['nestedProperty']; + } +} + +class NestedUnionArray extends JsonSerializableType +{ + /** + * @var array> $nestedArray + */ + #[ArrayType(['integer' => ['integer' => new Union(UnionObject::class, 'null', 'date')]])] + #[JsonProperty('nested_array')] + public array $nestedArray; + + /** + * @param array{ + * nestedArray: array>, + * } $values + */ + public function __construct( + array $values, + ) { + $this->nestedArray = $values['nestedArray']; + } +} + +class NestedUnionArrayTest extends TestCase +{ + public function testNestedUnionArray(): void + { + $expectedJson = json_encode( + [ + 'nested_array' => [ + 1 => [ + 1 => ['nested_property' => 'Nested One'], + 2 => null, + 4 => '2023-01-02' + ], + 2 => [ + 5 => ['nested_property' => 'Nested Two'], + 7 => '2023-02-02' + ] + ] + ], + JSON_THROW_ON_ERROR + ); + + $object = NestedUnionArray::fromJson($expectedJson); + $this->assertInstanceOf(UnionObject::class, $object->nestedArray[1][1], 'nested_array[1][1] should be an instance of Object.'); + $this->assertEquals('Nested One', $object->nestedArray[1][1]->nestedProperty, 'nested_array[1][1]->nestedProperty should match the original data.'); + $this->assertNull($object->nestedArray[1][2], 'nested_array[1][2] should be null.'); + $this->assertInstanceOf(DateTime::class, $object->nestedArray[1][4], 'nested_array[1][4] should be a DateTime instance.'); + $this->assertEquals('2023-01-02T00:00:00+00:00', $object->nestedArray[1][4]->format(Constant::DateTimeFormat), 'nested_array[1][4] should have the correct datetime.'); + $this->assertInstanceOf(UnionObject::class, $object->nestedArray[2][5], 'nested_array[2][5] should be an instance of Object.'); + $this->assertEquals('Nested Two', $object->nestedArray[2][5]->nestedProperty, 'nested_array[2][5]->nestedProperty should match the original data.'); + $this->assertInstanceOf(DateTime::class, $object->nestedArray[2][7], 'nested_array[1][4] should be a DateTime instance.'); + $this->assertEquals('2023-02-02', $object->nestedArray[2][7]->format('Y-m-d'), 'nested_array[1][4] should have the correct date.'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for nested_array.'); + } +} diff --git a/seed/php-model/nullable-allof-extends/tests/Core/Json/NullPropertyTest.php b/seed/php-model/nullable-allof-extends/tests/Core/Json/NullPropertyTest.php new file mode 100644 index 000000000000..ce20a2442825 --- /dev/null +++ b/seed/php-model/nullable-allof-extends/tests/Core/Json/NullPropertyTest.php @@ -0,0 +1,53 @@ +nonNullProperty = $values['nonNullProperty']; + $this->nullProperty = $values['nullProperty'] ?? null; + } +} + +class NullPropertyTest extends TestCase +{ + public function testNullPropertiesAreOmitted(): void + { + $object = new NullProperty( + [ + "nonNullProperty" => "Test String", + "nullProperty" => null + ] + ); + + $serialized = $object->jsonSerialize(); + $this->assertArrayHasKey('non_null_property', $serialized, 'non_null_property should be present in the serialized JSON.'); + $this->assertArrayNotHasKey('null_property', $serialized, 'null_property should be omitted from the serialized JSON.'); + $this->assertEquals('Test String', $serialized['non_null_property'], 'non_null_property should have the correct value.'); + } +} diff --git a/seed/php-model/nullable-allof-extends/tests/Core/Json/NullableArrayTest.php b/seed/php-model/nullable-allof-extends/tests/Core/Json/NullableArrayTest.php new file mode 100644 index 000000000000..fe0f19de6b13 --- /dev/null +++ b/seed/php-model/nullable-allof-extends/tests/Core/Json/NullableArrayTest.php @@ -0,0 +1,49 @@ + $nullableStringArray + */ + #[ArrayType([new Union('string', 'null')])] + #[JsonProperty('nullable_string_array')] + public array $nullableStringArray; + + /** + * @param array{ + * nullableStringArray: array, + * } $values + */ + public function __construct( + array $values, + ) { + $this->nullableStringArray = $values['nullableStringArray']; + } +} + +class NullableArrayTest extends TestCase +{ + public function testNullableArray(): void + { + $expectedJson = json_encode( + [ + 'nullable_string_array' => ['one', null, 'three'] + ], + JSON_THROW_ON_ERROR + ); + + $object = NullableArray::fromJson($expectedJson); + $this->assertEquals(['one', null, 'three'], $object->nullableStringArray, 'nullable_string_array should match the original data.'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for nullable_string_array.'); + } +} diff --git a/seed/php-model/nullable-allof-extends/tests/Core/Json/ScalarTest.php b/seed/php-model/nullable-allof-extends/tests/Core/Json/ScalarTest.php new file mode 100644 index 000000000000..604b7c0b959b --- /dev/null +++ b/seed/php-model/nullable-allof-extends/tests/Core/Json/ScalarTest.php @@ -0,0 +1,116 @@ + $intFloatArray + */ + #[ArrayType([new Union('integer', 'float')])] + #[JsonProperty('int_float_array')] + public array $intFloatArray; + + /** + * @var array $floatArray + */ + #[ArrayType(['float'])] + #[JsonProperty('float_array')] + public array $floatArray; + + /** + * @var bool|null $nullableBooleanProperty + */ + #[JsonProperty('nullable_boolean_property')] + public ?bool $nullableBooleanProperty; + + /** + * @param array{ + * integerProperty: int, + * floatProperty: float, + * otherFloatProperty: float, + * booleanProperty: bool, + * stringProperty: string, + * intFloatArray: array, + * floatArray: array, + * nullableBooleanProperty?: bool|null, + * } $values + */ + public function __construct( + array $values, + ) { + $this->integerProperty = $values['integerProperty']; + $this->floatProperty = $values['floatProperty']; + $this->otherFloatProperty = $values['otherFloatProperty']; + $this->booleanProperty = $values['booleanProperty']; + $this->stringProperty = $values['stringProperty']; + $this->intFloatArray = $values['intFloatArray']; + $this->floatArray = $values['floatArray']; + $this->nullableBooleanProperty = $values['nullableBooleanProperty'] ?? null; + } +} + +class ScalarTest extends TestCase +{ + public function testAllScalarTypesIncludingFloat(): void + { + $expectedJson = json_encode( + [ + 'integer_property' => 42, + 'float_property' => 3.14159, + 'other_float_property' => 3, + 'boolean_property' => true, + 'string_property' => 'Hello, World!', + 'int_float_array' => [1, 2.5, 3, 4.75], + 'float_array' => [1, 2, 3, 4] // Ensure we handle "integer-looking" floats + ], + JSON_THROW_ON_ERROR + ); + + $object = Scalar::fromJson($expectedJson); + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals(3.14159, $object->floatProperty, 'float_property should be 3.14159.'); + $this->assertTrue($object->booleanProperty, 'boolean_property should be true.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + $this->assertNull($object->nullableBooleanProperty, 'nullable_boolean_property should be null.'); + $this->assertEquals([1, 2.5, 3, 4.75], $object->intFloatArray, 'int_float_array should match the original data.'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTest.'); + } +} diff --git a/seed/php-model/nullable-allof-extends/tests/Core/Json/TraitTest.php b/seed/php-model/nullable-allof-extends/tests/Core/Json/TraitTest.php new file mode 100644 index 000000000000..837f239115f7 --- /dev/null +++ b/seed/php-model/nullable-allof-extends/tests/Core/Json/TraitTest.php @@ -0,0 +1,60 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $expectedJson = json_encode( + [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ], + JSON_THROW_ON_ERROR + ); + + $object = TypeWithTrait::fromJson($expectedJson); + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + } +} diff --git a/seed/php-model/nullable-allof-extends/tests/Core/Json/UnionArrayTest.php b/seed/php-model/nullable-allof-extends/tests/Core/Json/UnionArrayTest.php new file mode 100644 index 000000000000..33899cd3f051 --- /dev/null +++ b/seed/php-model/nullable-allof-extends/tests/Core/Json/UnionArrayTest.php @@ -0,0 +1,57 @@ + $mixedDates + */ + #[ArrayType(['integer' => new Union('datetime', 'string', 'null')])] + #[JsonProperty('mixed_dates')] + public array $mixedDates; + + /** + * @param array{ + * mixedDates: array, + * } $values + */ + public function __construct( + array $values, + ) { + $this->mixedDates = $values['mixedDates']; + } +} + +class UnionArrayTest extends TestCase +{ + public function testUnionArray(): void + { + $expectedJson = json_encode( + [ + 'mixed_dates' => [ + 1 => '2023-01-01T12:00:00Z', + 2 => null, + 3 => 'Some String' + ] + ], + JSON_THROW_ON_ERROR + ); + + $object = UnionArray::fromJson($expectedJson); + $this->assertInstanceOf(DateTime::class, $object->mixedDates[1], 'mixed_dates[1] should be a DateTime instance.'); + $this->assertEquals('2023-01-01 12:00:00', $object->mixedDates[1]->format('Y-m-d H:i:s'), 'mixed_dates[1] should have the correct datetime.'); + $this->assertNull($object->mixedDates[2], 'mixed_dates[2] should be null.'); + $this->assertEquals('Some String', $object->mixedDates[3], 'mixed_dates[3] should be "Some String".'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for mixed_dates.'); + } +} diff --git a/seed/php-model/nullable-allof-extends/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/nullable-allof-extends/tests/Core/Json/UnionPropertyTest.php new file mode 100644 index 000000000000..3119baace627 --- /dev/null +++ b/seed/php-model/nullable-allof-extends/tests/Core/Json/UnionPropertyTest.php @@ -0,0 +1,115 @@ + 'integer'], UnionProperty::class)] + #[JsonProperty('complexUnion')] + public mixed $complexUnion; + + /** + * @param array{ + * complexUnion: string|int|null|array|UnionProperty + * } $values + */ + public function __construct( + array $values, + ) { + $this->complexUnion = $values['complexUnion']; + } +} + +class UnionPropertyTest extends TestCase +{ + public function testWithMapOfIntToInt(): void + { + $expectedJson = json_encode( + [ + 'complexUnion' => [1 => 100, 2 => 200] + ], + JSON_THROW_ON_ERROR + ); + + $object = UnionProperty::fromJson($expectedJson); + $this->assertIsArray($object->complexUnion, 'complexUnion should be an array.'); + $this->assertEquals([1 => 100, 2 => 200], $object->complexUnion, 'complexUnion should match the original map of int => int.'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); + } + + public function testWithNestedUnionPropertyType(): void + { + $expectedJson = json_encode( + [ + 'complexUnion' => new UnionProperty( + [ + 'complexUnion' => 'Nested String' + ] + ) + ], + JSON_THROW_ON_ERROR + ); + + $object = UnionProperty::fromJson($expectedJson); + $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); + $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); + } + + public function testWithNull(): void + { + $expectedJson = json_encode( + [], + JSON_THROW_ON_ERROR + ); + + $object = UnionProperty::fromJson($expectedJson); + $this->assertNull($object->complexUnion, 'complexUnion should be null.'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); + } + + public function testWithInteger(): void + { + $expectedJson = json_encode( + [ + 'complexUnion' => 42 + ], + JSON_THROW_ON_ERROR + ); + + $object = UnionProperty::fromJson($expectedJson); + $this->assertIsInt($object->complexUnion, 'complexUnion should be an integer.'); + $this->assertEquals(42, $object->complexUnion, 'complexUnion should match the original integer.'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); + } + + public function testWithString(): void + { + $expectedJson = json_encode( + [ + 'complexUnion' => 'Some String' + ], + JSON_THROW_ON_ERROR + ); + + $object = UnionProperty::fromJson($expectedJson); + $this->assertIsString($object->complexUnion, 'complexUnion should be a string.'); + $this->assertEquals('Some String', $object->complexUnion, 'complexUnion should match the original string.'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); + } +} diff --git a/seed/php-model/nullable-optional/src/Core/Json/JsonEncoder.php b/seed/php-model/nullable-optional/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/nullable-optional/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/nullable-optional/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/nullable-optional/src/Core/Json/JsonProperty.php b/seed/php-model/nullable-optional/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/nullable-optional/src/Core/Json/JsonProperty.php +++ b/seed/php-model/nullable-optional/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/nullable-optional/src/Core/Types/ArrayType.php b/seed/php-model/nullable-optional/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/nullable-optional/src/Core/Types/ArrayType.php +++ b/seed/php-model/nullable-optional/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/nullable-optional/src/Core/Types/Constant.php b/seed/php-model/nullable-optional/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/nullable-optional/src/Core/Types/Constant.php +++ b/seed/php-model/nullable-optional/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/nullable-optional/src/Core/Types/Union.php b/seed/php-model/nullable-optional/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/nullable-optional/src/Core/Types/Union.php +++ b/seed/php-model/nullable-optional/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/nullable-optional/src/NullableOptional/Address.php b/seed/php-model/nullable-optional/src/NullableOptional/Address.php index 4fe81974666c..a80d4fc07dcd 100644 --- a/seed/php-model/nullable-optional/src/NullableOptional/Address.php +++ b/seed/php-model/nullable-optional/src/NullableOptional/Address.php @@ -65,15 +65,21 @@ class Address extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->street = $values['street'];$this->city = $values['city'] ?? null;$this->state = $values['state'] ?? null;$this->zipCode = $values['zipCode'];$this->country = $values['country'] ?? null;$this->buildingId = $values['buildingId'] ?? null;$this->tenantId = $values['tenantId'] ?? null; + ) { + $this->street = $values['street']; + $this->city = $values['city'] ?? null; + $this->state = $values['state'] ?? null; + $this->zipCode = $values['zipCode']; + $this->country = $values['country'] ?? null; + $this->buildingId = $values['buildingId'] ?? null; + $this->tenantId = $values['tenantId'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/nullable-optional/src/NullableOptional/ComplexProfile.php b/seed/php-model/nullable-optional/src/NullableOptional/ComplexProfile.php index 0170989b2936..9385a44da820 100644 --- a/seed/php-model/nullable-optional/src/NullableOptional/ComplexProfile.php +++ b/seed/php-model/nullable-optional/src/NullableOptional/ComplexProfile.php @@ -151,15 +151,33 @@ class ComplexProfile extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->nullableRole = $values['nullableRole'] ?? null;$this->optionalRole = $values['optionalRole'] ?? null;$this->optionalNullableRole = $values['optionalNullableRole'] ?? null;$this->nullableStatus = $values['nullableStatus'] ?? null;$this->optionalStatus = $values['optionalStatus'] ?? null;$this->optionalNullableStatus = $values['optionalNullableStatus'] ?? null;$this->nullableNotification = $values['nullableNotification'] ?? null;$this->optionalNotification = $values['optionalNotification'] ?? null;$this->optionalNullableNotification = $values['optionalNullableNotification'] ?? null;$this->nullableSearchResult = $values['nullableSearchResult'] ?? null;$this->optionalSearchResult = $values['optionalSearchResult'] ?? null;$this->nullableArray = $values['nullableArray'] ?? null;$this->optionalArray = $values['optionalArray'] ?? null;$this->optionalNullableArray = $values['optionalNullableArray'] ?? null;$this->nullableListOfNullables = $values['nullableListOfNullables'] ?? null;$this->nullableMapOfNullables = $values['nullableMapOfNullables'] ?? null;$this->nullableListOfUnions = $values['nullableListOfUnions'] ?? null;$this->optionalMapOfEnums = $values['optionalMapOfEnums'] ?? null; + ) { + $this->id = $values['id']; + $this->nullableRole = $values['nullableRole'] ?? null; + $this->optionalRole = $values['optionalRole'] ?? null; + $this->optionalNullableRole = $values['optionalNullableRole'] ?? null; + $this->nullableStatus = $values['nullableStatus'] ?? null; + $this->optionalStatus = $values['optionalStatus'] ?? null; + $this->optionalNullableStatus = $values['optionalNullableStatus'] ?? null; + $this->nullableNotification = $values['nullableNotification'] ?? null; + $this->optionalNotification = $values['optionalNotification'] ?? null; + $this->optionalNullableNotification = $values['optionalNullableNotification'] ?? null; + $this->nullableSearchResult = $values['nullableSearchResult'] ?? null; + $this->optionalSearchResult = $values['optionalSearchResult'] ?? null; + $this->nullableArray = $values['nullableArray'] ?? null; + $this->optionalArray = $values['optionalArray'] ?? null; + $this->optionalNullableArray = $values['optionalNullableArray'] ?? null; + $this->nullableListOfNullables = $values['nullableListOfNullables'] ?? null; + $this->nullableMapOfNullables = $values['nullableMapOfNullables'] ?? null; + $this->nullableListOfUnions = $values['nullableListOfUnions'] ?? null; + $this->optionalMapOfEnums = $values['optionalMapOfEnums'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/nullable-optional/src/NullableOptional/CreateUserRequest.php b/seed/php-model/nullable-optional/src/NullableOptional/CreateUserRequest.php index 0b6434a75f4b..05691d271e31 100644 --- a/seed/php-model/nullable-optional/src/NullableOptional/CreateUserRequest.php +++ b/seed/php-model/nullable-optional/src/NullableOptional/CreateUserRequest.php @@ -41,15 +41,18 @@ class CreateUserRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->username = $values['username'];$this->email = $values['email'] ?? null;$this->phone = $values['phone'] ?? null;$this->address = $values['address'] ?? null; + ) { + $this->username = $values['username']; + $this->email = $values['email'] ?? null; + $this->phone = $values['phone'] ?? null; + $this->address = $values['address'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/nullable-optional/src/NullableOptional/DeserializationTestRequest.php b/seed/php-model/nullable-optional/src/NullableOptional/DeserializationTestRequest.php index 347a84600ca3..872bcb0c36cc 100644 --- a/seed/php-model/nullable-optional/src/NullableOptional/DeserializationTestRequest.php +++ b/seed/php-model/nullable-optional/src/NullableOptional/DeserializationTestRequest.php @@ -101,15 +101,26 @@ class DeserializationTestRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->requiredString = $values['requiredString'];$this->nullableString = $values['nullableString'] ?? null;$this->optionalString = $values['optionalString'] ?? null;$this->optionalNullableString = $values['optionalNullableString'] ?? null;$this->nullableEnum = $values['nullableEnum'] ?? null;$this->optionalEnum = $values['optionalEnum'] ?? null;$this->nullableUnion = $values['nullableUnion'] ?? null;$this->optionalUnion = $values['optionalUnion'] ?? null;$this->nullableList = $values['nullableList'] ?? null;$this->nullableMap = $values['nullableMap'] ?? null;$this->nullableObject = $values['nullableObject'] ?? null;$this->optionalObject = $values['optionalObject'] ?? null; + ) { + $this->requiredString = $values['requiredString']; + $this->nullableString = $values['nullableString'] ?? null; + $this->optionalString = $values['optionalString'] ?? null; + $this->optionalNullableString = $values['optionalNullableString'] ?? null; + $this->nullableEnum = $values['nullableEnum'] ?? null; + $this->optionalEnum = $values['optionalEnum'] ?? null; + $this->nullableUnion = $values['nullableUnion'] ?? null; + $this->optionalUnion = $values['optionalUnion'] ?? null; + $this->nullableList = $values['nullableList'] ?? null; + $this->nullableMap = $values['nullableMap'] ?? null; + $this->nullableObject = $values['nullableObject'] ?? null; + $this->optionalObject = $values['optionalObject'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/nullable-optional/src/NullableOptional/DeserializationTestResponse.php b/seed/php-model/nullable-optional/src/NullableOptional/DeserializationTestResponse.php index c59b29c7464e..b85f9d26c1af 100644 --- a/seed/php-model/nullable-optional/src/NullableOptional/DeserializationTestResponse.php +++ b/seed/php-model/nullable-optional/src/NullableOptional/DeserializationTestResponse.php @@ -46,15 +46,18 @@ class DeserializationTestResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->echo = $values['echo'];$this->processedAt = $values['processedAt'];$this->nullCount = $values['nullCount'];$this->presentFieldsCount = $values['presentFieldsCount']; + ) { + $this->echo = $values['echo']; + $this->processedAt = $values['processedAt']; + $this->nullCount = $values['nullCount']; + $this->presentFieldsCount = $values['presentFieldsCount']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/nullable-optional/src/NullableOptional/Document.php b/seed/php-model/nullable-optional/src/NullableOptional/Document.php index d035a9ba06c4..8f65bb8b7350 100644 --- a/seed/php-model/nullable-optional/src/NullableOptional/Document.php +++ b/seed/php-model/nullable-optional/src/NullableOptional/Document.php @@ -49,15 +49,19 @@ class Document extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->title = $values['title'];$this->content = $values['content'];$this->author = $values['author'] ?? null;$this->tags = $values['tags'] ?? null; + ) { + $this->id = $values['id']; + $this->title = $values['title']; + $this->content = $values['content']; + $this->author = $values['author'] ?? null; + $this->tags = $values['tags'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/nullable-optional/src/NullableOptional/EmailNotification.php b/seed/php-model/nullable-optional/src/NullableOptional/EmailNotification.php index 1b8ed9d092c8..190b35ce8526 100644 --- a/seed/php-model/nullable-optional/src/NullableOptional/EmailNotification.php +++ b/seed/php-model/nullable-optional/src/NullableOptional/EmailNotification.php @@ -34,15 +34,17 @@ class EmailNotification extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->emailAddress = $values['emailAddress'];$this->subject = $values['subject'];$this->htmlContent = $values['htmlContent'] ?? null; + ) { + $this->emailAddress = $values['emailAddress']; + $this->subject = $values['subject']; + $this->htmlContent = $values['htmlContent'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/nullable-optional/src/NullableOptional/NotificationMethod.php b/seed/php-model/nullable-optional/src/NullableOptional/NotificationMethod.php index e6c5044fcea1..f134a5d63daa 100644 --- a/seed/php-model/nullable-optional/src/NullableOptional/NotificationMethod.php +++ b/seed/php-model/nullable-optional/src/NullableOptional/NotificationMethod.php @@ -49,16 +49,17 @@ class NotificationMethod extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param EmailNotification $email * @return NotificationMethod */ - public static function email(EmailNotification $email): NotificationMethod { + public static function email(EmailNotification $email): NotificationMethod + { return new NotificationMethod([ 'type' => 'email', 'value' => $email, @@ -69,7 +70,8 @@ public static function email(EmailNotification $email): NotificationMethod { * @param SmsNotification $sms * @return NotificationMethod */ - public static function sms(SmsNotification $sms): NotificationMethod { + public static function sms(SmsNotification $sms): NotificationMethod + { return new NotificationMethod([ 'type' => 'sms', 'value' => $sms, @@ -80,7 +82,8 @@ public static function sms(SmsNotification $sms): NotificationMethod { * @param PushNotification $push * @return NotificationMethod */ - public static function push(PushNotification $push): NotificationMethod { + public static function push(PushNotification $push): NotificationMethod + { return new NotificationMethod([ 'type' => 'push', 'value' => $push, @@ -90,81 +93,89 @@ public static function push(PushNotification $push): NotificationMethod { /** * @return bool */ - public function isEmail(): bool { - return $this->value instanceof EmailNotification&& $this->type === 'email'; + public function isEmail(): bool + { + return $this->value instanceof EmailNotification && $this->type === 'email'; } /** * @return EmailNotification */ - public function asEmail(): EmailNotification { - if (!($this->value instanceof EmailNotification&& $this->type === 'email')){ + public function asEmail(): EmailNotification + { + if (!($this->value instanceof EmailNotification && $this->type === 'email')) { throw new Exception( "Expected email; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isSms(): bool { - return $this->value instanceof SmsNotification&& $this->type === 'sms'; + public function isSms(): bool + { + return $this->value instanceof SmsNotification && $this->type === 'sms'; } /** * @return SmsNotification */ - public function asSms(): SmsNotification { - if (!($this->value instanceof SmsNotification&& $this->type === 'sms')){ + public function asSms(): SmsNotification + { + if (!($this->value instanceof SmsNotification && $this->type === 'sms')) { throw new Exception( "Expected sms; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isPush(): bool { - return $this->value instanceof PushNotification&& $this->type === 'push'; + public function isPush(): bool + { + return $this->value instanceof PushNotification && $this->type === 'push'; } /** * @return PushNotification */ - public function asPush(): PushNotification { - if (!($this->value instanceof PushNotification&& $this->type === 'push')){ + public function asPush(): PushNotification + { + if (!($this->value instanceof PushNotification && $this->type === 'push')) { throw new Exception( "Expected push; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'email': $value = $this->asEmail()->jsonSerialize(); $result = array_merge($value, $result); @@ -179,26 +190,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -207,22 +219,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'email': $args['value'] = EmailNotification::jsonDeserialize($data); break; @@ -237,7 +250,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/nullable-optional/src/NullableOptional/Organization.php b/seed/php-model/nullable-optional/src/NullableOptional/Organization.php index 5ee48192c4cf..9bb456fa680d 100644 --- a/seed/php-model/nullable-optional/src/NullableOptional/Organization.php +++ b/seed/php-model/nullable-optional/src/NullableOptional/Organization.php @@ -41,15 +41,18 @@ class Organization extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->name = $values['name'];$this->domain = $values['domain'] ?? null;$this->employeeCount = $values['employeeCount'] ?? null; + ) { + $this->id = $values['id']; + $this->name = $values['name']; + $this->domain = $values['domain'] ?? null; + $this->employeeCount = $values['employeeCount'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/nullable-optional/src/NullableOptional/PushNotification.php b/seed/php-model/nullable-optional/src/NullableOptional/PushNotification.php index 2602f3219aea..8a1a96efeb8f 100644 --- a/seed/php-model/nullable-optional/src/NullableOptional/PushNotification.php +++ b/seed/php-model/nullable-optional/src/NullableOptional/PushNotification.php @@ -41,15 +41,18 @@ class PushNotification extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->deviceToken = $values['deviceToken'];$this->title = $values['title'];$this->body = $values['body'];$this->badge = $values['badge'] ?? null; + ) { + $this->deviceToken = $values['deviceToken']; + $this->title = $values['title']; + $this->body = $values['body']; + $this->badge = $values['badge'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/nullable-optional/src/NullableOptional/SearchResult.php b/seed/php-model/nullable-optional/src/NullableOptional/SearchResult.php index 7aa9a2367d48..93ffad69609b 100644 --- a/seed/php-model/nullable-optional/src/NullableOptional/SearchResult.php +++ b/seed/php-model/nullable-optional/src/NullableOptional/SearchResult.php @@ -49,16 +49,17 @@ class SearchResult extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param UserResponse $user * @return SearchResult */ - public static function user(UserResponse $user): SearchResult { + public static function user(UserResponse $user): SearchResult + { return new SearchResult([ 'type' => 'user', 'value' => $user, @@ -69,7 +70,8 @@ public static function user(UserResponse $user): SearchResult { * @param Organization $organization * @return SearchResult */ - public static function organization(Organization $organization): SearchResult { + public static function organization(Organization $organization): SearchResult + { return new SearchResult([ 'type' => 'organization', 'value' => $organization, @@ -80,7 +82,8 @@ public static function organization(Organization $organization): SearchResult { * @param Document $document * @return SearchResult */ - public static function document(Document $document): SearchResult { + public static function document(Document $document): SearchResult + { return new SearchResult([ 'type' => 'document', 'value' => $document, @@ -90,81 +93,89 @@ public static function document(Document $document): SearchResult { /** * @return bool */ - public function isUser(): bool { - return $this->value instanceof UserResponse&& $this->type === 'user'; + public function isUser(): bool + { + return $this->value instanceof UserResponse && $this->type === 'user'; } /** * @return UserResponse */ - public function asUser(): UserResponse { - if (!($this->value instanceof UserResponse&& $this->type === 'user')){ + public function asUser(): UserResponse + { + if (!($this->value instanceof UserResponse && $this->type === 'user')) { throw new Exception( "Expected user; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isOrganization(): bool { - return $this->value instanceof Organization&& $this->type === 'organization'; + public function isOrganization(): bool + { + return $this->value instanceof Organization && $this->type === 'organization'; } /** * @return Organization */ - public function asOrganization(): Organization { - if (!($this->value instanceof Organization&& $this->type === 'organization')){ + public function asOrganization(): Organization + { + if (!($this->value instanceof Organization && $this->type === 'organization')) { throw new Exception( "Expected organization; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isDocument(): bool { - return $this->value instanceof Document&& $this->type === 'document'; + public function isDocument(): bool + { + return $this->value instanceof Document && $this->type === 'document'; } /** * @return Document */ - public function asDocument(): Document { - if (!($this->value instanceof Document&& $this->type === 'document')){ + public function asDocument(): Document + { + if (!($this->value instanceof Document && $this->type === 'document')) { throw new Exception( "Expected document; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'user': $value = $this->asUser()->jsonSerialize(); $result = array_merge($value, $result); @@ -179,26 +190,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -207,22 +219,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'user': $args['value'] = UserResponse::jsonDeserialize($data); break; @@ -237,7 +250,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/nullable-optional/src/NullableOptional/SmsNotification.php b/seed/php-model/nullable-optional/src/NullableOptional/SmsNotification.php index e6884f8c44f9..7cb1abbed5a8 100644 --- a/seed/php-model/nullable-optional/src/NullableOptional/SmsNotification.php +++ b/seed/php-model/nullable-optional/src/NullableOptional/SmsNotification.php @@ -34,15 +34,17 @@ class SmsNotification extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->phoneNumber = $values['phoneNumber'];$this->message = $values['message'];$this->shortCode = $values['shortCode'] ?? null; + ) { + $this->phoneNumber = $values['phoneNumber']; + $this->message = $values['message']; + $this->shortCode = $values['shortCode'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/nullable-optional/src/NullableOptional/UpdateUserRequest.php b/seed/php-model/nullable-optional/src/NullableOptional/UpdateUserRequest.php index 1cce2dc4ba34..63fba2adbe3e 100644 --- a/seed/php-model/nullable-optional/src/NullableOptional/UpdateUserRequest.php +++ b/seed/php-model/nullable-optional/src/NullableOptional/UpdateUserRequest.php @@ -44,15 +44,18 @@ class UpdateUserRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->username = $values['username'] ?? null;$this->email = $values['email'] ?? null;$this->phone = $values['phone'] ?? null;$this->address = $values['address'] ?? null; + ) { + $this->username = $values['username'] ?? null; + $this->email = $values['email'] ?? null; + $this->phone = $values['phone'] ?? null; + $this->address = $values['address'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/nullable-optional/src/NullableOptional/UserProfile.php b/seed/php-model/nullable-optional/src/NullableOptional/UserProfile.php index 38e5730c2c9d..7765b177d3a2 100644 --- a/seed/php-model/nullable-optional/src/NullableOptional/UserProfile.php +++ b/seed/php-model/nullable-optional/src/NullableOptional/UserProfile.php @@ -145,15 +145,32 @@ class UserProfile extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->username = $values['username'];$this->nullableString = $values['nullableString'] ?? null;$this->nullableInteger = $values['nullableInteger'] ?? null;$this->nullableBoolean = $values['nullableBoolean'] ?? null;$this->nullableDate = $values['nullableDate'] ?? null;$this->nullableObject = $values['nullableObject'] ?? null;$this->nullableList = $values['nullableList'] ?? null;$this->nullableMap = $values['nullableMap'] ?? null;$this->optionalString = $values['optionalString'] ?? null;$this->optionalInteger = $values['optionalInteger'] ?? null;$this->optionalBoolean = $values['optionalBoolean'] ?? null;$this->optionalDate = $values['optionalDate'] ?? null;$this->optionalObject = $values['optionalObject'] ?? null;$this->optionalList = $values['optionalList'] ?? null;$this->optionalMap = $values['optionalMap'] ?? null;$this->optionalNullableString = $values['optionalNullableString'] ?? null;$this->optionalNullableObject = $values['optionalNullableObject'] ?? null; + ) { + $this->id = $values['id']; + $this->username = $values['username']; + $this->nullableString = $values['nullableString'] ?? null; + $this->nullableInteger = $values['nullableInteger'] ?? null; + $this->nullableBoolean = $values['nullableBoolean'] ?? null; + $this->nullableDate = $values['nullableDate'] ?? null; + $this->nullableObject = $values['nullableObject'] ?? null; + $this->nullableList = $values['nullableList'] ?? null; + $this->nullableMap = $values['nullableMap'] ?? null; + $this->optionalString = $values['optionalString'] ?? null; + $this->optionalInteger = $values['optionalInteger'] ?? null; + $this->optionalBoolean = $values['optionalBoolean'] ?? null; + $this->optionalDate = $values['optionalDate'] ?? null; + $this->optionalObject = $values['optionalObject'] ?? null; + $this->optionalList = $values['optionalList'] ?? null; + $this->optionalMap = $values['optionalMap'] ?? null; + $this->optionalNullableString = $values['optionalNullableString'] ?? null; + $this->optionalNullableObject = $values['optionalNullableObject'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/nullable-optional/src/NullableOptional/UserResponse.php b/seed/php-model/nullable-optional/src/NullableOptional/UserResponse.php index a4909bcea200..7153be5b7c63 100644 --- a/seed/php-model/nullable-optional/src/NullableOptional/UserResponse.php +++ b/seed/php-model/nullable-optional/src/NullableOptional/UserResponse.php @@ -64,15 +64,21 @@ class UserResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->username = $values['username'];$this->email = $values['email'] ?? null;$this->phone = $values['phone'] ?? null;$this->createdAt = $values['createdAt'];$this->updatedAt = $values['updatedAt'] ?? null;$this->address = $values['address'] ?? null; + ) { + $this->id = $values['id']; + $this->username = $values['username']; + $this->email = $values['email'] ?? null; + $this->phone = $values['phone'] ?? null; + $this->createdAt = $values['createdAt']; + $this->updatedAt = $values['updatedAt'] ?? null; + $this->address = $values['address'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/nullable-optional/src/NullableOptional/UserRole.php b/seed/php-model/nullable-optional/src/NullableOptional/UserRole.php index 76f73b2e8b4c..ce85123fbdb3 100644 --- a/seed/php-model/nullable-optional/src/NullableOptional/UserRole.php +++ b/seed/php-model/nullable-optional/src/NullableOptional/UserRole.php @@ -2,8 +2,8 @@ namespace Seed\NullableOptional; -enum UserRole - : string { +enum UserRole: string +{ case Admin = "ADMIN"; case User = "USER"; case Guest = "GUEST"; diff --git a/seed/php-model/nullable-optional/src/NullableOptional/UserStatus.php b/seed/php-model/nullable-optional/src/NullableOptional/UserStatus.php index ea0dd19ba7f1..ab9a1ddc8949 100644 --- a/seed/php-model/nullable-optional/src/NullableOptional/UserStatus.php +++ b/seed/php-model/nullable-optional/src/NullableOptional/UserStatus.php @@ -2,8 +2,8 @@ namespace Seed\NullableOptional; -enum UserStatus - : string { +enum UserStatus: string +{ case Active = "active"; case Inactive = "inactive"; case Suspended = "suspended"; diff --git a/seed/php-model/nullable-optional/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/nullable-optional/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/nullable-optional/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/nullable-optional/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/nullable-optional/tests/Core/Json/EnumTest.php b/seed/php-model/nullable-optional/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/nullable-optional/tests/Core/Json/EnumTest.php +++ b/seed/php-model/nullable-optional/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/nullable-optional/tests/Core/Json/TraitTest.php b/seed/php-model/nullable-optional/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/nullable-optional/tests/Core/Json/TraitTest.php +++ b/seed/php-model/nullable-optional/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/nullable-optional/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/nullable-optional/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/nullable-optional/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/nullable-optional/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/nullable-request-body/src/Core/Json/JsonEncoder.php b/seed/php-model/nullable-request-body/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/nullable-request-body/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/nullable-request-body/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/nullable-request-body/src/Core/Json/JsonProperty.php b/seed/php-model/nullable-request-body/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/nullable-request-body/src/Core/Json/JsonProperty.php +++ b/seed/php-model/nullable-request-body/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/nullable-request-body/src/Core/Types/ArrayType.php b/seed/php-model/nullable-request-body/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/nullable-request-body/src/Core/Types/ArrayType.php +++ b/seed/php-model/nullable-request-body/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/nullable-request-body/src/Core/Types/Constant.php b/seed/php-model/nullable-request-body/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/nullable-request-body/src/Core/Types/Constant.php +++ b/seed/php-model/nullable-request-body/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/nullable-request-body/src/Core/Types/Union.php b/seed/php-model/nullable-request-body/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/nullable-request-body/src/Core/Types/Union.php +++ b/seed/php-model/nullable-request-body/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/nullable-request-body/src/PlainObject.php b/seed/php-model/nullable-request-body/src/PlainObject.php index d71efe9830f9..82a5209936a6 100644 --- a/seed/php-model/nullable-request-body/src/PlainObject.php +++ b/seed/php-model/nullable-request-body/src/PlainObject.php @@ -27,15 +27,16 @@ class PlainObject extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->id = $values['id'] ?? null;$this->name = $values['name'] ?? null; + ) { + $this->id = $values['id'] ?? null; + $this->name = $values['name'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/nullable-request-body/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/nullable-request-body/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/nullable-request-body/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/nullable-request-body/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/nullable-request-body/tests/Core/Json/EnumTest.php b/seed/php-model/nullable-request-body/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/nullable-request-body/tests/Core/Json/EnumTest.php +++ b/seed/php-model/nullable-request-body/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/nullable-request-body/tests/Core/Json/TraitTest.php b/seed/php-model/nullable-request-body/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/nullable-request-body/tests/Core/Json/TraitTest.php +++ b/seed/php-model/nullable-request-body/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/nullable-request-body/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/nullable-request-body/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/nullable-request-body/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/nullable-request-body/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/nullable/src/Core/Json/JsonEncoder.php b/seed/php-model/nullable/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/nullable/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/nullable/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/nullable/src/Core/Json/JsonProperty.php b/seed/php-model/nullable/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/nullable/src/Core/Json/JsonProperty.php +++ b/seed/php-model/nullable/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/nullable/src/Core/Types/ArrayType.php b/seed/php-model/nullable/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/nullable/src/Core/Types/ArrayType.php +++ b/seed/php-model/nullable/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/nullable/src/Core/Types/Constant.php b/seed/php-model/nullable/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/nullable/src/Core/Types/Constant.php +++ b/seed/php-model/nullable/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/nullable/src/Core/Types/Union.php b/seed/php-model/nullable/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/nullable/src/Core/Types/Union.php +++ b/seed/php-model/nullable/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/nullable/src/Nullable/Metadata.php b/seed/php-model/nullable/src/Nullable/Metadata.php index bf4771217b01..c55bc0b75a8c 100644 --- a/seed/php-model/nullable/src/Nullable/Metadata.php +++ b/seed/php-model/nullable/src/Nullable/Metadata.php @@ -59,15 +59,20 @@ class Metadata extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->createdAt = $values['createdAt'];$this->updatedAt = $values['updatedAt'];$this->avatar = $values['avatar'] ?? null;$this->activated = $values['activated'] ?? null;$this->status = $values['status'];$this->values = $values['values'] ?? null; + ) { + $this->createdAt = $values['createdAt']; + $this->updatedAt = $values['updatedAt']; + $this->avatar = $values['avatar'] ?? null; + $this->activated = $values['activated'] ?? null; + $this->status = $values['status']; + $this->values = $values['values'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/nullable/src/Nullable/Status.php b/seed/php-model/nullable/src/Nullable/Status.php index d6583dbe1987..78717658a97e 100644 --- a/seed/php-model/nullable/src/Nullable/Status.php +++ b/seed/php-model/nullable/src/Nullable/Status.php @@ -48,15 +48,16 @@ class Status extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @return Status */ - public static function active(): Status { + public static function active(): Status + { return new Status([ 'type' => 'active', 'value' => null, @@ -67,7 +68,8 @@ public static function active(): Status { * @param ?DateTime $archived * @return Status */ - public static function archived(?DateTime $archived = null): Status { + public static function archived(?DateTime $archived = null): Status + { return new Status([ 'type' => 'archived', 'value' => $archived, @@ -78,7 +80,8 @@ public static function archived(?DateTime $archived = null): Status { * @param ?DateTime $softDeleted * @return Status */ - public static function softDeleted(?DateTime $softDeleted = null): Status { + public static function softDeleted(?DateTime $softDeleted = null): Status + { return new Status([ 'type' => 'soft-deleted', 'value' => $softDeleted, @@ -88,107 +91,115 @@ public static function softDeleted(?DateTime $softDeleted = null): Status { /** * @return bool */ - public function isActive(): bool { - return is_null($this->value)&& $this->type === 'active'; + public function isActive(): bool + { + return is_null($this->value) && $this->type === 'active'; } /** * @return bool */ - public function isArchived(): bool { - return (is_null($this->value) || $this->value instanceof DateTime)&& $this->type === 'archived'; + public function isArchived(): bool + { + return (is_null($this->value) || $this->value instanceof DateTime) && $this->type === 'archived'; } /** * @return ?DateTime */ - public function asArchived(): ?DateTime { - if (!((is_null($this->value) || $this->value instanceof DateTime)&& $this->type === 'archived')){ + public function asArchived(): ?DateTime + { + if (!((is_null($this->value) || $this->value instanceof DateTime) && $this->type === 'archived')) { throw new Exception( "Expected archived; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isSoftDeleted(): bool { - return (is_null($this->value) || $this->value instanceof DateTime)&& $this->type === 'soft-deleted'; + public function isSoftDeleted(): bool + { + return (is_null($this->value) || $this->value instanceof DateTime) && $this->type === 'soft-deleted'; } /** * @return ?DateTime */ - public function asSoftDeleted(): ?DateTime { - if (!((is_null($this->value) || $this->value instanceof DateTime)&& $this->type === 'soft-deleted')){ + public function asSoftDeleted(): ?DateTime + { + if (!((is_null($this->value) || $this->value instanceof DateTime) && $this->type === 'soft-deleted')) { throw new Exception( "Expected soft-deleted; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'active': $result['active'] = []; break; case 'archived': $value = $this->asArchived(); - if (!is_null($value)){ + if (!is_null($value)) { $value = JsonSerializer::serializeDateTime($value); } $result['archived'] = $value; break; case 'soft-deleted': $value = $this->asSoftDeleted(); - if (!is_null($value)){ + if (!is_null($value)) { $value = JsonSerializer::serializeDateTime($value); } $result['soft-deleted'] = $value; break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -197,41 +208,42 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'active': $args['value'] = null; break; case 'archived': - if (!array_key_exists('archived', $data)){ + if (!array_key_exists('archived', $data)) { throw new Exception( "JSON data is missing property 'archived'", ); } - + $args['value'] = $data['archived']; break; case 'soft-deleted': - if (!array_key_exists('soft-deleted', $data)){ + if (!array_key_exists('soft-deleted', $data)) { throw new Exception( "JSON data is missing property 'soft-deleted'", ); } - + $args['value'] = $data['soft-deleted']; break; case '_unknown': @@ -239,7 +251,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/nullable/src/Nullable/User.php b/seed/php-model/nullable/src/Nullable/User.php index 216c4e194741..98590719ebc7 100644 --- a/seed/php-model/nullable/src/Nullable/User.php +++ b/seed/php-model/nullable/src/Nullable/User.php @@ -48,7 +48,7 @@ class User extends JsonSerializableType * |null * ) $favoriteNumber */ - #[JsonProperty('favorite-number'), Union('integer',new Union('float', 'null'),new Union('string', 'null'),'float')] + #[JsonProperty('favorite-number'), Union('integer', new Union('float', 'null'), new Union('string', 'null'), 'float')] public int|float|string|float|null $favoriteNumber; /** @@ -83,15 +83,22 @@ class User extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->id = $values['id'];$this->tags = $values['tags'] ?? null;$this->metadata = $values['metadata'] ?? null;$this->email = $values['email'] ?? null;$this->favoriteNumber = $values['favoriteNumber'];$this->numbers = $values['numbers'] ?? null;$this->strings = $values['strings'] ?? null; + ) { + $this->name = $values['name']; + $this->id = $values['id']; + $this->tags = $values['tags'] ?? null; + $this->metadata = $values['metadata'] ?? null; + $this->email = $values['email'] ?? null; + $this->favoriteNumber = $values['favoriteNumber']; + $this->numbers = $values['numbers'] ?? null; + $this->strings = $values['strings'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/nullable/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/nullable/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/nullable/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/nullable/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/nullable/tests/Core/Json/EnumTest.php b/seed/php-model/nullable/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/nullable/tests/Core/Json/EnumTest.php +++ b/seed/php-model/nullable/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/nullable/tests/Core/Json/TraitTest.php b/seed/php-model/nullable/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/nullable/tests/Core/Json/TraitTest.php +++ b/seed/php-model/nullable/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/nullable/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/nullable/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/nullable/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/nullable/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/oauth-client-credentials-custom/src/Auth/TokenResponse.php b/seed/php-model/oauth-client-credentials-custom/src/Auth/TokenResponse.php index 56fbe31ea0c2..9222a65c62ca 100644 --- a/seed/php-model/oauth-client-credentials-custom/src/Auth/TokenResponse.php +++ b/seed/php-model/oauth-client-credentials-custom/src/Auth/TokenResponse.php @@ -37,15 +37,17 @@ class TokenResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->accessToken = $values['accessToken'];$this->expiresIn = $values['expiresIn'];$this->refreshToken = $values['refreshToken'] ?? null; + ) { + $this->accessToken = $values['accessToken']; + $this->expiresIn = $values['expiresIn']; + $this->refreshToken = $values['refreshToken'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/oauth-client-credentials-custom/src/Core/Json/JsonEncoder.php b/seed/php-model/oauth-client-credentials-custom/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/oauth-client-credentials-custom/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/oauth-client-credentials-custom/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/oauth-client-credentials-custom/src/Core/Json/JsonProperty.php b/seed/php-model/oauth-client-credentials-custom/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/oauth-client-credentials-custom/src/Core/Json/JsonProperty.php +++ b/seed/php-model/oauth-client-credentials-custom/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/oauth-client-credentials-custom/src/Core/Types/ArrayType.php b/seed/php-model/oauth-client-credentials-custom/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/oauth-client-credentials-custom/src/Core/Types/ArrayType.php +++ b/seed/php-model/oauth-client-credentials-custom/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/oauth-client-credentials-custom/src/Core/Types/Constant.php b/seed/php-model/oauth-client-credentials-custom/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/oauth-client-credentials-custom/src/Core/Types/Constant.php +++ b/seed/php-model/oauth-client-credentials-custom/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/oauth-client-credentials-custom/src/Core/Types/Union.php b/seed/php-model/oauth-client-credentials-custom/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/oauth-client-credentials-custom/src/Core/Types/Union.php +++ b/seed/php-model/oauth-client-credentials-custom/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/oauth-client-credentials-custom/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/oauth-client-credentials-custom/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/oauth-client-credentials-custom/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/oauth-client-credentials-custom/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/oauth-client-credentials-custom/tests/Core/Json/EnumTest.php b/seed/php-model/oauth-client-credentials-custom/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/oauth-client-credentials-custom/tests/Core/Json/EnumTest.php +++ b/seed/php-model/oauth-client-credentials-custom/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/oauth-client-credentials-custom/tests/Core/Json/TraitTest.php b/seed/php-model/oauth-client-credentials-custom/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/oauth-client-credentials-custom/tests/Core/Json/TraitTest.php +++ b/seed/php-model/oauth-client-credentials-custom/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/oauth-client-credentials-custom/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/oauth-client-credentials-custom/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/oauth-client-credentials-custom/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/oauth-client-credentials-custom/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/oauth-client-credentials-default/src/Auth/TokenResponse.php b/seed/php-model/oauth-client-credentials-default/src/Auth/TokenResponse.php index bd7bda391f1b..84cb9c2068ee 100644 --- a/seed/php-model/oauth-client-credentials-default/src/Auth/TokenResponse.php +++ b/seed/php-model/oauth-client-credentials-default/src/Auth/TokenResponse.php @@ -30,15 +30,16 @@ class TokenResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->accessToken = $values['accessToken'];$this->expiresIn = $values['expiresIn']; + ) { + $this->accessToken = $values['accessToken']; + $this->expiresIn = $values['expiresIn']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/oauth-client-credentials-default/src/Core/Json/JsonEncoder.php b/seed/php-model/oauth-client-credentials-default/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/oauth-client-credentials-default/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/oauth-client-credentials-default/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/oauth-client-credentials-default/src/Core/Json/JsonProperty.php b/seed/php-model/oauth-client-credentials-default/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/oauth-client-credentials-default/src/Core/Json/JsonProperty.php +++ b/seed/php-model/oauth-client-credentials-default/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/oauth-client-credentials-default/src/Core/Types/ArrayType.php b/seed/php-model/oauth-client-credentials-default/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/oauth-client-credentials-default/src/Core/Types/ArrayType.php +++ b/seed/php-model/oauth-client-credentials-default/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/oauth-client-credentials-default/src/Core/Types/Constant.php b/seed/php-model/oauth-client-credentials-default/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/oauth-client-credentials-default/src/Core/Types/Constant.php +++ b/seed/php-model/oauth-client-credentials-default/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/oauth-client-credentials-default/src/Core/Types/Union.php b/seed/php-model/oauth-client-credentials-default/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/oauth-client-credentials-default/src/Core/Types/Union.php +++ b/seed/php-model/oauth-client-credentials-default/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/oauth-client-credentials-default/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/oauth-client-credentials-default/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/oauth-client-credentials-default/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/oauth-client-credentials-default/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/oauth-client-credentials-default/tests/Core/Json/EnumTest.php b/seed/php-model/oauth-client-credentials-default/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/oauth-client-credentials-default/tests/Core/Json/EnumTest.php +++ b/seed/php-model/oauth-client-credentials-default/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/oauth-client-credentials-default/tests/Core/Json/TraitTest.php b/seed/php-model/oauth-client-credentials-default/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/oauth-client-credentials-default/tests/Core/Json/TraitTest.php +++ b/seed/php-model/oauth-client-credentials-default/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/oauth-client-credentials-default/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/oauth-client-credentials-default/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/oauth-client-credentials-default/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/oauth-client-credentials-default/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/oauth-client-credentials-environment-variables/src/Auth/TokenResponse.php b/seed/php-model/oauth-client-credentials-environment-variables/src/Auth/TokenResponse.php index 56fbe31ea0c2..9222a65c62ca 100644 --- a/seed/php-model/oauth-client-credentials-environment-variables/src/Auth/TokenResponse.php +++ b/seed/php-model/oauth-client-credentials-environment-variables/src/Auth/TokenResponse.php @@ -37,15 +37,17 @@ class TokenResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->accessToken = $values['accessToken'];$this->expiresIn = $values['expiresIn'];$this->refreshToken = $values['refreshToken'] ?? null; + ) { + $this->accessToken = $values['accessToken']; + $this->expiresIn = $values['expiresIn']; + $this->refreshToken = $values['refreshToken'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/oauth-client-credentials-environment-variables/src/Core/Json/JsonEncoder.php b/seed/php-model/oauth-client-credentials-environment-variables/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/oauth-client-credentials-environment-variables/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/oauth-client-credentials-environment-variables/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/oauth-client-credentials-environment-variables/src/Core/Json/JsonProperty.php b/seed/php-model/oauth-client-credentials-environment-variables/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/oauth-client-credentials-environment-variables/src/Core/Json/JsonProperty.php +++ b/seed/php-model/oauth-client-credentials-environment-variables/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/oauth-client-credentials-environment-variables/src/Core/Types/ArrayType.php b/seed/php-model/oauth-client-credentials-environment-variables/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/oauth-client-credentials-environment-variables/src/Core/Types/ArrayType.php +++ b/seed/php-model/oauth-client-credentials-environment-variables/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/oauth-client-credentials-environment-variables/src/Core/Types/Constant.php b/seed/php-model/oauth-client-credentials-environment-variables/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/oauth-client-credentials-environment-variables/src/Core/Types/Constant.php +++ b/seed/php-model/oauth-client-credentials-environment-variables/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/oauth-client-credentials-environment-variables/src/Core/Types/Union.php b/seed/php-model/oauth-client-credentials-environment-variables/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/oauth-client-credentials-environment-variables/src/Core/Types/Union.php +++ b/seed/php-model/oauth-client-credentials-environment-variables/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/oauth-client-credentials-environment-variables/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/oauth-client-credentials-environment-variables/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/oauth-client-credentials-environment-variables/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/oauth-client-credentials-environment-variables/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/oauth-client-credentials-environment-variables/tests/Core/Json/EnumTest.php b/seed/php-model/oauth-client-credentials-environment-variables/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/oauth-client-credentials-environment-variables/tests/Core/Json/EnumTest.php +++ b/seed/php-model/oauth-client-credentials-environment-variables/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/oauth-client-credentials-environment-variables/tests/Core/Json/TraitTest.php b/seed/php-model/oauth-client-credentials-environment-variables/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/oauth-client-credentials-environment-variables/tests/Core/Json/TraitTest.php +++ b/seed/php-model/oauth-client-credentials-environment-variables/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/oauth-client-credentials-environment-variables/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/oauth-client-credentials-environment-variables/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/oauth-client-credentials-environment-variables/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/oauth-client-credentials-environment-variables/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/oauth-client-credentials-mandatory-auth/.fern/metadata.json b/seed/php-model/oauth-client-credentials-mandatory-auth/.fern/metadata.json new file mode 100644 index 000000000000..90046b58049c --- /dev/null +++ b/seed/php-model/oauth-client-credentials-mandatory-auth/.fern/metadata.json @@ -0,0 +1,5 @@ +{ + "cliVersion": "DUMMY", + "generatorName": "fernapi/fern-php-model", + "generatorVersion": "latest" +} \ No newline at end of file diff --git a/seed/php-model/oauth-client-credentials-mandatory-auth/.github/workflows/ci.yml b/seed/php-model/oauth-client-credentials-mandatory-auth/.github/workflows/ci.yml new file mode 100644 index 000000000000..ba5836ae6967 --- /dev/null +++ b/seed/php-model/oauth-client-credentials-mandatory-auth/.github/workflows/ci.yml @@ -0,0 +1,48 @@ +name: ci + +on: [push] + +jobs: + compile: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "8.1" + + - name: Install tools + run: | + composer install + + - name: Build + run: | + composer build + + - name: Analyze + run: | + composer analyze + + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "8.1" + + - name: Install tools + run: | + composer install + + - name: Run Tests + run: | + composer test diff --git a/seed/php-model/oauth-client-credentials-mandatory-auth/.gitignore b/seed/php-model/oauth-client-credentials-mandatory-auth/.gitignore new file mode 100644 index 000000000000..31a1aeb14f35 --- /dev/null +++ b/seed/php-model/oauth-client-credentials-mandatory-auth/.gitignore @@ -0,0 +1,5 @@ +.idea +.php-cs-fixer.cache +.phpunit.result.cache +composer.lock +vendor/ \ No newline at end of file diff --git a/seed/php-model/oauth-client-credentials-mandatory-auth/composer.json b/seed/php-model/oauth-client-credentials-mandatory-auth/composer.json new file mode 100644 index 000000000000..c6a5d9f87477 --- /dev/null +++ b/seed/php-model/oauth-client-credentials-mandatory-auth/composer.json @@ -0,0 +1,39 @@ +{ + "name": "seed/seed", + "version": "0.0.1", + "description": "Seed PHP Library", + "keywords": [ + "seed", + "api", + "sdk" + ], + "license": [], + "require": { + "php": "^8.1", + "ext-json": "*", + "guzzlehttp/guzzle": "^7.4" + }, + "require-dev": { + "phpunit/phpunit": "^9.0", + "friendsofphp/php-cs-fixer": "3.5.0", + "phpstan/phpstan": "^1.12" + }, + "autoload": { + "psr-4": { + "Seed\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Seed\\Tests\\": "tests/" + } + }, + "scripts": { + "build": [ + "@php -l src", + "@php -l tests" + ], + "test": "phpunit", + "analyze": "phpstan analyze src tests --memory-limit=1G" + } +} \ No newline at end of file diff --git a/seed/php-model/oauth-client-credentials-mandatory-auth/phpstan.neon b/seed/php-model/oauth-client-credentials-mandatory-auth/phpstan.neon new file mode 100644 index 000000000000..780706b8f8a2 --- /dev/null +++ b/seed/php-model/oauth-client-credentials-mandatory-auth/phpstan.neon @@ -0,0 +1,6 @@ +parameters: + level: max + reportUnmatchedIgnoredErrors: false + paths: + - src + - tests \ No newline at end of file diff --git a/seed/php-model/oauth-client-credentials-mandatory-auth/phpunit.xml b/seed/php-model/oauth-client-credentials-mandatory-auth/phpunit.xml new file mode 100644 index 000000000000..54630a51163c --- /dev/null +++ b/seed/php-model/oauth-client-credentials-mandatory-auth/phpunit.xml @@ -0,0 +1,7 @@ + + + + tests + + + \ No newline at end of file diff --git a/seed/php-model/oauth-client-credentials-mandatory-auth/snippet.json b/seed/php-model/oauth-client-credentials-mandatory-auth/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/php-model/oauth-client-credentials-mandatory-auth/src/Auth/TokenResponse.php b/seed/php-model/oauth-client-credentials-mandatory-auth/src/Auth/TokenResponse.php new file mode 100644 index 000000000000..9222a65c62ca --- /dev/null +++ b/seed/php-model/oauth-client-credentials-mandatory-auth/src/Auth/TokenResponse.php @@ -0,0 +1,53 @@ +accessToken = $values['accessToken']; + $this->expiresIn = $values['expiresIn']; + $this->refreshToken = $values['refreshToken'] ?? null; + } + + /** + * @return string + */ + public function __toString(): string + { + return $this->toJson(); + } +} diff --git a/seed/php-model/oauth-client-credentials-mandatory-auth/src/Core/Json/JsonDecoder.php b/seed/php-model/oauth-client-credentials-mandatory-auth/src/Core/Json/JsonDecoder.php new file mode 100644 index 000000000000..2ddff0273482 --- /dev/null +++ b/seed/php-model/oauth-client-credentials-mandatory-auth/src/Core/Json/JsonDecoder.php @@ -0,0 +1,161 @@ + $type The type definition for deserialization. + * @return mixed[]|array The deserialized array. + * @throws JsonException If the decoded value is not an array. + */ + public static function decodeArray(string $json, array $type): array + { + $decoded = self::decode($json); + if (!is_array($decoded)) { + throw new JsonException("Unexpected non-array json value: " . $json); + } + return JsonDeserializer::deserializeArray($decoded, $type); + } + + /** + * Decodes a JSON string and deserializes it based on the provided union type definition. + * + * @param string $json The JSON string to decode. + * @param Union $union The union type definition for deserialization. + * @return mixed The deserialized value. + * @throws JsonException If the deserialization for all types in the union fails. + */ + public static function decodeUnion(string $json, Union $union): mixed + { + $decoded = self::decode($json); + return JsonDeserializer::deserializeUnion($decoded, $union); + } + /** + * Decodes a JSON string and returns a mixed. + * + * @param string $json The JSON string to decode. + * @return mixed The decoded mixed. + * @throws JsonException If the decoded value is not an mixed. + */ + public static function decodeMixed(string $json): mixed + { + return self::decode($json); + } + + /** + * Decodes a JSON string into a PHP value. + * + * @param string $json The JSON string to decode. + * @return mixed The decoded value. + * @throws JsonException If an error occurs during JSON decoding. + */ + public static function decode(string $json): mixed + { + return json_decode($json, associative: true, flags: JSON_THROW_ON_ERROR); + } +} diff --git a/seed/php-model/oauth-client-credentials-mandatory-auth/src/Core/Json/JsonDeserializer.php b/seed/php-model/oauth-client-credentials-mandatory-auth/src/Core/Json/JsonDeserializer.php new file mode 100644 index 000000000000..ab6a3f826238 --- /dev/null +++ b/seed/php-model/oauth-client-credentials-mandatory-auth/src/Core/Json/JsonDeserializer.php @@ -0,0 +1,206 @@ + $data The array to be deserialized. + * @param mixed[]|array $type The type definition from the annotation. + * @return mixed[]|array The deserialized array. + * @throws JsonException If deserialization fails. + */ + public static function deserializeArray(array $data, array $type): array + { + return Utils::isMapType($type) + ? self::deserializeMap($data, $type) + : self::deserializeList($data, $type); + } + + /** + * Deserializes a value based on its type definition. + * + * @param mixed $data The data to deserialize. + * @param mixed $type The type definition. + * @return mixed The deserialized value. + * @throws JsonException If deserialization fails. + */ + private static function deserializeValue(mixed $data, mixed $type): mixed + { + if ($type instanceof Union) { + return self::deserializeUnion($data, $type); + } + + if (is_array($type)) { + return self::deserializeArray((array)$data, $type); + } + + if (gettype($type) != "string") { + throw new JsonException("Unexpected non-string type."); + } + + return self::deserializeSingleValue($data, $type); + } + + /** + * Deserializes a value based on the possible types in a union type definition. + * + * @param mixed $data The data to deserialize. + * @param Union $type The union type definition. + * @return mixed The deserialized value. + * @throws JsonException If none of the union types can successfully deserialize the value. + */ + public static function deserializeUnion(mixed $data, Union $type): mixed + { + foreach ($type->types as $unionType) { + try { + return self::deserializeValue($data, $unionType); + } catch (\Throwable) { + // Catching Throwable instead of Exception to handle TypeError + // that occurs when assigning null to non-nullable typed properties + continue; + } + } + $readableType = Utils::getReadableType($data); + throw new JsonException( + "Cannot deserialize value of type $readableType with any of the union types: " . $type + ); + } + + /** + * Deserializes a single value based on its expected type. + * + * @param mixed $data The data to deserialize. + * @param string $type The expected type. + * @return mixed The deserialized value. + * @throws JsonException If deserialization fails. + */ + private static function deserializeSingleValue(mixed $data, string $type): mixed + { + if ($type === 'null' && $data === null) { + return null; + } + + if ($type === 'date' && is_string($data)) { + return self::deserializeDate($data); + } + + if ($type === 'datetime' && is_string($data)) { + return self::deserializeDateTime($data); + } + + if ($type === 'mixed') { + return $data; + } + + if (class_exists($type) && is_array($data)) { + return self::deserializeObject($data, $type); + } + + // Handle floats as a special case since gettype($data) returns "double" for float values in PHP, and because + // floats make come through from json_decoded as integers + if ($type === 'float' && (is_numeric($data))) { + return (float) $data; + } + + if (gettype($data) === $type) { + return $data; + } + + throw new JsonException("Unable to deserialize value of type '" . gettype($data) . "' as '$type'."); + } + + /** + * Deserializes an array into an object of the given type. + * + * @param array $data The data to deserialize. + * @param string $type The class name of the object to deserialize into. + * + * @return object The deserialized object. + * + * @throws JsonException If the type does not implement JsonSerializableType. + */ + public static function deserializeObject(array $data, string $type): object + { + if (!is_subclass_of($type, JsonSerializableType::class)) { + throw new JsonException("$type is not a subclass of JsonSerializableType."); + } + return $type::jsonDeserialize($data); + } + + /** + * Deserializes a map (associative array) with defined key and value types. + * + * @param array $data The associative array to deserialize. + * @param array $type The type definition for the map. + * @return array The deserialized map. + * @throws JsonException If deserialization fails. + */ + private static function deserializeMap(array $data, array $type): array + { + $keyType = array_key_first($type); + $valueType = $type[$keyType]; + $result = []; + + foreach ($data as $key => $item) { + $key = Utils::castKey($key, (string)$keyType); + $result[$key] = self::deserializeValue($item, $valueType); + } + + return $result; + } + + /** + * Deserializes a list (indexed array) with a defined value type. + * + * @param array $data The list to deserialize. + * @param array $type The type definition for the list. + * @return array The deserialized list. + * @throws JsonException If deserialization fails. + */ + private static function deserializeList(array $data, array $type): array + { + $valueType = $type[0]; + return array_map(fn ($item) => self::deserializeValue($item, $valueType), $data); + } +} diff --git a/seed/php-model/oauth-client-credentials-mandatory-auth/src/Core/Json/JsonEncoder.php b/seed/php-model/oauth-client-credentials-mandatory-auth/src/Core/Json/JsonEncoder.php new file mode 100644 index 000000000000..0dbf3fcc9948 --- /dev/null +++ b/seed/php-model/oauth-client-credentials-mandatory-auth/src/Core/Json/JsonEncoder.php @@ -0,0 +1,20 @@ + Extra properties from JSON that don't map to class properties */ + private array $__additionalProperties = []; + + /** + * Serializes the object to a JSON string. + * + * @return string JSON-encoded string representation of the object. + * @throws Exception If encoding fails. + */ + public function toJson(): string + { + $serializedObject = $this->jsonSerialize(); + $encoded = JsonEncoder::encode($serializedObject); + if (!$encoded) { + throw new Exception("Could not encode type"); + } + return $encoded; + } + + /** + * Serializes the object to an array. + * + * @return mixed[] Array representation of the object. + * @throws JsonException If serialization fails. + */ + public function jsonSerialize(): array + { + $result = []; + $reflectionClass = new \ReflectionClass($this); + foreach ($reflectionClass->getProperties() as $property) { + $jsonKey = self::getJsonKey($property); + if ($jsonKey == null) { + continue; + } + $value = $property->getValue($this); + + // Handle DateTime properties + $dateTypeAttr = $property->getAttributes(Date::class)[0] ?? null; + if ($dateTypeAttr && $value instanceof DateTime) { + $dateType = $dateTypeAttr->newInstance()->type; + $value = ($dateType === Date::TYPE_DATE) + ? JsonSerializer::serializeDate($value) + : JsonSerializer::serializeDateTime($value); + } + + // Handle Union annotations + $unionTypeAttr = $property->getAttributes(Union::class)[0] ?? null; + if ($unionTypeAttr) { + $unionType = $unionTypeAttr->newInstance(); + $value = JsonSerializer::serializeUnion($value, $unionType); + } + + // Handle arrays with type annotations + $arrayTypeAttr = $property->getAttributes(ArrayType::class)[0] ?? null; + if ($arrayTypeAttr && is_array($value)) { + $arrayType = $arrayTypeAttr->newInstance()->type; + $value = JsonSerializer::serializeArray($value, $arrayType); + } + + // Handle object + if (is_object($value)) { + $value = JsonSerializer::serializeObject($value); + } + + if ($value !== null) { + $result[$jsonKey] = $value; + } + } + return $result; + } + + /** + * Deserializes a JSON string into an instance of the calling class. + * + * @param string $json JSON string to deserialize. + * @return static Deserialized object. + * @throws JsonException If decoding fails or the result is not an array. + * @throws Exception If deserialization fails. + */ + public static function fromJson(string $json): static + { + $decodedJson = JsonDecoder::decode($json); + if (!is_array($decodedJson)) { + throw new JsonException("Unexpected non-array decoded type: " . gettype($decodedJson)); + } + return self::jsonDeserialize($decodedJson); + } + + /** + * Deserializes an array into an instance of the calling class. + * + * @param array $data Array data to deserialize. + * @return static Deserialized object. + * @throws JsonException If deserialization fails. + */ + public static function jsonDeserialize(array $data): static + { + $reflectionClass = new \ReflectionClass(static::class); + $constructor = $reflectionClass->getConstructor(); + if ($constructor === null) { + throw new JsonException("No constructor found."); + } + + $args = []; + $properties = []; + $additionalProperties = []; + foreach ($reflectionClass->getProperties() as $property) { + $jsonKey = self::getJsonKey($property) ?? $property->getName(); + $properties[$jsonKey] = $property; + } + + foreach ($data as $jsonKey => $value) { + if (!isset($properties[$jsonKey])) { + // This JSON key doesn't map to any class property - add it to additionalProperties + $additionalProperties[$jsonKey] = $value; + continue; + } + + $property = $properties[$jsonKey]; + + // Handle Date annotation + $dateTypeAttr = $property->getAttributes(Date::class)[0] ?? null; + if ($dateTypeAttr) { + $dateType = $dateTypeAttr->newInstance()->type; + if (!is_string($value)) { + throw new JsonException("Unexpected non-string type for date."); + } + $value = ($dateType === Date::TYPE_DATE) + ? JsonDeserializer::deserializeDate($value) + : JsonDeserializer::deserializeDateTime($value); + } + + // Handle Array annotation + $arrayTypeAttr = $property->getAttributes(ArrayType::class)[0] ?? null; + if (is_array($value) && $arrayTypeAttr) { + $arrayType = $arrayTypeAttr->newInstance()->type; + $value = JsonDeserializer::deserializeArray($value, $arrayType); + } + + // Handle Union annotations + $unionTypeAttr = $property->getAttributes(Union::class)[0] ?? null; + if ($unionTypeAttr) { + $unionType = $unionTypeAttr->newInstance(); + $value = JsonDeserializer::deserializeUnion($value, $unionType); + } + + // Handle object + $type = $property->getType(); + if (is_array($value) && $type instanceof ReflectionNamedType && !$type->isBuiltin()) { + $value = JsonDeserializer::deserializeObject($value, $type->getName()); + } + + $args[$property->getName()] = $value; + } + + // Fill in any missing properties with defaults + foreach ($properties as $property) { + if (!isset($args[$property->getName()])) { + $args[$property->getName()] = $property->getDefaultValue() ?? null; + } + } + + // @phpstan-ignore-next-line + $result = new static($args); + $result->__additionalProperties = $additionalProperties; + return $result; + } + + /** + * Get properties from JSON that weren't mapped to class fields + * @return array + */ + public function getAdditionalProperties(): array + { + return $this->__additionalProperties; + } + + /** + * Retrieves the JSON key associated with a property. + * + * @param ReflectionProperty $property The reflection property. + * @return ?string The JSON key, or null if not available. + */ + private static function getJsonKey(ReflectionProperty $property): ?string + { + $jsonPropertyAttr = $property->getAttributes(JsonProperty::class)[0] ?? null; + return $jsonPropertyAttr?->newInstance()?->name; + } +} diff --git a/seed/php-model/oauth-client-credentials-mandatory-auth/src/Core/Json/JsonSerializer.php b/seed/php-model/oauth-client-credentials-mandatory-auth/src/Core/Json/JsonSerializer.php new file mode 100644 index 000000000000..36dc06dfcf50 --- /dev/null +++ b/seed/php-model/oauth-client-credentials-mandatory-auth/src/Core/Json/JsonSerializer.php @@ -0,0 +1,197 @@ +format(Constant::DateFormat); + } + + /** + * Serializes a DateTime object into a string using the date-time format. + * Normalizes UTC times to use 'Z' suffix instead of '+00:00'. + * + * @param DateTime $date The DateTime object to serialize. + * @return string The serialized date-time string. + */ + public static function serializeDateTime(DateTime $date): string + { + $formatted = $date->format(Constant::DateTimeFormat); + if (str_ends_with($formatted, '+00:00')) { + return substr($formatted, 0, -6) . 'Z'; + } + return $formatted; + } + + /** + * Serializes an array based on type annotations (either a list or map). + * + * @param mixed[]|array $data The array to be serialized. + * @param mixed[]|array $type The type definition from the annotation. + * @return mixed[]|array The serialized array. + * @throws JsonException If serialization fails. + */ + public static function serializeArray(array $data, array $type): array + { + return Utils::isMapType($type) + ? self::serializeMap($data, $type) + : self::serializeList($data, $type); + } + + /** + * Serializes a value based on its type definition. + * + * @param mixed $data The value to serialize. + * @param mixed $type The type definition. + * @return mixed The serialized value. + * @throws JsonException If serialization fails. + */ + private static function serializeValue(mixed $data, mixed $type): mixed + { + if ($type instanceof Union) { + return self::serializeUnion($data, $type); + } + + if (is_array($type)) { + return self::serializeArray((array)$data, $type); + } + + if (gettype($type) != "string") { + throw new JsonException("Unexpected non-string type."); + } + + return self::serializeSingleValue($data, $type); + } + + /** + * Serializes a value for a union type definition. + * + * @param mixed $data The value to serialize. + * @param Union $unionType The union type definition. + * @return mixed The serialized value. + * @throws JsonException If serialization fails for all union types. + */ + public static function serializeUnion(mixed $data, Union $unionType): mixed + { + foreach ($unionType->types as $type) { + try { + return self::serializeValue($data, $type); + } catch (Exception) { + // Try the next type in the union + continue; + } + } + $readableType = Utils::getReadableType($data); + throw new JsonException( + "Cannot serialize value of type $readableType with any of the union types: " . $unionType + ); + } + + /** + * Serializes a single value based on its type. + * + * @param mixed $data The value to serialize. + * @param string $type The expected type. + * @return mixed The serialized value. + * @throws JsonException If serialization fails. + */ + private static function serializeSingleValue(mixed $data, string $type): mixed + { + if ($type === 'null' && $data === null) { + return null; + } + + if (($type === 'date' || $type === 'datetime') && $data instanceof DateTime) { + return $type === 'date' ? self::serializeDate($data) : self::serializeDateTime($data); + } + + if ($type === 'mixed') { + return $data; + } + + if (class_exists($type) && $data instanceof $type) { + return self::serializeObject($data); + } + + // Handle floats as a special case since gettype($data) returns "double" for float values in PHP. + if ($type === 'float' && is_float($data)) { + return $data; + } + + if (gettype($data) === $type) { + return $data; + } + + throw new JsonException("Unable to serialize value of type '" . gettype($data) . "' as '$type'."); + } + + /** + * Serializes an object to a JSON-serializable format. + * + * @param object $data The object to serialize. + * @return mixed The serialized data. + * @throws JsonException If the object does not implement JsonSerializable. + */ + public static function serializeObject(object $data): mixed + { + if (!is_subclass_of($data, JsonSerializable::class)) { + $type = get_class($data); + throw new JsonException("Class $type must implement JsonSerializable."); + } + return $data->jsonSerialize(); + } + + /** + * Serializes a map (associative array) with defined key and value types. + * + * @param array $data The associative array to serialize. + * @param array $type The type definition for the map. + * @return array The serialized map. + * @throws JsonException If serialization fails. + */ + private static function serializeMap(array $data, array $type): array + { + $keyType = array_key_first($type); + if ($keyType === null) { + throw new JsonException("Unexpected no key in ArrayType."); + } + $valueType = $type[$keyType]; + $result = []; + + foreach ($data as $key => $item) { + $key = Utils::castKey($key, $keyType); + $result[$key] = self::serializeValue($item, $valueType); + } + + return $result; + } + + /** + * Serializes a list (indexed array) where only the value type is defined. + * + * @param array $data The list to serialize. + * @param array $type The type definition for the list. + * @return array The serialized list. + * @throws JsonException If serialization fails. + */ + private static function serializeList(array $data, array $type): array + { + $valueType = $type[0]; + return array_map(fn ($item) => self::serializeValue($item, $valueType), $data); + } +} diff --git a/seed/php-model/oauth-client-credentials-mandatory-auth/src/Core/Json/Utils.php b/seed/php-model/oauth-client-credentials-mandatory-auth/src/Core/Json/Utils.php new file mode 100644 index 000000000000..7577c058916d --- /dev/null +++ b/seed/php-model/oauth-client-credentials-mandatory-auth/src/Core/Json/Utils.php @@ -0,0 +1,61 @@ + $type The type definition from the annotation. + * @return bool True if the type is a map, false if it's a list. + */ + public static function isMapType(array $type): bool + { + return count($type) === 1 && !array_is_list($type); + } + + /** + * Casts the key to the appropriate type based on the key type. + * + * @param mixed $key The key to be cast. + * @param string $keyType The type to cast the key to ('string', 'integer', 'float'). + * @return mixed The casted key. + * @throws JsonException + */ + public static function castKey(mixed $key, string $keyType): mixed + { + if (!is_scalar($key)) { + throw new JsonException("Key must be a scalar type."); + } + return match ($keyType) { + 'integer' => (int)$key, + 'float' => (float)$key, + 'string' => (string)$key, + default => $key, + }; + } + + /** + * Returns a human-readable representation of the input's type. + * + * @param mixed $input The input value to determine the type of. + * @return string A readable description of the input type. + */ + public static function getReadableType(mixed $input): string + { + if (is_object($input)) { + return get_class($input); + } elseif (is_array($input)) { + return 'array(' . count($input) . ' items)'; + } elseif (is_null($input)) { + return 'null'; + } else { + return gettype($input); + } + } +} diff --git a/seed/php-model/oauth-client-credentials-mandatory-auth/src/Core/Types/ArrayType.php b/seed/php-model/oauth-client-credentials-mandatory-auth/src/Core/Types/ArrayType.php new file mode 100644 index 000000000000..a26d29008ec3 --- /dev/null +++ b/seed/php-model/oauth-client-credentials-mandatory-auth/src/Core/Types/ArrayType.php @@ -0,0 +1,16 @@ + 'valueType'] for maps, or ['valueType'] for lists + */ + public function __construct(public array $type) + { + } +} diff --git a/seed/php-model/oauth-client-credentials-mandatory-auth/src/Core/Types/Constant.php b/seed/php-model/oauth-client-credentials-mandatory-auth/src/Core/Types/Constant.php new file mode 100644 index 000000000000..5ac4518cc6d6 --- /dev/null +++ b/seed/php-model/oauth-client-credentials-mandatory-auth/src/Core/Types/Constant.php @@ -0,0 +1,12 @@ +> The types allowed for this property, which can be strings, arrays, or nested Union types. + */ + public array $types; + + /** + * Constructor for the Union attribute. + * + * @param string|Union|array ...$types The list of types that the property can accept. + * This can include primitive types (e.g., 'string', 'int'), arrays, or other Union instances. + * + * Example: + * ```php + * #[Union('string', 'null', 'date', new Union('boolean', 'int'))] + * ``` + */ + public function __construct(string|Union|array ...$types) + { + $this->types = $types; + } + + /** + * Converts the Union type to a string representation. + * + * @return string A string representation of the union types. + */ + public function __toString(): string + { + return implode(' | ', array_map(function ($type) { + if (is_string($type)) { + return $type; + } elseif ($type instanceof Union) { + return (string) $type; // Recursively handle nested unions + } elseif (is_array($type)) { + return 'array'; // Handle arrays + } + }, $this->types)); + } +} diff --git a/seed/php-model/oauth-client-credentials-mandatory-auth/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/oauth-client-credentials-mandatory-auth/tests/Core/Json/AdditionalPropertiesTest.php new file mode 100644 index 000000000000..5bcb7ba283a8 --- /dev/null +++ b/seed/php-model/oauth-client-credentials-mandatory-auth/tests/Core/Json/AdditionalPropertiesTest.php @@ -0,0 +1,76 @@ +name; + } + + /** + * @return string|null + */ + public function getEmail(): ?string + { + return $this->email; + } + + /** + * @param array{ + * name: string, + * email?: string|null, + * } $values + */ + public function __construct( + array $values, + ) { + $this->name = $values['name']; + $this->email = $values['email'] ?? null; + } +} + +class AdditionalPropertiesTest extends TestCase +{ + public function testExtraProperties(): void + { + $expectedJson = json_encode( + [ + 'name' => 'john.doe', + 'email' => 'john.doe@example.com', + 'age' => 42 + ], + JSON_THROW_ON_ERROR + ); + + $person = Person::fromJson($expectedJson); + $this->assertEquals('john.doe', $person->getName()); + $this->assertEquals('john.doe@example.com', $person->getEmail()); + $this->assertEquals( + [ + 'age' => 42 + ], + $person->getAdditionalProperties(), + ); + } +} diff --git a/seed/php-model/oauth-client-credentials-mandatory-auth/tests/Core/Json/DateArrayTest.php b/seed/php-model/oauth-client-credentials-mandatory-auth/tests/Core/Json/DateArrayTest.php new file mode 100644 index 000000000000..a72cfdbdd227 --- /dev/null +++ b/seed/php-model/oauth-client-credentials-mandatory-auth/tests/Core/Json/DateArrayTest.php @@ -0,0 +1,54 @@ +dates = $values['dates']; + } +} + +class DateArrayTest extends TestCase +{ + public function testDateTimeInArrays(): void + { + $expectedJson = json_encode( + [ + 'dates' => ['2023-01-01', '2023-02-01', '2023-03-01'] + ], + JSON_THROW_ON_ERROR + ); + + $object = DateArray::fromJson($expectedJson); + $this->assertInstanceOf(DateTime::class, $object->dates[0], 'dates[0] should be a DateTime instance.'); + $this->assertEquals('2023-01-01', $object->dates[0]->format('Y-m-d'), 'dates[0] should have the correct date.'); + $this->assertInstanceOf(DateTime::class, $object->dates[1], 'dates[1] should be a DateTime instance.'); + $this->assertEquals('2023-02-01', $object->dates[1]->format('Y-m-d'), 'dates[1] should have the correct date.'); + $this->assertInstanceOf(DateTime::class, $object->dates[2], 'dates[2] should be a DateTime instance.'); + $this->assertEquals('2023-03-01', $object->dates[2]->format('Y-m-d'), 'dates[2] should have the correct date.'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for dates array.'); + } +} diff --git a/seed/php-model/oauth-client-credentials-mandatory-auth/tests/Core/Json/EmptyArrayTest.php b/seed/php-model/oauth-client-credentials-mandatory-auth/tests/Core/Json/EmptyArrayTest.php new file mode 100644 index 000000000000..d243a08916d4 --- /dev/null +++ b/seed/php-model/oauth-client-credentials-mandatory-auth/tests/Core/Json/EmptyArrayTest.php @@ -0,0 +1,71 @@ + $emptyMapArray + */ + #[JsonProperty('empty_map_array')] + #[ArrayType(['integer' => new Union('string', 'null')])] + public array $emptyMapArray; + + /** + * @var array $emptyDatesArray + */ + #[ArrayType([new Union('date', 'null')])] + #[JsonProperty('empty_dates_array')] + public array $emptyDatesArray; + + /** + * @param array{ + * emptyStringArray: string[], + * emptyMapArray: array, + * emptyDatesArray: array, + * } $values + */ + public function __construct( + array $values, + ) { + $this->emptyStringArray = $values['emptyStringArray']; + $this->emptyMapArray = $values['emptyMapArray']; + $this->emptyDatesArray = $values['emptyDatesArray']; + } +} + +class EmptyArrayTest extends TestCase +{ + public function testEmptyArray(): void + { + $expectedJson = json_encode( + [ + 'empty_string_array' => [], + 'empty_map_array' => [], + 'empty_dates_array' => [] + ], + JSON_THROW_ON_ERROR + ); + + $object = EmptyArray::fromJson($expectedJson); + $this->assertEmpty($object->emptyStringArray, 'empty_string_array should be empty.'); + $this->assertEmpty($object->emptyMapArray, 'empty_map_array should be empty.'); + $this->assertEmpty($object->emptyDatesArray, 'empty_dates_array should be empty.'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for EmptyArraysType.'); + } +} diff --git a/seed/php-model/oauth-client-credentials-mandatory-auth/tests/Core/Json/EnumTest.php b/seed/php-model/oauth-client-credentials-mandatory-auth/tests/Core/Json/EnumTest.php new file mode 100644 index 000000000000..bf83d5b8ab0f --- /dev/null +++ b/seed/php-model/oauth-client-credentials-mandatory-auth/tests/Core/Json/EnumTest.php @@ -0,0 +1,76 @@ +value; + } +} + +class ShapeType extends JsonSerializableType +{ + /** + * @var Shape $shape + */ + #[JsonProperty('shape')] + public Shape $shape; + + /** + * @var Shape[] $shapes + */ + #[ArrayType([Shape::class])] + #[JsonProperty('shapes')] + public array $shapes; + + /** + * @param Shape $shape + * @param Shape[] $shapes + */ + public function __construct( + Shape $shape, + array $shapes, + ) { + $this->shape = $shape; + $this->shapes = $shapes; + } +} + +class EnumTest extends TestCase +{ + public function testEnumSerialization(): void + { + $object = new ShapeType( + Shape::Circle, + [Shape::Square, Shape::Circle, Shape::Triangle] + ); + + $expectedJson = json_encode([ + 'shape' => 'CIRCLE', + 'shapes' => ['SQUARE', 'CIRCLE', 'TRIANGLE'] + ], JSON_THROW_ON_ERROR); + + $actualJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString( + $expectedJson, + $actualJson, + 'Serialized JSON does not match expected JSON for shape and shapes properties.' + ); + } +} diff --git a/seed/php-model/oauth-client-credentials-mandatory-auth/tests/Core/Json/ExhaustiveTest.php b/seed/php-model/oauth-client-credentials-mandatory-auth/tests/Core/Json/ExhaustiveTest.php new file mode 100644 index 000000000000..ed1330eda3e4 --- /dev/null +++ b/seed/php-model/oauth-client-credentials-mandatory-auth/tests/Core/Json/ExhaustiveTest.php @@ -0,0 +1,197 @@ +nestedProperty = $values['nestedProperty']; + } +} + +class Type extends JsonSerializableType +{ + /** + * @var Nested nestedType + */ + #[JsonProperty('nested_type')] + public Nested $nestedType; /** + + * @var string $simpleProperty + */ + #[JsonProperty('simple_property')] + public string $simpleProperty; + + /** + * @var DateTime $dateProperty + */ + #[Date(Date::TYPE_DATE)] + #[JsonProperty('date_property')] + public DateTime $dateProperty; + + /** + * @var DateTime $datetimeProperty + */ + #[Date(Date::TYPE_DATETIME)] + #[JsonProperty('datetime_property')] + public DateTime $datetimeProperty; + + /** + * @var array $stringArray + */ + #[ArrayType(['string'])] + #[JsonProperty('string_array')] + public array $stringArray; + + /** + * @var array $mapProperty + */ + #[ArrayType(['string' => 'integer'])] + #[JsonProperty('map_property')] + public array $mapProperty; + + /** + * @var array $objectArray + */ + #[ArrayType(['integer' => new Union(Nested::class, 'null')])] + #[JsonProperty('object_array')] + public array $objectArray; + + /** + * @var array> $nestedArray + */ + #[ArrayType(['integer' => ['integer' => new Union('string', 'null')]])] + #[JsonProperty('nested_array')] + public array $nestedArray; + + /** + * @var array $datesArray + */ + #[ArrayType([new Union('date', 'null')])] + #[JsonProperty('dates_array')] + public array $datesArray; + + /** + * @var string|null $nullableProperty + */ + #[JsonProperty('nullable_property')] + public ?string $nullableProperty; + + /** + * @param array{ + * nestedType: Nested, + * simpleProperty: string, + * dateProperty: DateTime, + * datetimeProperty: DateTime, + * stringArray: array, + * mapProperty: array, + * objectArray: array, + * nestedArray: array>, + * datesArray: array, + * nullableProperty?: string|null, + * } $values + */ + public function __construct( + array $values, + ) { + $this->nestedType = $values['nestedType']; + $this->simpleProperty = $values['simpleProperty']; + $this->dateProperty = $values['dateProperty']; + $this->datetimeProperty = $values['datetimeProperty']; + $this->stringArray = $values['stringArray']; + $this->mapProperty = $values['mapProperty']; + $this->objectArray = $values['objectArray']; + $this->nestedArray = $values['nestedArray']; + $this->datesArray = $values['datesArray']; + $this->nullableProperty = $values['nullableProperty'] ?? null; + } +} + +class ExhaustiveTest extends TestCase +{ + /** + * Test serialization and deserialization of all types in Type. + */ + public function testExhaustive(): void + { + $expectedJson = json_encode( + [ + 'nested_type' => ['nested_property' => '1995-07-20'], + 'simple_property' => 'Test String', + // Omit 'nullable_property' to test null serialization + 'date_property' => '2023-01-01', + 'datetime_property' => '2023-01-01T12:34:56Z', + 'string_array' => ['one', 'two', 'three'], + 'map_property' => ['key1' => 1, 'key2' => 2], + 'object_array' => [ + 1 => ['nested_property' => '2021-07-20'], + 2 => null, // Testing nullable objects in array + ], + 'nested_array' => [ + 1 => [1 => 'value1', 2 => null], // Testing nullable strings in nested array + 2 => [3 => 'value3', 4 => 'value4'] + ], + 'dates_array' => ['2023-01-01', null, '2023-03-01'] // Testing nullable dates in array> + ], + JSON_THROW_ON_ERROR + ); + + $object = Type::fromJson($expectedJson); + + // Check that nullable property is null and not included in JSON + $this->assertNull($object->nullableProperty, 'Nullable property should be null.'); + + // Check date properties + $this->assertInstanceOf(DateTime::class, $object->dateProperty, 'date_property should be a DateTime instance.'); + $this->assertEquals('2023-01-01', $object->dateProperty->format('Y-m-d'), 'date_property should have the correct date.'); + $this->assertInstanceOf(DateTime::class, $object->datetimeProperty, 'datetime_property should be a DateTime instance.'); + $this->assertEquals('2023-01-01 12:34:56', $object->datetimeProperty->format('Y-m-d H:i:s'), 'datetime_property should have the correct datetime.'); + + // Check scalar arrays + $this->assertEquals(['one', 'two', 'three'], $object->stringArray, 'string_array should match the original data.'); + $this->assertEquals(['key1' => 1, 'key2' => 2], $object->mapProperty, 'map_property should match the original data.'); + + // Check object array with nullable elements + $this->assertInstanceOf(Nested::class, $object->objectArray[1], 'object_array[1] should be an instance of TestNestedType1.'); + $this->assertEquals('2021-07-20', $object->objectArray[1]->nestedProperty->format('Y-m-d'), 'object_array[1]->nestedProperty should match the original data.'); + $this->assertNull($object->objectArray[2], 'object_array[2] should be null.'); + + // Check nested array with nullable strings + $this->assertEquals('value1', $object->nestedArray[1][1], 'nested_array[1][1] should match the original data.'); + $this->assertNull($object->nestedArray[1][2], 'nested_array[1][2] should be null.'); + $this->assertEquals('value3', $object->nestedArray[2][3], 'nested_array[2][3] should match the original data.'); + $this->assertEquals('value4', $object->nestedArray[2][4], 'nested_array[2][4] should match the original data.'); + + // Check dates array with nullable DateTime objects + $this->assertInstanceOf(DateTime::class, $object->datesArray[0], 'dates_array[0] should be a DateTime instance.'); + $this->assertEquals('2023-01-01', $object->datesArray[0]->format('Y-m-d'), 'dates_array[0] should have the correct date.'); + $this->assertNull($object->datesArray[1], 'dates_array[1] should be null.'); + $this->assertInstanceOf(DateTime::class, $object->datesArray[2], 'dates_array[2] should be a DateTime instance.'); + $this->assertEquals('2023-03-01', $object->datesArray[2]->format('Y-m-d'), 'dates_array[2] should have the correct date.'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'The serialized JSON does not match the original JSON.'); + } +} diff --git a/seed/php-model/oauth-client-credentials-mandatory-auth/tests/Core/Json/InvalidTest.php b/seed/php-model/oauth-client-credentials-mandatory-auth/tests/Core/Json/InvalidTest.php new file mode 100644 index 000000000000..7d1d79406a5a --- /dev/null +++ b/seed/php-model/oauth-client-credentials-mandatory-auth/tests/Core/Json/InvalidTest.php @@ -0,0 +1,42 @@ +integerProperty = $values['integerProperty']; + } +} + +class InvalidTest extends TestCase +{ + public function testInvalidJsonThrowsException(): void + { + $this->expectException(\TypeError::class); + $json = json_encode( + [ + 'integer_property' => 'not_an_integer' + ], + JSON_THROW_ON_ERROR + ); + Invalid::fromJson($json); + } +} diff --git a/seed/php-model/oauth-client-credentials-mandatory-auth/tests/Core/Json/NestedUnionArrayTest.php b/seed/php-model/oauth-client-credentials-mandatory-auth/tests/Core/Json/NestedUnionArrayTest.php new file mode 100644 index 000000000000..0fcdd06667e5 --- /dev/null +++ b/seed/php-model/oauth-client-credentials-mandatory-auth/tests/Core/Json/NestedUnionArrayTest.php @@ -0,0 +1,89 @@ +nestedProperty = $values['nestedProperty']; + } +} + +class NestedUnionArray extends JsonSerializableType +{ + /** + * @var array> $nestedArray + */ + #[ArrayType(['integer' => ['integer' => new Union(UnionObject::class, 'null', 'date')]])] + #[JsonProperty('nested_array')] + public array $nestedArray; + + /** + * @param array{ + * nestedArray: array>, + * } $values + */ + public function __construct( + array $values, + ) { + $this->nestedArray = $values['nestedArray']; + } +} + +class NestedUnionArrayTest extends TestCase +{ + public function testNestedUnionArray(): void + { + $expectedJson = json_encode( + [ + 'nested_array' => [ + 1 => [ + 1 => ['nested_property' => 'Nested One'], + 2 => null, + 4 => '2023-01-02' + ], + 2 => [ + 5 => ['nested_property' => 'Nested Two'], + 7 => '2023-02-02' + ] + ] + ], + JSON_THROW_ON_ERROR + ); + + $object = NestedUnionArray::fromJson($expectedJson); + $this->assertInstanceOf(UnionObject::class, $object->nestedArray[1][1], 'nested_array[1][1] should be an instance of Object.'); + $this->assertEquals('Nested One', $object->nestedArray[1][1]->nestedProperty, 'nested_array[1][1]->nestedProperty should match the original data.'); + $this->assertNull($object->nestedArray[1][2], 'nested_array[1][2] should be null.'); + $this->assertInstanceOf(DateTime::class, $object->nestedArray[1][4], 'nested_array[1][4] should be a DateTime instance.'); + $this->assertEquals('2023-01-02T00:00:00+00:00', $object->nestedArray[1][4]->format(Constant::DateTimeFormat), 'nested_array[1][4] should have the correct datetime.'); + $this->assertInstanceOf(UnionObject::class, $object->nestedArray[2][5], 'nested_array[2][5] should be an instance of Object.'); + $this->assertEquals('Nested Two', $object->nestedArray[2][5]->nestedProperty, 'nested_array[2][5]->nestedProperty should match the original data.'); + $this->assertInstanceOf(DateTime::class, $object->nestedArray[2][7], 'nested_array[1][4] should be a DateTime instance.'); + $this->assertEquals('2023-02-02', $object->nestedArray[2][7]->format('Y-m-d'), 'nested_array[1][4] should have the correct date.'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for nested_array.'); + } +} diff --git a/seed/php-model/oauth-client-credentials-mandatory-auth/tests/Core/Json/NullPropertyTest.php b/seed/php-model/oauth-client-credentials-mandatory-auth/tests/Core/Json/NullPropertyTest.php new file mode 100644 index 000000000000..ce20a2442825 --- /dev/null +++ b/seed/php-model/oauth-client-credentials-mandatory-auth/tests/Core/Json/NullPropertyTest.php @@ -0,0 +1,53 @@ +nonNullProperty = $values['nonNullProperty']; + $this->nullProperty = $values['nullProperty'] ?? null; + } +} + +class NullPropertyTest extends TestCase +{ + public function testNullPropertiesAreOmitted(): void + { + $object = new NullProperty( + [ + "nonNullProperty" => "Test String", + "nullProperty" => null + ] + ); + + $serialized = $object->jsonSerialize(); + $this->assertArrayHasKey('non_null_property', $serialized, 'non_null_property should be present in the serialized JSON.'); + $this->assertArrayNotHasKey('null_property', $serialized, 'null_property should be omitted from the serialized JSON.'); + $this->assertEquals('Test String', $serialized['non_null_property'], 'non_null_property should have the correct value.'); + } +} diff --git a/seed/php-model/oauth-client-credentials-mandatory-auth/tests/Core/Json/NullableArrayTest.php b/seed/php-model/oauth-client-credentials-mandatory-auth/tests/Core/Json/NullableArrayTest.php new file mode 100644 index 000000000000..fe0f19de6b13 --- /dev/null +++ b/seed/php-model/oauth-client-credentials-mandatory-auth/tests/Core/Json/NullableArrayTest.php @@ -0,0 +1,49 @@ + $nullableStringArray + */ + #[ArrayType([new Union('string', 'null')])] + #[JsonProperty('nullable_string_array')] + public array $nullableStringArray; + + /** + * @param array{ + * nullableStringArray: array, + * } $values + */ + public function __construct( + array $values, + ) { + $this->nullableStringArray = $values['nullableStringArray']; + } +} + +class NullableArrayTest extends TestCase +{ + public function testNullableArray(): void + { + $expectedJson = json_encode( + [ + 'nullable_string_array' => ['one', null, 'three'] + ], + JSON_THROW_ON_ERROR + ); + + $object = NullableArray::fromJson($expectedJson); + $this->assertEquals(['one', null, 'three'], $object->nullableStringArray, 'nullable_string_array should match the original data.'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for nullable_string_array.'); + } +} diff --git a/seed/php-model/oauth-client-credentials-mandatory-auth/tests/Core/Json/ScalarTest.php b/seed/php-model/oauth-client-credentials-mandatory-auth/tests/Core/Json/ScalarTest.php new file mode 100644 index 000000000000..604b7c0b959b --- /dev/null +++ b/seed/php-model/oauth-client-credentials-mandatory-auth/tests/Core/Json/ScalarTest.php @@ -0,0 +1,116 @@ + $intFloatArray + */ + #[ArrayType([new Union('integer', 'float')])] + #[JsonProperty('int_float_array')] + public array $intFloatArray; + + /** + * @var array $floatArray + */ + #[ArrayType(['float'])] + #[JsonProperty('float_array')] + public array $floatArray; + + /** + * @var bool|null $nullableBooleanProperty + */ + #[JsonProperty('nullable_boolean_property')] + public ?bool $nullableBooleanProperty; + + /** + * @param array{ + * integerProperty: int, + * floatProperty: float, + * otherFloatProperty: float, + * booleanProperty: bool, + * stringProperty: string, + * intFloatArray: array, + * floatArray: array, + * nullableBooleanProperty?: bool|null, + * } $values + */ + public function __construct( + array $values, + ) { + $this->integerProperty = $values['integerProperty']; + $this->floatProperty = $values['floatProperty']; + $this->otherFloatProperty = $values['otherFloatProperty']; + $this->booleanProperty = $values['booleanProperty']; + $this->stringProperty = $values['stringProperty']; + $this->intFloatArray = $values['intFloatArray']; + $this->floatArray = $values['floatArray']; + $this->nullableBooleanProperty = $values['nullableBooleanProperty'] ?? null; + } +} + +class ScalarTest extends TestCase +{ + public function testAllScalarTypesIncludingFloat(): void + { + $expectedJson = json_encode( + [ + 'integer_property' => 42, + 'float_property' => 3.14159, + 'other_float_property' => 3, + 'boolean_property' => true, + 'string_property' => 'Hello, World!', + 'int_float_array' => [1, 2.5, 3, 4.75], + 'float_array' => [1, 2, 3, 4] // Ensure we handle "integer-looking" floats + ], + JSON_THROW_ON_ERROR + ); + + $object = Scalar::fromJson($expectedJson); + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals(3.14159, $object->floatProperty, 'float_property should be 3.14159.'); + $this->assertTrue($object->booleanProperty, 'boolean_property should be true.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + $this->assertNull($object->nullableBooleanProperty, 'nullable_boolean_property should be null.'); + $this->assertEquals([1, 2.5, 3, 4.75], $object->intFloatArray, 'int_float_array should match the original data.'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTest.'); + } +} diff --git a/seed/php-model/oauth-client-credentials-mandatory-auth/tests/Core/Json/TraitTest.php b/seed/php-model/oauth-client-credentials-mandatory-auth/tests/Core/Json/TraitTest.php new file mode 100644 index 000000000000..837f239115f7 --- /dev/null +++ b/seed/php-model/oauth-client-credentials-mandatory-auth/tests/Core/Json/TraitTest.php @@ -0,0 +1,60 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $expectedJson = json_encode( + [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ], + JSON_THROW_ON_ERROR + ); + + $object = TypeWithTrait::fromJson($expectedJson); + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + } +} diff --git a/seed/php-model/oauth-client-credentials-mandatory-auth/tests/Core/Json/UnionArrayTest.php b/seed/php-model/oauth-client-credentials-mandatory-auth/tests/Core/Json/UnionArrayTest.php new file mode 100644 index 000000000000..33899cd3f051 --- /dev/null +++ b/seed/php-model/oauth-client-credentials-mandatory-auth/tests/Core/Json/UnionArrayTest.php @@ -0,0 +1,57 @@ + $mixedDates + */ + #[ArrayType(['integer' => new Union('datetime', 'string', 'null')])] + #[JsonProperty('mixed_dates')] + public array $mixedDates; + + /** + * @param array{ + * mixedDates: array, + * } $values + */ + public function __construct( + array $values, + ) { + $this->mixedDates = $values['mixedDates']; + } +} + +class UnionArrayTest extends TestCase +{ + public function testUnionArray(): void + { + $expectedJson = json_encode( + [ + 'mixed_dates' => [ + 1 => '2023-01-01T12:00:00Z', + 2 => null, + 3 => 'Some String' + ] + ], + JSON_THROW_ON_ERROR + ); + + $object = UnionArray::fromJson($expectedJson); + $this->assertInstanceOf(DateTime::class, $object->mixedDates[1], 'mixed_dates[1] should be a DateTime instance.'); + $this->assertEquals('2023-01-01 12:00:00', $object->mixedDates[1]->format('Y-m-d H:i:s'), 'mixed_dates[1] should have the correct datetime.'); + $this->assertNull($object->mixedDates[2], 'mixed_dates[2] should be null.'); + $this->assertEquals('Some String', $object->mixedDates[3], 'mixed_dates[3] should be "Some String".'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for mixed_dates.'); + } +} diff --git a/seed/php-model/oauth-client-credentials-mandatory-auth/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/oauth-client-credentials-mandatory-auth/tests/Core/Json/UnionPropertyTest.php new file mode 100644 index 000000000000..3119baace627 --- /dev/null +++ b/seed/php-model/oauth-client-credentials-mandatory-auth/tests/Core/Json/UnionPropertyTest.php @@ -0,0 +1,115 @@ + 'integer'], UnionProperty::class)] + #[JsonProperty('complexUnion')] + public mixed $complexUnion; + + /** + * @param array{ + * complexUnion: string|int|null|array|UnionProperty + * } $values + */ + public function __construct( + array $values, + ) { + $this->complexUnion = $values['complexUnion']; + } +} + +class UnionPropertyTest extends TestCase +{ + public function testWithMapOfIntToInt(): void + { + $expectedJson = json_encode( + [ + 'complexUnion' => [1 => 100, 2 => 200] + ], + JSON_THROW_ON_ERROR + ); + + $object = UnionProperty::fromJson($expectedJson); + $this->assertIsArray($object->complexUnion, 'complexUnion should be an array.'); + $this->assertEquals([1 => 100, 2 => 200], $object->complexUnion, 'complexUnion should match the original map of int => int.'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); + } + + public function testWithNestedUnionPropertyType(): void + { + $expectedJson = json_encode( + [ + 'complexUnion' => new UnionProperty( + [ + 'complexUnion' => 'Nested String' + ] + ) + ], + JSON_THROW_ON_ERROR + ); + + $object = UnionProperty::fromJson($expectedJson); + $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); + $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); + } + + public function testWithNull(): void + { + $expectedJson = json_encode( + [], + JSON_THROW_ON_ERROR + ); + + $object = UnionProperty::fromJson($expectedJson); + $this->assertNull($object->complexUnion, 'complexUnion should be null.'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); + } + + public function testWithInteger(): void + { + $expectedJson = json_encode( + [ + 'complexUnion' => 42 + ], + JSON_THROW_ON_ERROR + ); + + $object = UnionProperty::fromJson($expectedJson); + $this->assertIsInt($object->complexUnion, 'complexUnion should be an integer.'); + $this->assertEquals(42, $object->complexUnion, 'complexUnion should match the original integer.'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); + } + + public function testWithString(): void + { + $expectedJson = json_encode( + [ + 'complexUnion' => 'Some String' + ], + JSON_THROW_ON_ERROR + ); + + $object = UnionProperty::fromJson($expectedJson); + $this->assertIsString($object->complexUnion, 'complexUnion should be a string.'); + $this->assertEquals('Some String', $object->complexUnion, 'complexUnion should match the original string.'); + + $actualJson = $object->toJson(); + $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); + } +} diff --git a/seed/php-model/oauth-client-credentials-nested-root/src/Auth/TokenResponse.php b/seed/php-model/oauth-client-credentials-nested-root/src/Auth/TokenResponse.php index 56fbe31ea0c2..9222a65c62ca 100644 --- a/seed/php-model/oauth-client-credentials-nested-root/src/Auth/TokenResponse.php +++ b/seed/php-model/oauth-client-credentials-nested-root/src/Auth/TokenResponse.php @@ -37,15 +37,17 @@ class TokenResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->accessToken = $values['accessToken'];$this->expiresIn = $values['expiresIn'];$this->refreshToken = $values['refreshToken'] ?? null; + ) { + $this->accessToken = $values['accessToken']; + $this->expiresIn = $values['expiresIn']; + $this->refreshToken = $values['refreshToken'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/oauth-client-credentials-nested-root/src/Core/Json/JsonEncoder.php b/seed/php-model/oauth-client-credentials-nested-root/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/oauth-client-credentials-nested-root/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/oauth-client-credentials-nested-root/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/oauth-client-credentials-nested-root/src/Core/Json/JsonProperty.php b/seed/php-model/oauth-client-credentials-nested-root/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/oauth-client-credentials-nested-root/src/Core/Json/JsonProperty.php +++ b/seed/php-model/oauth-client-credentials-nested-root/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/oauth-client-credentials-nested-root/src/Core/Types/ArrayType.php b/seed/php-model/oauth-client-credentials-nested-root/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/oauth-client-credentials-nested-root/src/Core/Types/ArrayType.php +++ b/seed/php-model/oauth-client-credentials-nested-root/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/oauth-client-credentials-nested-root/src/Core/Types/Constant.php b/seed/php-model/oauth-client-credentials-nested-root/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/oauth-client-credentials-nested-root/src/Core/Types/Constant.php +++ b/seed/php-model/oauth-client-credentials-nested-root/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/oauth-client-credentials-nested-root/src/Core/Types/Union.php b/seed/php-model/oauth-client-credentials-nested-root/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/oauth-client-credentials-nested-root/src/Core/Types/Union.php +++ b/seed/php-model/oauth-client-credentials-nested-root/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/oauth-client-credentials-nested-root/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/oauth-client-credentials-nested-root/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/oauth-client-credentials-nested-root/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/oauth-client-credentials-nested-root/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/oauth-client-credentials-nested-root/tests/Core/Json/EnumTest.php b/seed/php-model/oauth-client-credentials-nested-root/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/oauth-client-credentials-nested-root/tests/Core/Json/EnumTest.php +++ b/seed/php-model/oauth-client-credentials-nested-root/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/oauth-client-credentials-nested-root/tests/Core/Json/TraitTest.php b/seed/php-model/oauth-client-credentials-nested-root/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/oauth-client-credentials-nested-root/tests/Core/Json/TraitTest.php +++ b/seed/php-model/oauth-client-credentials-nested-root/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/oauth-client-credentials-nested-root/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/oauth-client-credentials-nested-root/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/oauth-client-credentials-nested-root/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/oauth-client-credentials-nested-root/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/oauth-client-credentials-with-variables/src/Auth/TokenResponse.php b/seed/php-model/oauth-client-credentials-with-variables/src/Auth/TokenResponse.php index 56fbe31ea0c2..9222a65c62ca 100644 --- a/seed/php-model/oauth-client-credentials-with-variables/src/Auth/TokenResponse.php +++ b/seed/php-model/oauth-client-credentials-with-variables/src/Auth/TokenResponse.php @@ -37,15 +37,17 @@ class TokenResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->accessToken = $values['accessToken'];$this->expiresIn = $values['expiresIn'];$this->refreshToken = $values['refreshToken'] ?? null; + ) { + $this->accessToken = $values['accessToken']; + $this->expiresIn = $values['expiresIn']; + $this->refreshToken = $values['refreshToken'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/oauth-client-credentials-with-variables/src/Core/Json/JsonEncoder.php b/seed/php-model/oauth-client-credentials-with-variables/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/oauth-client-credentials-with-variables/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/oauth-client-credentials-with-variables/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/oauth-client-credentials-with-variables/src/Core/Json/JsonProperty.php b/seed/php-model/oauth-client-credentials-with-variables/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/oauth-client-credentials-with-variables/src/Core/Json/JsonProperty.php +++ b/seed/php-model/oauth-client-credentials-with-variables/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/oauth-client-credentials-with-variables/src/Core/Types/ArrayType.php b/seed/php-model/oauth-client-credentials-with-variables/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/oauth-client-credentials-with-variables/src/Core/Types/ArrayType.php +++ b/seed/php-model/oauth-client-credentials-with-variables/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/oauth-client-credentials-with-variables/src/Core/Types/Constant.php b/seed/php-model/oauth-client-credentials-with-variables/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/oauth-client-credentials-with-variables/src/Core/Types/Constant.php +++ b/seed/php-model/oauth-client-credentials-with-variables/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/oauth-client-credentials-with-variables/src/Core/Types/Union.php b/seed/php-model/oauth-client-credentials-with-variables/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/oauth-client-credentials-with-variables/src/Core/Types/Union.php +++ b/seed/php-model/oauth-client-credentials-with-variables/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/oauth-client-credentials-with-variables/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/oauth-client-credentials-with-variables/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/oauth-client-credentials-with-variables/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/oauth-client-credentials-with-variables/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/oauth-client-credentials-with-variables/tests/Core/Json/EnumTest.php b/seed/php-model/oauth-client-credentials-with-variables/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/oauth-client-credentials-with-variables/tests/Core/Json/EnumTest.php +++ b/seed/php-model/oauth-client-credentials-with-variables/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/oauth-client-credentials-with-variables/tests/Core/Json/TraitTest.php b/seed/php-model/oauth-client-credentials-with-variables/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/oauth-client-credentials-with-variables/tests/Core/Json/TraitTest.php +++ b/seed/php-model/oauth-client-credentials-with-variables/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/oauth-client-credentials-with-variables/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/oauth-client-credentials-with-variables/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/oauth-client-credentials-with-variables/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/oauth-client-credentials-with-variables/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/oauth-client-credentials/src/Auth/TokenResponse.php b/seed/php-model/oauth-client-credentials/src/Auth/TokenResponse.php index 56fbe31ea0c2..9222a65c62ca 100644 --- a/seed/php-model/oauth-client-credentials/src/Auth/TokenResponse.php +++ b/seed/php-model/oauth-client-credentials/src/Auth/TokenResponse.php @@ -37,15 +37,17 @@ class TokenResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->accessToken = $values['accessToken'];$this->expiresIn = $values['expiresIn'];$this->refreshToken = $values['refreshToken'] ?? null; + ) { + $this->accessToken = $values['accessToken']; + $this->expiresIn = $values['expiresIn']; + $this->refreshToken = $values['refreshToken'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/oauth-client-credentials/src/Core/Json/JsonEncoder.php b/seed/php-model/oauth-client-credentials/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/oauth-client-credentials/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/oauth-client-credentials/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/oauth-client-credentials/src/Core/Json/JsonProperty.php b/seed/php-model/oauth-client-credentials/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/oauth-client-credentials/src/Core/Json/JsonProperty.php +++ b/seed/php-model/oauth-client-credentials/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/oauth-client-credentials/src/Core/Types/ArrayType.php b/seed/php-model/oauth-client-credentials/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/oauth-client-credentials/src/Core/Types/ArrayType.php +++ b/seed/php-model/oauth-client-credentials/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/oauth-client-credentials/src/Core/Types/Constant.php b/seed/php-model/oauth-client-credentials/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/oauth-client-credentials/src/Core/Types/Constant.php +++ b/seed/php-model/oauth-client-credentials/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/oauth-client-credentials/src/Core/Types/Union.php b/seed/php-model/oauth-client-credentials/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/oauth-client-credentials/src/Core/Types/Union.php +++ b/seed/php-model/oauth-client-credentials/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/oauth-client-credentials/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/oauth-client-credentials/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/oauth-client-credentials/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/oauth-client-credentials/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/oauth-client-credentials/tests/Core/Json/EnumTest.php b/seed/php-model/oauth-client-credentials/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/oauth-client-credentials/tests/Core/Json/EnumTest.php +++ b/seed/php-model/oauth-client-credentials/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/oauth-client-credentials/tests/Core/Json/TraitTest.php b/seed/php-model/oauth-client-credentials/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/oauth-client-credentials/tests/Core/Json/TraitTest.php +++ b/seed/php-model/oauth-client-credentials/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/oauth-client-credentials/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/oauth-client-credentials/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/oauth-client-credentials/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/oauth-client-credentials/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/object/src/Core/Json/JsonEncoder.php b/seed/php-model/object/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/object/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/object/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/object/src/Core/Json/JsonProperty.php b/seed/php-model/object/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/object/src/Core/Json/JsonProperty.php +++ b/seed/php-model/object/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/object/src/Core/Types/ArrayType.php b/seed/php-model/object/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/object/src/Core/Types/ArrayType.php +++ b/seed/php-model/object/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/object/src/Core/Types/Constant.php b/seed/php-model/object/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/object/src/Core/Types/Constant.php +++ b/seed/php-model/object/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/object/src/Core/Types/Union.php b/seed/php-model/object/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/object/src/Core/Types/Union.php +++ b/seed/php-model/object/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/object/src/Name.php b/seed/php-model/object/src/Name.php index 995cb11dfdbd..754b0fd754ba 100644 --- a/seed/php-model/object/src/Name.php +++ b/seed/php-model/object/src/Name.php @@ -27,15 +27,16 @@ class Name extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->value = $values['value']; + ) { + $this->id = $values['id']; + $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/object/src/Type.php b/seed/php-model/object/src/Type.php index 42c8300c23dc..a6d4044e3348 100644 --- a/seed/php-model/object/src/Type.php +++ b/seed/php-model/object/src/Type.php @@ -195,15 +195,39 @@ class Type extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->one = $values['one'];$this->two = $values['two'];$this->three = $values['three'];$this->four = $values['four'];$this->five = $values['five'];$this->six = $values['six'];$this->seven = $values['seven'];$this->eight = $values['eight'];$this->nine = $values['nine'];$this->ten = $values['ten'];$this->eleven = $values['eleven'];$this->twelve = $values['twelve'];$this->thirteen = $values['thirteen'] ?? null;$this->fourteen = $values['fourteen'];$this->fifteen = $values['fifteen'];$this->sixteen = $values['sixteen'];$this->seventeen = $values['seventeen'];$this->eighteen = $values['eighteen'];$this->nineteen = $values['nineteen'];$this->twenty = $values['twenty'];$this->twentyone = $values['twentyone'];$this->twentytwo = $values['twentytwo'];$this->twentythree = $values['twentythree'];$this->twentyfour = $values['twentyfour'] ?? null;$this->twentyfive = $values['twentyfive'] ?? null; + ) { + $this->one = $values['one']; + $this->two = $values['two']; + $this->three = $values['three']; + $this->four = $values['four']; + $this->five = $values['five']; + $this->six = $values['six']; + $this->seven = $values['seven']; + $this->eight = $values['eight']; + $this->nine = $values['nine']; + $this->ten = $values['ten']; + $this->eleven = $values['eleven']; + $this->twelve = $values['twelve']; + $this->thirteen = $values['thirteen'] ?? null; + $this->fourteen = $values['fourteen']; + $this->fifteen = $values['fifteen']; + $this->sixteen = $values['sixteen']; + $this->seventeen = $values['seventeen']; + $this->eighteen = $values['eighteen']; + $this->nineteen = $values['nineteen']; + $this->twenty = $values['twenty']; + $this->twentyone = $values['twentyone']; + $this->twentytwo = $values['twentytwo']; + $this->twentythree = $values['twentythree']; + $this->twentyfour = $values['twentyfour'] ?? null; + $this->twentyfive = $values['twentyfive'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/object/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/object/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/object/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/object/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/object/tests/Core/Json/EnumTest.php b/seed/php-model/object/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/object/tests/Core/Json/EnumTest.php +++ b/seed/php-model/object/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/object/tests/Core/Json/TraitTest.php b/seed/php-model/object/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/object/tests/Core/Json/TraitTest.php +++ b/seed/php-model/object/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/object/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/object/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/object/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/object/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/objects-with-imports/src/Commons/Metadata/Metadata.php b/seed/php-model/objects-with-imports/src/Commons/Metadata/Metadata.php index 78a521a1ea8b..8ed054eb6c13 100644 --- a/seed/php-model/objects-with-imports/src/Commons/Metadata/Metadata.php +++ b/seed/php-model/objects-with-imports/src/Commons/Metadata/Metadata.php @@ -28,15 +28,16 @@ class Metadata extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->data = $values['data'] ?? null; + ) { + $this->id = $values['id']; + $this->data = $values['data'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/objects-with-imports/src/Core/Json/JsonEncoder.php b/seed/php-model/objects-with-imports/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/objects-with-imports/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/objects-with-imports/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/objects-with-imports/src/Core/Json/JsonProperty.php b/seed/php-model/objects-with-imports/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/objects-with-imports/src/Core/Json/JsonProperty.php +++ b/seed/php-model/objects-with-imports/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/objects-with-imports/src/Core/Types/ArrayType.php b/seed/php-model/objects-with-imports/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/objects-with-imports/src/Core/Types/ArrayType.php +++ b/seed/php-model/objects-with-imports/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/objects-with-imports/src/Core/Types/Constant.php b/seed/php-model/objects-with-imports/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/objects-with-imports/src/Core/Types/Constant.php +++ b/seed/php-model/objects-with-imports/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/objects-with-imports/src/Core/Types/Union.php b/seed/php-model/objects-with-imports/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/objects-with-imports/src/Core/Types/Union.php +++ b/seed/php-model/objects-with-imports/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/objects-with-imports/src/File/Directory/Directory.php b/seed/php-model/objects-with-imports/src/File/Directory/Directory.php index 6c18c2f18f98..15f84c2ed288 100644 --- a/seed/php-model/objects-with-imports/src/File/Directory/Directory.php +++ b/seed/php-model/objects-with-imports/src/File/Directory/Directory.php @@ -36,15 +36,17 @@ class Directory extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->files = $values['files'] ?? null;$this->directories = $values['directories'] ?? null; + ) { + $this->name = $values['name']; + $this->files = $values['files'] ?? null; + $this->directories = $values['directories'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/objects-with-imports/src/File/File.php b/seed/php-model/objects-with-imports/src/File/File.php index 0f4c92a2cd0c..794400edd228 100644 --- a/seed/php-model/objects-with-imports/src/File/File.php +++ b/seed/php-model/objects-with-imports/src/File/File.php @@ -34,15 +34,17 @@ class File extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->contents = $values['contents'];$this->info = $values['info']; + ) { + $this->name = $values['name']; + $this->contents = $values['contents']; + $this->info = $values['info']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/objects-with-imports/src/File/FileInfo.php b/seed/php-model/objects-with-imports/src/File/FileInfo.php index 67094c4984f3..3dc2a3b7327a 100644 --- a/seed/php-model/objects-with-imports/src/File/FileInfo.php +++ b/seed/php-model/objects-with-imports/src/File/FileInfo.php @@ -2,8 +2,8 @@ namespace Seed\File; -enum FileInfo - : string { +enum FileInfo: string +{ case Regular = "REGULAR"; case Directory = "DIRECTORY"; } diff --git a/seed/php-model/objects-with-imports/src/Node.php b/seed/php-model/objects-with-imports/src/Node.php index 150dc7ab31e3..803a23278095 100644 --- a/seed/php-model/objects-with-imports/src/Node.php +++ b/seed/php-model/objects-with-imports/src/Node.php @@ -35,15 +35,17 @@ class Node extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->label = $values['label'] ?? null;$this->metadata = $values['metadata'] ?? null; + ) { + $this->id = $values['id']; + $this->label = $values['label'] ?? null; + $this->metadata = $values['metadata'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/objects-with-imports/src/Tree.php b/seed/php-model/objects-with-imports/src/Tree.php index 22513e4715af..19876d8e8330 100644 --- a/seed/php-model/objects-with-imports/src/Tree.php +++ b/seed/php-model/objects-with-imports/src/Tree.php @@ -21,15 +21,15 @@ class Tree extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->nodes = $values['nodes'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/objects-with-imports/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/objects-with-imports/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/objects-with-imports/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/objects-with-imports/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/objects-with-imports/tests/Core/Json/EnumTest.php b/seed/php-model/objects-with-imports/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/objects-with-imports/tests/Core/Json/EnumTest.php +++ b/seed/php-model/objects-with-imports/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/objects-with-imports/tests/Core/Json/TraitTest.php b/seed/php-model/objects-with-imports/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/objects-with-imports/tests/Core/Json/TraitTest.php +++ b/seed/php-model/objects-with-imports/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/objects-with-imports/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/objects-with-imports/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/objects-with-imports/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/objects-with-imports/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/optional/src/Core/Json/JsonEncoder.php b/seed/php-model/optional/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/optional/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/optional/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/optional/src/Core/Json/JsonProperty.php b/seed/php-model/optional/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/optional/src/Core/Json/JsonProperty.php +++ b/seed/php-model/optional/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/optional/src/Core/Types/ArrayType.php b/seed/php-model/optional/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/optional/src/Core/Types/ArrayType.php +++ b/seed/php-model/optional/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/optional/src/Core/Types/Constant.php b/seed/php-model/optional/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/optional/src/Core/Types/Constant.php +++ b/seed/php-model/optional/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/optional/src/Core/Types/Union.php b/seed/php-model/optional/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/optional/src/Core/Types/Union.php +++ b/seed/php-model/optional/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/optional/src/Optional/DeployParams.php b/seed/php-model/optional/src/Optional/DeployParams.php index 49aaca4f00e6..7c8a8bcd86ce 100644 --- a/seed/php-model/optional/src/Optional/DeployParams.php +++ b/seed/php-model/optional/src/Optional/DeployParams.php @@ -20,15 +20,15 @@ class DeployParams extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->updateDraft = $values['updateDraft'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/optional/src/Optional/DeployResponse.php b/seed/php-model/optional/src/Optional/DeployResponse.php index 5fa12f46dd74..6fd213534dee 100644 --- a/seed/php-model/optional/src/Optional/DeployResponse.php +++ b/seed/php-model/optional/src/Optional/DeployResponse.php @@ -20,15 +20,15 @@ class DeployResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->success = $values['success']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/optional/src/Optional/SendOptionalBodyRequest.php b/seed/php-model/optional/src/Optional/SendOptionalBodyRequest.php index 3dc1a1de3f9a..c8a2f4ad69b6 100644 --- a/seed/php-model/optional/src/Optional/SendOptionalBodyRequest.php +++ b/seed/php-model/optional/src/Optional/SendOptionalBodyRequest.php @@ -20,15 +20,15 @@ class SendOptionalBodyRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->message = $values['message']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/optional/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/optional/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/optional/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/optional/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/optional/tests/Core/Json/EnumTest.php b/seed/php-model/optional/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/optional/tests/Core/Json/EnumTest.php +++ b/seed/php-model/optional/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/optional/tests/Core/Json/TraitTest.php b/seed/php-model/optional/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/optional/tests/Core/Json/TraitTest.php +++ b/seed/php-model/optional/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/optional/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/optional/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/optional/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/optional/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/package-yml/src/Core/Json/JsonEncoder.php b/seed/php-model/package-yml/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/package-yml/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/package-yml/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/package-yml/src/Core/Json/JsonProperty.php b/seed/php-model/package-yml/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/package-yml/src/Core/Json/JsonProperty.php +++ b/seed/php-model/package-yml/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/package-yml/src/Core/Types/ArrayType.php b/seed/php-model/package-yml/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/package-yml/src/Core/Types/ArrayType.php +++ b/seed/php-model/package-yml/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/package-yml/src/Core/Types/Constant.php b/seed/php-model/package-yml/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/package-yml/src/Core/Types/Constant.php +++ b/seed/php-model/package-yml/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/package-yml/src/Core/Types/Union.php b/seed/php-model/package-yml/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/package-yml/src/Core/Types/Union.php +++ b/seed/php-model/package-yml/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/package-yml/src/EchoRequest.php b/seed/php-model/package-yml/src/EchoRequest.php index 449dca3a6103..6a21cd06f992 100644 --- a/seed/php-model/package-yml/src/EchoRequest.php +++ b/seed/php-model/package-yml/src/EchoRequest.php @@ -27,15 +27,16 @@ class EchoRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->size = $values['size']; + ) { + $this->name = $values['name']; + $this->size = $values['size']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/package-yml/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/package-yml/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/package-yml/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/package-yml/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/package-yml/tests/Core/Json/EnumTest.php b/seed/php-model/package-yml/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/package-yml/tests/Core/Json/EnumTest.php +++ b/seed/php-model/package-yml/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/package-yml/tests/Core/Json/TraitTest.php b/seed/php-model/package-yml/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/package-yml/tests/Core/Json/TraitTest.php +++ b/seed/php-model/package-yml/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/package-yml/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/package-yml/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/package-yml/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/package-yml/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/pagination-custom/src/Core/Json/JsonEncoder.php b/seed/php-model/pagination-custom/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/pagination-custom/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/pagination-custom/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/pagination-custom/src/Core/Json/JsonProperty.php b/seed/php-model/pagination-custom/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/pagination-custom/src/Core/Json/JsonProperty.php +++ b/seed/php-model/pagination-custom/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/pagination-custom/src/Core/Types/ArrayType.php b/seed/php-model/pagination-custom/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/pagination-custom/src/Core/Types/ArrayType.php +++ b/seed/php-model/pagination-custom/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/pagination-custom/src/Core/Types/Constant.php b/seed/php-model/pagination-custom/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/pagination-custom/src/Core/Types/Constant.php +++ b/seed/php-model/pagination-custom/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/pagination-custom/src/Core/Types/Union.php b/seed/php-model/pagination-custom/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/pagination-custom/src/Core/Types/Union.php +++ b/seed/php-model/pagination-custom/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/pagination-custom/src/UsernameCursor.php b/seed/php-model/pagination-custom/src/UsernameCursor.php index 59ca5029f778..486a18cd1557 100644 --- a/seed/php-model/pagination-custom/src/UsernameCursor.php +++ b/seed/php-model/pagination-custom/src/UsernameCursor.php @@ -20,15 +20,15 @@ class UsernameCursor extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->cursor = $values['cursor']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/pagination-custom/src/UsernamePage.php b/seed/php-model/pagination-custom/src/UsernamePage.php index e5b9cde805ee..2b60f8863692 100644 --- a/seed/php-model/pagination-custom/src/UsernamePage.php +++ b/seed/php-model/pagination-custom/src/UsernamePage.php @@ -28,15 +28,16 @@ class UsernamePage extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->after = $values['after'] ?? null;$this->data = $values['data']; + ) { + $this->after = $values['after'] ?? null; + $this->data = $values['data']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/pagination-custom/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/pagination-custom/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/pagination-custom/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/pagination-custom/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/pagination-custom/tests/Core/Json/EnumTest.php b/seed/php-model/pagination-custom/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/pagination-custom/tests/Core/Json/EnumTest.php +++ b/seed/php-model/pagination-custom/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/pagination-custom/tests/Core/Json/TraitTest.php b/seed/php-model/pagination-custom/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/pagination-custom/tests/Core/Json/TraitTest.php +++ b/seed/php-model/pagination-custom/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/pagination-custom/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/pagination-custom/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/pagination-custom/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/pagination-custom/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/pagination/src/Complex/Conversation.php b/seed/php-model/pagination/src/Complex/Conversation.php index 1f6b85ab66cb..12fba74cad08 100644 --- a/seed/php-model/pagination/src/Complex/Conversation.php +++ b/seed/php-model/pagination/src/Complex/Conversation.php @@ -20,15 +20,15 @@ class Conversation extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->foo = $values['foo']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/pagination/src/Complex/CursorPages.php b/seed/php-model/pagination/src/Complex/CursorPages.php index 96b0d203ab6c..37a558783614 100644 --- a/seed/php-model/pagination/src/Complex/CursorPages.php +++ b/seed/php-model/pagination/src/Complex/CursorPages.php @@ -48,15 +48,19 @@ class CursorPages extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->next = $values['next'] ?? null;$this->page = $values['page'] ?? null;$this->perPage = $values['perPage'] ?? null;$this->totalPages = $values['totalPages'] ?? null;$this->type = $values['type']; + ) { + $this->next = $values['next'] ?? null; + $this->page = $values['page'] ?? null; + $this->perPage = $values['perPage'] ?? null; + $this->totalPages = $values['totalPages'] ?? null; + $this->type = $values['type']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/pagination/src/Complex/MultipleFilterSearchRequest.php b/seed/php-model/pagination/src/Complex/MultipleFilterSearchRequest.php index a20d3a00e6f5..8624f8d7395a 100644 --- a/seed/php-model/pagination/src/Complex/MultipleFilterSearchRequest.php +++ b/seed/php-model/pagination/src/Complex/MultipleFilterSearchRequest.php @@ -20,7 +20,7 @@ class MultipleFilterSearchRequest extends JsonSerializableType * |array * )|null $value */ - #[JsonProperty('value'), Union([MultipleFilterSearchRequest::class],[SingleFilterSearchRequest::class],'null')] + #[JsonProperty('value'), Union([MultipleFilterSearchRequest::class], [SingleFilterSearchRequest::class], 'null')] public array|null $value; /** @@ -34,15 +34,16 @@ class MultipleFilterSearchRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->operator = $values['operator'] ?? null;$this->value = $values['value'] ?? null; + ) { + $this->operator = $values['operator'] ?? null; + $this->value = $values['value'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/pagination/src/Complex/MultipleFilterSearchRequestOperator.php b/seed/php-model/pagination/src/Complex/MultipleFilterSearchRequestOperator.php index 178b501bdb1e..abb20e8eee58 100644 --- a/seed/php-model/pagination/src/Complex/MultipleFilterSearchRequestOperator.php +++ b/seed/php-model/pagination/src/Complex/MultipleFilterSearchRequestOperator.php @@ -2,8 +2,8 @@ namespace Seed\Complex; -enum MultipleFilterSearchRequestOperator - : string { +enum MultipleFilterSearchRequestOperator: string +{ case And_ = "AND"; case Or_ = "OR"; } diff --git a/seed/php-model/pagination/src/Complex/PaginatedConversationResponse.php b/seed/php-model/pagination/src/Complex/PaginatedConversationResponse.php index 22b4796b4304..4b445194ee48 100644 --- a/seed/php-model/pagination/src/Complex/PaginatedConversationResponse.php +++ b/seed/php-model/pagination/src/Complex/PaginatedConversationResponse.php @@ -42,15 +42,18 @@ class PaginatedConversationResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->conversations = $values['conversations'];$this->pages = $values['pages'] ?? null;$this->totalCount = $values['totalCount'];$this->type = $values['type']; + ) { + $this->conversations = $values['conversations']; + $this->pages = $values['pages'] ?? null; + $this->totalCount = $values['totalCount']; + $this->type = $values['type']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/pagination/src/Complex/SearchRequest.php b/seed/php-model/pagination/src/Complex/SearchRequest.php index ecc237ef8e54..dbc6d3d676e3 100644 --- a/seed/php-model/pagination/src/Complex/SearchRequest.php +++ b/seed/php-model/pagination/src/Complex/SearchRequest.php @@ -20,7 +20,7 @@ class SearchRequest extends JsonSerializableType * |MultipleFilterSearchRequest * ) $query */ - #[JsonProperty('query'), Union(SingleFilterSearchRequest::class,MultipleFilterSearchRequest::class)] + #[JsonProperty('query'), Union(SingleFilterSearchRequest::class, MultipleFilterSearchRequest::class)] public SingleFilterSearchRequest|MultipleFilterSearchRequest $query; /** @@ -34,15 +34,16 @@ class SearchRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->pagination = $values['pagination'] ?? null;$this->query = $values['query']; + ) { + $this->pagination = $values['pagination'] ?? null; + $this->query = $values['query']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/pagination/src/Complex/SingleFilterSearchRequest.php b/seed/php-model/pagination/src/Complex/SingleFilterSearchRequest.php index 1dccd3c0d200..7d751b395ea8 100644 --- a/seed/php-model/pagination/src/Complex/SingleFilterSearchRequest.php +++ b/seed/php-model/pagination/src/Complex/SingleFilterSearchRequest.php @@ -34,15 +34,17 @@ class SingleFilterSearchRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->field = $values['field'] ?? null;$this->operator = $values['operator'] ?? null;$this->value = $values['value'] ?? null; + ) { + $this->field = $values['field'] ?? null; + $this->operator = $values['operator'] ?? null; + $this->value = $values['value'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/pagination/src/Complex/SingleFilterSearchRequestOperator.php b/seed/php-model/pagination/src/Complex/SingleFilterSearchRequestOperator.php index 69bdb38335af..15ffb27af3c7 100644 --- a/seed/php-model/pagination/src/Complex/SingleFilterSearchRequestOperator.php +++ b/seed/php-model/pagination/src/Complex/SingleFilterSearchRequestOperator.php @@ -2,8 +2,8 @@ namespace Seed\Complex; -enum SingleFilterSearchRequestOperator - : string { +enum SingleFilterSearchRequestOperator: string +{ case Equals = "="; case NotEquals = "!="; case In = "IN"; diff --git a/seed/php-model/pagination/src/Complex/StartingAfterPaging.php b/seed/php-model/pagination/src/Complex/StartingAfterPaging.php index 85c65e38e230..708a83776a2b 100644 --- a/seed/php-model/pagination/src/Complex/StartingAfterPaging.php +++ b/seed/php-model/pagination/src/Complex/StartingAfterPaging.php @@ -27,15 +27,16 @@ class StartingAfterPaging extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->perPage = $values['perPage'];$this->startingAfter = $values['startingAfter'] ?? null; + ) { + $this->perPage = $values['perPage']; + $this->startingAfter = $values['startingAfter'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/pagination/src/Core/Json/JsonEncoder.php b/seed/php-model/pagination/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/pagination/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/pagination/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/pagination/src/Core/Json/JsonProperty.php b/seed/php-model/pagination/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/pagination/src/Core/Json/JsonProperty.php +++ b/seed/php-model/pagination/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/pagination/src/Core/Types/ArrayType.php b/seed/php-model/pagination/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/pagination/src/Core/Types/ArrayType.php +++ b/seed/php-model/pagination/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/pagination/src/Core/Types/Constant.php b/seed/php-model/pagination/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/pagination/src/Core/Types/Constant.php +++ b/seed/php-model/pagination/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/pagination/src/Core/Types/Union.php b/seed/php-model/pagination/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/pagination/src/Core/Types/Union.php +++ b/seed/php-model/pagination/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/pagination/src/InlineUsers/InlineUsers/ListUsersExtendedOptionalListResponse.php b/seed/php-model/pagination/src/InlineUsers/InlineUsers/ListUsersExtendedOptionalListResponse.php index 8b077df35463..f8bb4bcbae1e 100644 --- a/seed/php-model/pagination/src/InlineUsers/InlineUsers/ListUsersExtendedOptionalListResponse.php +++ b/seed/php-model/pagination/src/InlineUsers/InlineUsers/ListUsersExtendedOptionalListResponse.php @@ -25,15 +25,17 @@ class ListUsersExtendedOptionalListResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->data = $values['data'];$this->next = $values['next'] ?? null;$this->totalCount = $values['totalCount']; + ) { + $this->data = $values['data']; + $this->next = $values['next'] ?? null; + $this->totalCount = $values['totalCount']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/pagination/src/InlineUsers/InlineUsers/ListUsersExtendedResponse.php b/seed/php-model/pagination/src/InlineUsers/InlineUsers/ListUsersExtendedResponse.php index 9a6630905fc1..ff955e11e6ee 100644 --- a/seed/php-model/pagination/src/InlineUsers/InlineUsers/ListUsersExtendedResponse.php +++ b/seed/php-model/pagination/src/InlineUsers/InlineUsers/ListUsersExtendedResponse.php @@ -25,15 +25,17 @@ class ListUsersExtendedResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->data = $values['data'];$this->next = $values['next'] ?? null;$this->totalCount = $values['totalCount']; + ) { + $this->data = $values['data']; + $this->next = $values['next'] ?? null; + $this->totalCount = $values['totalCount']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/pagination/src/InlineUsers/InlineUsers/ListUsersMixedTypePaginationResponse.php b/seed/php-model/pagination/src/InlineUsers/InlineUsers/ListUsersMixedTypePaginationResponse.php index 4a7add79213c..0a229a476fc8 100644 --- a/seed/php-model/pagination/src/InlineUsers/InlineUsers/ListUsersMixedTypePaginationResponse.php +++ b/seed/php-model/pagination/src/InlineUsers/InlineUsers/ListUsersMixedTypePaginationResponse.php @@ -27,15 +27,16 @@ class ListUsersMixedTypePaginationResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->next = $values['next'];$this->data = $values['data']; + ) { + $this->next = $values['next']; + $this->data = $values['data']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/pagination/src/InlineUsers/InlineUsers/ListUsersPaginationResponse.php b/seed/php-model/pagination/src/InlineUsers/InlineUsers/ListUsersPaginationResponse.php index 254c4afde17b..c70b591756c1 100644 --- a/seed/php-model/pagination/src/InlineUsers/InlineUsers/ListUsersPaginationResponse.php +++ b/seed/php-model/pagination/src/InlineUsers/InlineUsers/ListUsersPaginationResponse.php @@ -41,15 +41,18 @@ class ListUsersPaginationResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->hasNextPage = $values['hasNextPage'] ?? null;$this->page = $values['page'] ?? null;$this->totalCount = $values['totalCount'];$this->data = $values['data']; + ) { + $this->hasNextPage = $values['hasNextPage'] ?? null; + $this->page = $values['page'] ?? null; + $this->totalCount = $values['totalCount']; + $this->data = $values['data']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/pagination/src/InlineUsers/InlineUsers/NextPage.php b/seed/php-model/pagination/src/InlineUsers/InlineUsers/NextPage.php index d59a9462b20d..0a5ee2aa9e09 100644 --- a/seed/php-model/pagination/src/InlineUsers/InlineUsers/NextPage.php +++ b/seed/php-model/pagination/src/InlineUsers/InlineUsers/NextPage.php @@ -27,15 +27,16 @@ class NextPage extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->page = $values['page'];$this->startingAfter = $values['startingAfter']; + ) { + $this->page = $values['page']; + $this->startingAfter = $values['startingAfter']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/pagination/src/InlineUsers/InlineUsers/Order.php b/seed/php-model/pagination/src/InlineUsers/InlineUsers/Order.php index 41f923f6ccc3..dcbc779f3adb 100644 --- a/seed/php-model/pagination/src/InlineUsers/InlineUsers/Order.php +++ b/seed/php-model/pagination/src/InlineUsers/InlineUsers/Order.php @@ -2,8 +2,8 @@ namespace Seed\InlineUsers\InlineUsers; -enum Order - : string { +enum Order: string +{ case Asc = "asc"; case Desc = "desc"; } diff --git a/seed/php-model/pagination/src/InlineUsers/InlineUsers/Page.php b/seed/php-model/pagination/src/InlineUsers/InlineUsers/Page.php index 9aee523f530b..c066449d0678 100644 --- a/seed/php-model/pagination/src/InlineUsers/InlineUsers/Page.php +++ b/seed/php-model/pagination/src/InlineUsers/InlineUsers/Page.php @@ -41,15 +41,18 @@ class Page extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->page = $values['page'];$this->next = $values['next'] ?? null;$this->perPage = $values['perPage'];$this->totalPage = $values['totalPage']; + ) { + $this->page = $values['page']; + $this->next = $values['next'] ?? null; + $this->perPage = $values['perPage']; + $this->totalPage = $values['totalPage']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/pagination/src/InlineUsers/InlineUsers/Traits/UserOptionalListPage.php b/seed/php-model/pagination/src/InlineUsers/InlineUsers/Traits/UserOptionalListPage.php index dd4330f75018..641336e27953 100644 --- a/seed/php-model/pagination/src/InlineUsers/InlineUsers/Traits/UserOptionalListPage.php +++ b/seed/php-model/pagination/src/InlineUsers/InlineUsers/Traits/UserOptionalListPage.php @@ -9,7 +9,7 @@ * @property UserOptionalListContainer $data * @property ?string $next */ -trait UserOptionalListPage +trait UserOptionalListPage { /** * @var UserOptionalListContainer $data diff --git a/seed/php-model/pagination/src/InlineUsers/InlineUsers/Traits/UserPage.php b/seed/php-model/pagination/src/InlineUsers/InlineUsers/Traits/UserPage.php index e17199a01885..0a91241431bc 100644 --- a/seed/php-model/pagination/src/InlineUsers/InlineUsers/Traits/UserPage.php +++ b/seed/php-model/pagination/src/InlineUsers/InlineUsers/Traits/UserPage.php @@ -9,7 +9,7 @@ * @property UserListContainer $data * @property ?string $next */ -trait UserPage +trait UserPage { /** * @var UserListContainer $data diff --git a/seed/php-model/pagination/src/InlineUsers/InlineUsers/User.php b/seed/php-model/pagination/src/InlineUsers/InlineUsers/User.php index 6353a41fb484..a01ec4832c65 100644 --- a/seed/php-model/pagination/src/InlineUsers/InlineUsers/User.php +++ b/seed/php-model/pagination/src/InlineUsers/InlineUsers/User.php @@ -27,15 +27,16 @@ class User extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->id = $values['id']; + ) { + $this->name = $values['name']; + $this->id = $values['id']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/pagination/src/InlineUsers/InlineUsers/UserListContainer.php b/seed/php-model/pagination/src/InlineUsers/InlineUsers/UserListContainer.php index 64040c61a3ae..6606746c0b0b 100644 --- a/seed/php-model/pagination/src/InlineUsers/InlineUsers/UserListContainer.php +++ b/seed/php-model/pagination/src/InlineUsers/InlineUsers/UserListContainer.php @@ -21,15 +21,15 @@ class UserListContainer extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->users = $values['users']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/pagination/src/InlineUsers/InlineUsers/UserOptionalListContainer.php b/seed/php-model/pagination/src/InlineUsers/InlineUsers/UserOptionalListContainer.php index dbd139729145..62c453a6b4da 100644 --- a/seed/php-model/pagination/src/InlineUsers/InlineUsers/UserOptionalListContainer.php +++ b/seed/php-model/pagination/src/InlineUsers/InlineUsers/UserOptionalListContainer.php @@ -21,15 +21,15 @@ class UserOptionalListContainer extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->users = $values['users'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/pagination/src/InlineUsers/InlineUsers/UserOptionalListPage.php b/seed/php-model/pagination/src/InlineUsers/InlineUsers/UserOptionalListPage.php index 552cf7cf0790..0ac799a9da0c 100644 --- a/seed/php-model/pagination/src/InlineUsers/InlineUsers/UserOptionalListPage.php +++ b/seed/php-model/pagination/src/InlineUsers/InlineUsers/UserOptionalListPage.php @@ -27,15 +27,16 @@ class UserOptionalListPage extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->data = $values['data'];$this->next = $values['next'] ?? null; + ) { + $this->data = $values['data']; + $this->next = $values['next'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/pagination/src/InlineUsers/InlineUsers/UserPage.php b/seed/php-model/pagination/src/InlineUsers/InlineUsers/UserPage.php index 80dc283176ae..0dbf05722486 100644 --- a/seed/php-model/pagination/src/InlineUsers/InlineUsers/UserPage.php +++ b/seed/php-model/pagination/src/InlineUsers/InlineUsers/UserPage.php @@ -27,15 +27,16 @@ class UserPage extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->data = $values['data'];$this->next = $values['next'] ?? null; + ) { + $this->data = $values['data']; + $this->next = $values['next'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/pagination/src/InlineUsers/InlineUsers/UsernameContainer.php b/seed/php-model/pagination/src/InlineUsers/InlineUsers/UsernameContainer.php index 177640087892..e522377d2ebc 100644 --- a/seed/php-model/pagination/src/InlineUsers/InlineUsers/UsernameContainer.php +++ b/seed/php-model/pagination/src/InlineUsers/InlineUsers/UsernameContainer.php @@ -21,15 +21,15 @@ class UsernameContainer extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->results = $values['results']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/pagination/src/InlineUsers/InlineUsers/Users.php b/seed/php-model/pagination/src/InlineUsers/InlineUsers/Users.php index 55114b9054d6..02fb4c1a4178 100644 --- a/seed/php-model/pagination/src/InlineUsers/InlineUsers/Users.php +++ b/seed/php-model/pagination/src/InlineUsers/InlineUsers/Users.php @@ -21,15 +21,15 @@ class Users extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->users = $values['users']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/pagination/src/InlineUsers/InlineUsers/WithCursor.php b/seed/php-model/pagination/src/InlineUsers/InlineUsers/WithCursor.php index 9e98c48eabd7..01236d8ea71d 100644 --- a/seed/php-model/pagination/src/InlineUsers/InlineUsers/WithCursor.php +++ b/seed/php-model/pagination/src/InlineUsers/InlineUsers/WithCursor.php @@ -20,15 +20,15 @@ class WithCursor extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->cursor = $values['cursor'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/pagination/src/InlineUsers/InlineUsers/WithPage.php b/seed/php-model/pagination/src/InlineUsers/InlineUsers/WithPage.php index f88cb0eec347..f3e8b4e2557a 100644 --- a/seed/php-model/pagination/src/InlineUsers/InlineUsers/WithPage.php +++ b/seed/php-model/pagination/src/InlineUsers/InlineUsers/WithPage.php @@ -20,15 +20,15 @@ class WithPage extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->page = $values['page'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/pagination/src/UsernameCursor.php b/seed/php-model/pagination/src/UsernameCursor.php index 59ca5029f778..486a18cd1557 100644 --- a/seed/php-model/pagination/src/UsernameCursor.php +++ b/seed/php-model/pagination/src/UsernameCursor.php @@ -20,15 +20,15 @@ class UsernameCursor extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->cursor = $values['cursor']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/pagination/src/UsernamePage.php b/seed/php-model/pagination/src/UsernamePage.php index e5b9cde805ee..2b60f8863692 100644 --- a/seed/php-model/pagination/src/UsernamePage.php +++ b/seed/php-model/pagination/src/UsernamePage.php @@ -28,15 +28,16 @@ class UsernamePage extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->after = $values['after'] ?? null;$this->data = $values['data']; + ) { + $this->after = $values['after'] ?? null; + $this->data = $values['data']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/pagination/src/Users/ListUsersExtendedOptionalListResponse.php b/seed/php-model/pagination/src/Users/ListUsersExtendedOptionalListResponse.php index 92c722c02823..a00eb4ebb204 100644 --- a/seed/php-model/pagination/src/Users/ListUsersExtendedOptionalListResponse.php +++ b/seed/php-model/pagination/src/Users/ListUsersExtendedOptionalListResponse.php @@ -25,15 +25,17 @@ class ListUsersExtendedOptionalListResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->data = $values['data'];$this->next = $values['next'] ?? null;$this->totalCount = $values['totalCount']; + ) { + $this->data = $values['data']; + $this->next = $values['next'] ?? null; + $this->totalCount = $values['totalCount']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/pagination/src/Users/ListUsersExtendedResponse.php b/seed/php-model/pagination/src/Users/ListUsersExtendedResponse.php index f339dcc4f3d9..1421cbf55fba 100644 --- a/seed/php-model/pagination/src/Users/ListUsersExtendedResponse.php +++ b/seed/php-model/pagination/src/Users/ListUsersExtendedResponse.php @@ -25,15 +25,17 @@ class ListUsersExtendedResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->data = $values['data'];$this->next = $values['next'] ?? null;$this->totalCount = $values['totalCount']; + ) { + $this->data = $values['data']; + $this->next = $values['next'] ?? null; + $this->totalCount = $values['totalCount']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/pagination/src/Users/ListUsersMixedTypePaginationResponse.php b/seed/php-model/pagination/src/Users/ListUsersMixedTypePaginationResponse.php index 8a3414ec4eb7..d99ab8c68264 100644 --- a/seed/php-model/pagination/src/Users/ListUsersMixedTypePaginationResponse.php +++ b/seed/php-model/pagination/src/Users/ListUsersMixedTypePaginationResponse.php @@ -28,15 +28,16 @@ class ListUsersMixedTypePaginationResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->next = $values['next'];$this->data = $values['data']; + ) { + $this->next = $values['next']; + $this->data = $values['data']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/pagination/src/Users/ListUsersOptionalDataPaginationResponse.php b/seed/php-model/pagination/src/Users/ListUsersOptionalDataPaginationResponse.php new file mode 100644 index 000000000000..25e9c51f3032 --- /dev/null +++ b/seed/php-model/pagination/src/Users/ListUsersOptionalDataPaginationResponse.php @@ -0,0 +1,59 @@ + $data + */ + #[JsonProperty('data'), ArrayType([User::class])] + public ?array $data; + + /** + * @param array{ + * totalCount: int, + * hasNextPage?: ?bool, + * page?: ?Page, + * data?: ?array, + * } $values + */ + public function __construct( + array $values, + ) { + $this->hasNextPage = $values['hasNextPage'] ?? null; + $this->page = $values['page'] ?? null; + $this->totalCount = $values['totalCount']; + $this->data = $values['data'] ?? null; + } + + /** + * @return string + */ + public function __toString(): string + { + return $this->toJson(); + } +} diff --git a/seed/php-model/pagination/src/Users/ListUsersPaginationResponse.php b/seed/php-model/pagination/src/Users/ListUsersPaginationResponse.php index 546b5ad3768a..5d48ce3c46e8 100644 --- a/seed/php-model/pagination/src/Users/ListUsersPaginationResponse.php +++ b/seed/php-model/pagination/src/Users/ListUsersPaginationResponse.php @@ -42,15 +42,18 @@ class ListUsersPaginationResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->hasNextPage = $values['hasNextPage'] ?? null;$this->page = $values['page'] ?? null;$this->totalCount = $values['totalCount'];$this->data = $values['data']; + ) { + $this->hasNextPage = $values['hasNextPage'] ?? null; + $this->page = $values['page'] ?? null; + $this->totalCount = $values['totalCount']; + $this->data = $values['data']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/pagination/src/Users/NextPage.php b/seed/php-model/pagination/src/Users/NextPage.php index c7afb01cef9e..c2130af9e307 100644 --- a/seed/php-model/pagination/src/Users/NextPage.php +++ b/seed/php-model/pagination/src/Users/NextPage.php @@ -27,15 +27,16 @@ class NextPage extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->page = $values['page'];$this->startingAfter = $values['startingAfter']; + ) { + $this->page = $values['page']; + $this->startingAfter = $values['startingAfter']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/pagination/src/Users/Order.php b/seed/php-model/pagination/src/Users/Order.php index 29ee9c2876a5..0fb09df75a6f 100644 --- a/seed/php-model/pagination/src/Users/Order.php +++ b/seed/php-model/pagination/src/Users/Order.php @@ -2,8 +2,8 @@ namespace Seed\Users; -enum Order - : string { +enum Order: string +{ case Asc = "asc"; case Desc = "desc"; } diff --git a/seed/php-model/pagination/src/Users/Page.php b/seed/php-model/pagination/src/Users/Page.php index ef870536fb0d..8271ae7510f8 100644 --- a/seed/php-model/pagination/src/Users/Page.php +++ b/seed/php-model/pagination/src/Users/Page.php @@ -41,15 +41,18 @@ class Page extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->page = $values['page'];$this->next = $values['next'] ?? null;$this->perPage = $values['perPage'];$this->totalPage = $values['totalPage']; + ) { + $this->page = $values['page']; + $this->next = $values['next'] ?? null; + $this->perPage = $values['perPage']; + $this->totalPage = $values['totalPage']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/pagination/src/Users/Traits/UserOptionalListPage.php b/seed/php-model/pagination/src/Users/Traits/UserOptionalListPage.php index 86bcd2757412..e91bfc31f3e9 100644 --- a/seed/php-model/pagination/src/Users/Traits/UserOptionalListPage.php +++ b/seed/php-model/pagination/src/Users/Traits/UserOptionalListPage.php @@ -9,7 +9,7 @@ * @property UserOptionalListContainer $data * @property ?string $next */ -trait UserOptionalListPage +trait UserOptionalListPage { /** * @var UserOptionalListContainer $data diff --git a/seed/php-model/pagination/src/Users/Traits/UserPage.php b/seed/php-model/pagination/src/Users/Traits/UserPage.php index 6ba31fa12eb3..98f510f6a1d6 100644 --- a/seed/php-model/pagination/src/Users/Traits/UserPage.php +++ b/seed/php-model/pagination/src/Users/Traits/UserPage.php @@ -9,7 +9,7 @@ * @property UserListContainer $data * @property ?string $next */ -trait UserPage +trait UserPage { /** * @var UserListContainer $data diff --git a/seed/php-model/pagination/src/Users/User.php b/seed/php-model/pagination/src/Users/User.php index e48ad6f02a8a..43f9316929fa 100644 --- a/seed/php-model/pagination/src/Users/User.php +++ b/seed/php-model/pagination/src/Users/User.php @@ -27,15 +27,16 @@ class User extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->id = $values['id']; + ) { + $this->name = $values['name']; + $this->id = $values['id']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/pagination/src/Users/UserListContainer.php b/seed/php-model/pagination/src/Users/UserListContainer.php index b5f730455a02..ea76b2781de5 100644 --- a/seed/php-model/pagination/src/Users/UserListContainer.php +++ b/seed/php-model/pagination/src/Users/UserListContainer.php @@ -21,15 +21,15 @@ class UserListContainer extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->users = $values['users']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/pagination/src/Users/UserOptionalListContainer.php b/seed/php-model/pagination/src/Users/UserOptionalListContainer.php index 283bff951772..f097b4bc6da3 100644 --- a/seed/php-model/pagination/src/Users/UserOptionalListContainer.php +++ b/seed/php-model/pagination/src/Users/UserOptionalListContainer.php @@ -21,15 +21,15 @@ class UserOptionalListContainer extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->users = $values['users'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/pagination/src/Users/UserOptionalListPage.php b/seed/php-model/pagination/src/Users/UserOptionalListPage.php index b4bf80286d2b..c31a312c281b 100644 --- a/seed/php-model/pagination/src/Users/UserOptionalListPage.php +++ b/seed/php-model/pagination/src/Users/UserOptionalListPage.php @@ -27,15 +27,16 @@ class UserOptionalListPage extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->data = $values['data'];$this->next = $values['next'] ?? null; + ) { + $this->data = $values['data']; + $this->next = $values['next'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/pagination/src/Users/UserPage.php b/seed/php-model/pagination/src/Users/UserPage.php index eda8c2482837..352155a8f586 100644 --- a/seed/php-model/pagination/src/Users/UserPage.php +++ b/seed/php-model/pagination/src/Users/UserPage.php @@ -27,15 +27,16 @@ class UserPage extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->data = $values['data'];$this->next = $values['next'] ?? null; + ) { + $this->data = $values['data']; + $this->next = $values['next'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/pagination/src/Users/UsernameContainer.php b/seed/php-model/pagination/src/Users/UsernameContainer.php index fb819c3a1113..a3aac8b35b17 100644 --- a/seed/php-model/pagination/src/Users/UsernameContainer.php +++ b/seed/php-model/pagination/src/Users/UsernameContainer.php @@ -21,15 +21,15 @@ class UsernameContainer extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->results = $values['results']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/pagination/src/Users/WithCursor.php b/seed/php-model/pagination/src/Users/WithCursor.php index a1a53badec77..6fd8d6dbd861 100644 --- a/seed/php-model/pagination/src/Users/WithCursor.php +++ b/seed/php-model/pagination/src/Users/WithCursor.php @@ -20,15 +20,15 @@ class WithCursor extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->cursor = $values['cursor'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/pagination/src/Users/WithPage.php b/seed/php-model/pagination/src/Users/WithPage.php index 8848a89b4047..21e3ab817594 100644 --- a/seed/php-model/pagination/src/Users/WithPage.php +++ b/seed/php-model/pagination/src/Users/WithPage.php @@ -20,15 +20,15 @@ class WithPage extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->page = $values['page'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/pagination/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/pagination/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/pagination/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/pagination/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/pagination/tests/Core/Json/EnumTest.php b/seed/php-model/pagination/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/pagination/tests/Core/Json/EnumTest.php +++ b/seed/php-model/pagination/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/pagination/tests/Core/Json/TraitTest.php b/seed/php-model/pagination/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/pagination/tests/Core/Json/TraitTest.php +++ b/seed/php-model/pagination/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/pagination/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/pagination/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/pagination/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/pagination/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/path-parameters/src/Core/Json/JsonEncoder.php b/seed/php-model/path-parameters/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/path-parameters/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/path-parameters/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/path-parameters/src/Core/Json/JsonProperty.php b/seed/php-model/path-parameters/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/path-parameters/src/Core/Json/JsonProperty.php +++ b/seed/php-model/path-parameters/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/path-parameters/src/Core/Types/ArrayType.php b/seed/php-model/path-parameters/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/path-parameters/src/Core/Types/ArrayType.php +++ b/seed/php-model/path-parameters/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/path-parameters/src/Core/Types/Constant.php b/seed/php-model/path-parameters/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/path-parameters/src/Core/Types/Constant.php +++ b/seed/php-model/path-parameters/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/path-parameters/src/Core/Types/Union.php b/seed/php-model/path-parameters/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/path-parameters/src/Core/Types/Union.php +++ b/seed/php-model/path-parameters/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/path-parameters/src/Organizations/Organization.php b/seed/php-model/path-parameters/src/Organizations/Organization.php index d0b9cbd4f213..39b899473b17 100644 --- a/seed/php-model/path-parameters/src/Organizations/Organization.php +++ b/seed/php-model/path-parameters/src/Organizations/Organization.php @@ -28,15 +28,16 @@ class Organization extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->tags = $values['tags']; + ) { + $this->name = $values['name']; + $this->tags = $values['tags']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/path-parameters/src/User/User.php b/seed/php-model/path-parameters/src/User/User.php index 876319d611e3..6e276ede3a98 100644 --- a/seed/php-model/path-parameters/src/User/User.php +++ b/seed/php-model/path-parameters/src/User/User.php @@ -28,15 +28,16 @@ class User extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->tags = $values['tags']; + ) { + $this->name = $values['name']; + $this->tags = $values['tags']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/path-parameters/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/path-parameters/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/path-parameters/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/path-parameters/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/path-parameters/tests/Core/Json/EnumTest.php b/seed/php-model/path-parameters/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/path-parameters/tests/Core/Json/EnumTest.php +++ b/seed/php-model/path-parameters/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/path-parameters/tests/Core/Json/TraitTest.php b/seed/php-model/path-parameters/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/path-parameters/tests/Core/Json/TraitTest.php +++ b/seed/php-model/path-parameters/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/path-parameters/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/path-parameters/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/path-parameters/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/path-parameters/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/plain-text/src/Core/Json/JsonEncoder.php b/seed/php-model/plain-text/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/plain-text/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/plain-text/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/plain-text/src/Core/Json/JsonProperty.php b/seed/php-model/plain-text/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/plain-text/src/Core/Json/JsonProperty.php +++ b/seed/php-model/plain-text/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/plain-text/src/Core/Types/ArrayType.php b/seed/php-model/plain-text/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/plain-text/src/Core/Types/ArrayType.php +++ b/seed/php-model/plain-text/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/plain-text/src/Core/Types/Constant.php b/seed/php-model/plain-text/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/plain-text/src/Core/Types/Constant.php +++ b/seed/php-model/plain-text/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/plain-text/src/Core/Types/Union.php b/seed/php-model/plain-text/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/plain-text/src/Core/Types/Union.php +++ b/seed/php-model/plain-text/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/plain-text/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/plain-text/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/plain-text/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/plain-text/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/plain-text/tests/Core/Json/EnumTest.php b/seed/php-model/plain-text/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/plain-text/tests/Core/Json/EnumTest.php +++ b/seed/php-model/plain-text/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/plain-text/tests/Core/Json/TraitTest.php b/seed/php-model/plain-text/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/plain-text/tests/Core/Json/TraitTest.php +++ b/seed/php-model/plain-text/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/plain-text/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/plain-text/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/plain-text/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/plain-text/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/property-access/src/Admin.php b/seed/php-model/property-access/src/Admin.php index be1d75c0ea6f..bf922f079cb2 100644 --- a/seed/php-model/property-access/src/Admin.php +++ b/seed/php-model/property-access/src/Admin.php @@ -30,15 +30,19 @@ class Admin extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->email = $values['email'];$this->password = $values['password'];$this->profile = $values['profile'];$this->adminLevel = $values['adminLevel']; + ) { + $this->id = $values['id']; + $this->email = $values['email']; + $this->password = $values['password']; + $this->profile = $values['profile']; + $this->adminLevel = $values['adminLevel']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/property-access/src/Core/Json/JsonEncoder.php b/seed/php-model/property-access/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/property-access/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/property-access/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/property-access/src/Core/Json/JsonProperty.php b/seed/php-model/property-access/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/property-access/src/Core/Json/JsonProperty.php +++ b/seed/php-model/property-access/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/property-access/src/Core/Types/ArrayType.php b/seed/php-model/property-access/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/property-access/src/Core/Types/ArrayType.php +++ b/seed/php-model/property-access/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/property-access/src/Core/Types/Constant.php b/seed/php-model/property-access/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/property-access/src/Core/Types/Constant.php +++ b/seed/php-model/property-access/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/property-access/src/Core/Types/Union.php b/seed/php-model/property-access/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/property-access/src/Core/Types/Union.php +++ b/seed/php-model/property-access/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/property-access/src/Foo.php b/seed/php-model/property-access/src/Foo.php index fd2d0eb6515b..d51b433a1d52 100644 --- a/seed/php-model/property-access/src/Foo.php +++ b/seed/php-model/property-access/src/Foo.php @@ -34,15 +34,17 @@ class Foo extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->normal = $values['normal'];$this->read = $values['read'];$this->write = $values['write']; + ) { + $this->normal = $values['normal']; + $this->read = $values['read']; + $this->write = $values['write']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/property-access/src/Traits/User.php b/seed/php-model/property-access/src/Traits/User.php index 53b26ecc1601..5a73e64e2553 100644 --- a/seed/php-model/property-access/src/Traits/User.php +++ b/seed/php-model/property-access/src/Traits/User.php @@ -13,7 +13,7 @@ * @property string $password * @property UserProfile $profile */ -trait User +trait User { /** * @var string $id The unique identifier for the user. diff --git a/seed/php-model/property-access/src/User.php b/seed/php-model/property-access/src/User.php index 1b609120f676..8272298afd37 100644 --- a/seed/php-model/property-access/src/User.php +++ b/seed/php-model/property-access/src/User.php @@ -44,15 +44,18 @@ class User extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->email = $values['email'];$this->password = $values['password'];$this->profile = $values['profile']; + ) { + $this->id = $values['id']; + $this->email = $values['email']; + $this->password = $values['password']; + $this->profile = $values['profile']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/property-access/src/UserOrAdminDiscriminated.php b/seed/php-model/property-access/src/UserOrAdminDiscriminated.php index 75237d70cde1..93ea143c94b9 100644 --- a/seed/php-model/property-access/src/UserOrAdminDiscriminated.php +++ b/seed/php-model/property-access/src/UserOrAdminDiscriminated.php @@ -64,9 +64,11 @@ class UserOrAdminDiscriminated extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->normal = $values['normal'];$this->foo = $values['foo'];$this->type = $values['type'];$this->value = $values['value']; + ) { + $this->normal = $values['normal']; + $this->foo = $values['foo']; + $this->type = $values['type']; + $this->value = $values['value']; } /** @@ -75,7 +77,8 @@ private function __construct( * @param User $user * @return UserOrAdminDiscriminated */ - public static function user(string $normal, Foo $foo, User $user): UserOrAdminDiscriminated { + public static function user(string $normal, Foo $foo, User $user): UserOrAdminDiscriminated + { return new UserOrAdminDiscriminated([ 'normal' => $normal, 'foo' => $foo, @@ -90,7 +93,8 @@ public static function user(string $normal, Foo $foo, User $user): UserOrAdminDi * @param Admin $admin * @return UserOrAdminDiscriminated */ - public static function admin(string $normal, Foo $foo, Admin $admin): UserOrAdminDiscriminated { + public static function admin(string $normal, Foo $foo, Admin $admin): UserOrAdminDiscriminated + { return new UserOrAdminDiscriminated([ 'normal' => $normal, 'foo' => $foo, @@ -104,7 +108,8 @@ public static function admin(string $normal, Foo $foo, Admin $admin): UserOrAdmi * @param Foo $foo * @return UserOrAdminDiscriminated */ - public static function empty(string $normal, Foo $foo): UserOrAdminDiscriminated { + public static function empty(string $normal, Foo $foo): UserOrAdminDiscriminated + { return new UserOrAdminDiscriminated([ 'normal' => $normal, 'foo' => $foo, @@ -116,68 +121,75 @@ public static function empty(string $normal, Foo $foo): UserOrAdminDiscriminated /** * @return bool */ - public function isUser(): bool { - return $this->value instanceof User&& $this->type === 'user'; + public function isUser(): bool + { + return $this->value instanceof User && $this->type === 'user'; } /** * @return User */ - public function asUser(): User { - if (!($this->value instanceof User&& $this->type === 'user')){ + public function asUser(): User + { + if (!($this->value instanceof User && $this->type === 'user')) { throw new Exception( "Expected user; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isAdmin(): bool { - return $this->value instanceof Admin&& $this->type === 'admin'; + public function isAdmin(): bool + { + return $this->value instanceof Admin && $this->type === 'admin'; } /** * @return Admin */ - public function asAdmin(): Admin { - if (!($this->value instanceof Admin&& $this->type === 'admin')){ + public function asAdmin(): Admin + { + if (!($this->value instanceof Admin && $this->type === 'admin')) { throw new Exception( "Expected admin; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isEmpty(): bool { - return is_null($this->value)&& $this->type === 'empty'; + public function isEmpty(): bool + { + return is_null($this->value) && $this->type === 'empty'; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'user': $value = $this->asUser()->jsonSerialize(); $result = array_merge($value, $result); @@ -191,26 +203,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -219,57 +232,58 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('normal', $data)){ + if (!array_key_exists('normal', $data)) { throw new Exception( "JSON data is missing property 'normal'", ); } - if (!(is_string($data['normal']))){ + if (!(is_string($data['normal']))) { throw new Exception( "Expected property 'normal' in JSON data to be string, instead received " . get_debug_type($data['normal']), ); } $args['normal'] = $data['normal']; - - if (!array_key_exists('foo', $data)){ + + if (!array_key_exists('foo', $data)) { throw new Exception( "JSON data is missing property 'foo'", ); } - if (!($data['foo'] instanceof Foo)){ + if (!($data['foo'] instanceof Foo)) { throw new Exception( "Expected property 'foo' in JSON data to be reference, instead received " . get_debug_type($data['foo']), ); } $args['foo'] = $data['foo']; - - if (!array_key_exists('type', $data)){ + + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'user': $args['value'] = User::jsonDeserialize($data); break; case 'admin': - if (!array_key_exists('admin', $data)){ + if (!array_key_exists('admin', $data)) { throw new Exception( "JSON data is missing property 'admin'", ); } - - if (!(is_array($data['admin']))){ + + if (!(is_array($data['admin']))) { throw new Exception( "Expected property 'admin' in JSON data to be array, instead received " . get_debug_type($data['admin']), ); @@ -284,7 +298,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/property-access/src/UserProfile.php b/seed/php-model/property-access/src/UserProfile.php index 2aa7837ba333..a756afcf654e 100644 --- a/seed/php-model/property-access/src/UserProfile.php +++ b/seed/php-model/property-access/src/UserProfile.php @@ -37,15 +37,17 @@ class UserProfile extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->verification = $values['verification'];$this->ssn = $values['ssn']; + ) { + $this->name = $values['name']; + $this->verification = $values['verification']; + $this->ssn = $values['ssn']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/property-access/src/UserProfileVerification.php b/seed/php-model/property-access/src/UserProfileVerification.php index dabfbfdb294a..b9bbb580178f 100644 --- a/seed/php-model/property-access/src/UserProfileVerification.php +++ b/seed/php-model/property-access/src/UserProfileVerification.php @@ -23,15 +23,15 @@ class UserProfileVerification extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->verified = $values['verified']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/property-access/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/property-access/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/property-access/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/property-access/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/property-access/tests/Core/Json/EnumTest.php b/seed/php-model/property-access/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/property-access/tests/Core/Json/EnumTest.php +++ b/seed/php-model/property-access/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/property-access/tests/Core/Json/TraitTest.php b/seed/php-model/property-access/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/property-access/tests/Core/Json/TraitTest.php +++ b/seed/php-model/property-access/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/property-access/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/property-access/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/property-access/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/property-access/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/public-object/src/Core/Json/JsonEncoder.php b/seed/php-model/public-object/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/public-object/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/public-object/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/public-object/src/Core/Json/JsonProperty.php b/seed/php-model/public-object/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/public-object/src/Core/Json/JsonProperty.php +++ b/seed/php-model/public-object/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/public-object/src/Core/Types/ArrayType.php b/seed/php-model/public-object/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/public-object/src/Core/Types/ArrayType.php +++ b/seed/php-model/public-object/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/public-object/src/Core/Types/Constant.php b/seed/php-model/public-object/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/public-object/src/Core/Types/Constant.php +++ b/seed/php-model/public-object/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/public-object/src/Core/Types/Union.php b/seed/php-model/public-object/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/public-object/src/Core/Types/Union.php +++ b/seed/php-model/public-object/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/public-object/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/public-object/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/public-object/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/public-object/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/public-object/tests/Core/Json/EnumTest.php b/seed/php-model/public-object/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/public-object/tests/Core/Json/EnumTest.php +++ b/seed/php-model/public-object/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/public-object/tests/Core/Json/TraitTest.php b/seed/php-model/public-object/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/public-object/tests/Core/Json/TraitTest.php +++ b/seed/php-model/public-object/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/public-object/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/public-object/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/public-object/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/public-object/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/query-parameters-openapi-as-objects/src/Core/Json/JsonEncoder.php b/seed/php-model/query-parameters-openapi-as-objects/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/query-parameters-openapi-as-objects/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/query-parameters-openapi-as-objects/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/query-parameters-openapi-as-objects/src/Core/Json/JsonProperty.php b/seed/php-model/query-parameters-openapi-as-objects/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/query-parameters-openapi-as-objects/src/Core/Json/JsonProperty.php +++ b/seed/php-model/query-parameters-openapi-as-objects/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/query-parameters-openapi-as-objects/src/Core/Types/ArrayType.php b/seed/php-model/query-parameters-openapi-as-objects/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/query-parameters-openapi-as-objects/src/Core/Types/ArrayType.php +++ b/seed/php-model/query-parameters-openapi-as-objects/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/query-parameters-openapi-as-objects/src/Core/Types/Constant.php b/seed/php-model/query-parameters-openapi-as-objects/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/query-parameters-openapi-as-objects/src/Core/Types/Constant.php +++ b/seed/php-model/query-parameters-openapi-as-objects/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/query-parameters-openapi-as-objects/src/Core/Types/Union.php b/seed/php-model/query-parameters-openapi-as-objects/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/query-parameters-openapi-as-objects/src/Core/Types/Union.php +++ b/seed/php-model/query-parameters-openapi-as-objects/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/query-parameters-openapi-as-objects/src/NestedUser.php b/seed/php-model/query-parameters-openapi-as-objects/src/NestedUser.php index 0379534446fc..14bb73a330bd 100644 --- a/seed/php-model/query-parameters-openapi-as-objects/src/NestedUser.php +++ b/seed/php-model/query-parameters-openapi-as-objects/src/NestedUser.php @@ -27,15 +27,16 @@ class NestedUser extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->name = $values['name'] ?? null;$this->user = $values['user'] ?? null; + ) { + $this->name = $values['name'] ?? null; + $this->user = $values['user'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/query-parameters-openapi-as-objects/src/SearchResponse.php b/seed/php-model/query-parameters-openapi-as-objects/src/SearchResponse.php index ea5bd007630f..26e7a30f8c9e 100644 --- a/seed/php-model/query-parameters-openapi-as-objects/src/SearchResponse.php +++ b/seed/php-model/query-parameters-openapi-as-objects/src/SearchResponse.php @@ -21,15 +21,15 @@ class SearchResponse extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->results = $values['results'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/query-parameters-openapi-as-objects/src/User.php b/seed/php-model/query-parameters-openapi-as-objects/src/User.php index 15f01b5cee94..006563893377 100644 --- a/seed/php-model/query-parameters-openapi-as-objects/src/User.php +++ b/seed/php-model/query-parameters-openapi-as-objects/src/User.php @@ -28,15 +28,16 @@ class User extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->name = $values['name'] ?? null;$this->tags = $values['tags'] ?? null; + ) { + $this->name = $values['name'] ?? null; + $this->tags = $values['tags'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/query-parameters-openapi-as-objects/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/query-parameters-openapi-as-objects/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/query-parameters-openapi-as-objects/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/query-parameters-openapi-as-objects/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/query-parameters-openapi-as-objects/tests/Core/Json/EnumTest.php b/seed/php-model/query-parameters-openapi-as-objects/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/query-parameters-openapi-as-objects/tests/Core/Json/EnumTest.php +++ b/seed/php-model/query-parameters-openapi-as-objects/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/query-parameters-openapi-as-objects/tests/Core/Json/TraitTest.php b/seed/php-model/query-parameters-openapi-as-objects/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/query-parameters-openapi-as-objects/tests/Core/Json/TraitTest.php +++ b/seed/php-model/query-parameters-openapi-as-objects/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/query-parameters-openapi-as-objects/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/query-parameters-openapi-as-objects/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/query-parameters-openapi-as-objects/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/query-parameters-openapi-as-objects/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/query-parameters-openapi/src/Core/Json/JsonEncoder.php b/seed/php-model/query-parameters-openapi/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/query-parameters-openapi/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/query-parameters-openapi/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/query-parameters-openapi/src/Core/Json/JsonProperty.php b/seed/php-model/query-parameters-openapi/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/query-parameters-openapi/src/Core/Json/JsonProperty.php +++ b/seed/php-model/query-parameters-openapi/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/query-parameters-openapi/src/Core/Types/ArrayType.php b/seed/php-model/query-parameters-openapi/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/query-parameters-openapi/src/Core/Types/ArrayType.php +++ b/seed/php-model/query-parameters-openapi/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/query-parameters-openapi/src/Core/Types/Constant.php b/seed/php-model/query-parameters-openapi/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/query-parameters-openapi/src/Core/Types/Constant.php +++ b/seed/php-model/query-parameters-openapi/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/query-parameters-openapi/src/Core/Types/Union.php b/seed/php-model/query-parameters-openapi/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/query-parameters-openapi/src/Core/Types/Union.php +++ b/seed/php-model/query-parameters-openapi/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/query-parameters-openapi/src/NestedUser.php b/seed/php-model/query-parameters-openapi/src/NestedUser.php index 0379534446fc..14bb73a330bd 100644 --- a/seed/php-model/query-parameters-openapi/src/NestedUser.php +++ b/seed/php-model/query-parameters-openapi/src/NestedUser.php @@ -27,15 +27,16 @@ class NestedUser extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->name = $values['name'] ?? null;$this->user = $values['user'] ?? null; + ) { + $this->name = $values['name'] ?? null; + $this->user = $values['user'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/query-parameters-openapi/src/SearchResponse.php b/seed/php-model/query-parameters-openapi/src/SearchResponse.php index ea5bd007630f..26e7a30f8c9e 100644 --- a/seed/php-model/query-parameters-openapi/src/SearchResponse.php +++ b/seed/php-model/query-parameters-openapi/src/SearchResponse.php @@ -21,15 +21,15 @@ class SearchResponse extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->results = $values['results'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/query-parameters-openapi/src/User.php b/seed/php-model/query-parameters-openapi/src/User.php index 15f01b5cee94..006563893377 100644 --- a/seed/php-model/query-parameters-openapi/src/User.php +++ b/seed/php-model/query-parameters-openapi/src/User.php @@ -28,15 +28,16 @@ class User extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->name = $values['name'] ?? null;$this->tags = $values['tags'] ?? null; + ) { + $this->name = $values['name'] ?? null; + $this->tags = $values['tags'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/query-parameters-openapi/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/query-parameters-openapi/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/query-parameters-openapi/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/query-parameters-openapi/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/query-parameters-openapi/tests/Core/Json/EnumTest.php b/seed/php-model/query-parameters-openapi/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/query-parameters-openapi/tests/Core/Json/EnumTest.php +++ b/seed/php-model/query-parameters-openapi/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/query-parameters-openapi/tests/Core/Json/TraitTest.php b/seed/php-model/query-parameters-openapi/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/query-parameters-openapi/tests/Core/Json/TraitTest.php +++ b/seed/php-model/query-parameters-openapi/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/query-parameters-openapi/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/query-parameters-openapi/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/query-parameters-openapi/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/query-parameters-openapi/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/query-parameters/src/Core/Json/JsonEncoder.php b/seed/php-model/query-parameters/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/query-parameters/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/query-parameters/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/query-parameters/src/Core/Json/JsonProperty.php b/seed/php-model/query-parameters/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/query-parameters/src/Core/Json/JsonProperty.php +++ b/seed/php-model/query-parameters/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/query-parameters/src/Core/Types/ArrayType.php b/seed/php-model/query-parameters/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/query-parameters/src/Core/Types/ArrayType.php +++ b/seed/php-model/query-parameters/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/query-parameters/src/Core/Types/Constant.php b/seed/php-model/query-parameters/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/query-parameters/src/Core/Types/Constant.php +++ b/seed/php-model/query-parameters/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/query-parameters/src/Core/Types/Union.php b/seed/php-model/query-parameters/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/query-parameters/src/Core/Types/Union.php +++ b/seed/php-model/query-parameters/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/query-parameters/src/User/NestedUser.php b/seed/php-model/query-parameters/src/User/NestedUser.php index ecc67310297a..c3c670fafcc0 100644 --- a/seed/php-model/query-parameters/src/User/NestedUser.php +++ b/seed/php-model/query-parameters/src/User/NestedUser.php @@ -27,15 +27,16 @@ class NestedUser extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->user = $values['user']; + ) { + $this->name = $values['name']; + $this->user = $values['user']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/query-parameters/src/User/User.php b/seed/php-model/query-parameters/src/User/User.php index 876319d611e3..6e276ede3a98 100644 --- a/seed/php-model/query-parameters/src/User/User.php +++ b/seed/php-model/query-parameters/src/User/User.php @@ -28,15 +28,16 @@ class User extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->tags = $values['tags']; + ) { + $this->name = $values['name']; + $this->tags = $values['tags']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/query-parameters/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/query-parameters/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/query-parameters/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/query-parameters/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/query-parameters/tests/Core/Json/EnumTest.php b/seed/php-model/query-parameters/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/query-parameters/tests/Core/Json/EnumTest.php +++ b/seed/php-model/query-parameters/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/query-parameters/tests/Core/Json/TraitTest.php b/seed/php-model/query-parameters/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/query-parameters/tests/Core/Json/TraitTest.php +++ b/seed/php-model/query-parameters/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/query-parameters/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/query-parameters/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/query-parameters/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/query-parameters/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/request-parameters/src/Core/Json/JsonEncoder.php b/seed/php-model/request-parameters/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/request-parameters/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/request-parameters/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/request-parameters/src/Core/Json/JsonProperty.php b/seed/php-model/request-parameters/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/request-parameters/src/Core/Json/JsonProperty.php +++ b/seed/php-model/request-parameters/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/request-parameters/src/Core/Types/ArrayType.php b/seed/php-model/request-parameters/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/request-parameters/src/Core/Types/ArrayType.php +++ b/seed/php-model/request-parameters/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/request-parameters/src/Core/Types/Constant.php b/seed/php-model/request-parameters/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/request-parameters/src/Core/Types/Constant.php +++ b/seed/php-model/request-parameters/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/request-parameters/src/Core/Types/Union.php b/seed/php-model/request-parameters/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/request-parameters/src/Core/Types/Union.php +++ b/seed/php-model/request-parameters/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/request-parameters/src/User/CreateUsernameBody.php b/seed/php-model/request-parameters/src/User/CreateUsernameBody.php index 7e2e42eda0c8..26cde7d0bdc9 100644 --- a/seed/php-model/request-parameters/src/User/CreateUsernameBody.php +++ b/seed/php-model/request-parameters/src/User/CreateUsernameBody.php @@ -34,15 +34,17 @@ class CreateUsernameBody extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->username = $values['username'];$this->password = $values['password'];$this->name = $values['name']; + ) { + $this->username = $values['username']; + $this->password = $values['password']; + $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/request-parameters/src/User/CreateUsernameBodyOptionalProperties.php b/seed/php-model/request-parameters/src/User/CreateUsernameBodyOptionalProperties.php index 98b45ef52e83..9d237f30ed07 100644 --- a/seed/php-model/request-parameters/src/User/CreateUsernameBodyOptionalProperties.php +++ b/seed/php-model/request-parameters/src/User/CreateUsernameBodyOptionalProperties.php @@ -34,15 +34,17 @@ class CreateUsernameBodyOptionalProperties extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->username = $values['username'] ?? null;$this->password = $values['password'] ?? null;$this->name = $values['name'] ?? null; + ) { + $this->username = $values['username'] ?? null; + $this->password = $values['password'] ?? null; + $this->name = $values['name'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/request-parameters/src/User/NestedUser.php b/seed/php-model/request-parameters/src/User/NestedUser.php index ecc67310297a..c3c670fafcc0 100644 --- a/seed/php-model/request-parameters/src/User/NestedUser.php +++ b/seed/php-model/request-parameters/src/User/NestedUser.php @@ -27,15 +27,16 @@ class NestedUser extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->user = $values['user']; + ) { + $this->name = $values['name']; + $this->user = $values['user']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/request-parameters/src/User/User.php b/seed/php-model/request-parameters/src/User/User.php index 876319d611e3..6e276ede3a98 100644 --- a/seed/php-model/request-parameters/src/User/User.php +++ b/seed/php-model/request-parameters/src/User/User.php @@ -28,15 +28,16 @@ class User extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->tags = $values['tags']; + ) { + $this->name = $values['name']; + $this->tags = $values['tags']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/request-parameters/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/request-parameters/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/request-parameters/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/request-parameters/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/request-parameters/tests/Core/Json/EnumTest.php b/seed/php-model/request-parameters/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/request-parameters/tests/Core/Json/EnumTest.php +++ b/seed/php-model/request-parameters/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/request-parameters/tests/Core/Json/TraitTest.php b/seed/php-model/request-parameters/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/request-parameters/tests/Core/Json/TraitTest.php +++ b/seed/php-model/request-parameters/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/request-parameters/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/request-parameters/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/request-parameters/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/request-parameters/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/required-nullable/src/Core/Json/JsonEncoder.php b/seed/php-model/required-nullable/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/required-nullable/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/required-nullable/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/required-nullable/src/Core/Json/JsonProperty.php b/seed/php-model/required-nullable/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/required-nullable/src/Core/Json/JsonProperty.php +++ b/seed/php-model/required-nullable/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/required-nullable/src/Core/Types/ArrayType.php b/seed/php-model/required-nullable/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/required-nullable/src/Core/Types/ArrayType.php +++ b/seed/php-model/required-nullable/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/required-nullable/src/Core/Types/Constant.php b/seed/php-model/required-nullable/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/required-nullable/src/Core/Types/Constant.php +++ b/seed/php-model/required-nullable/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/required-nullable/src/Core/Types/Union.php b/seed/php-model/required-nullable/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/required-nullable/src/Core/Types/Union.php +++ b/seed/php-model/required-nullable/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/required-nullable/src/Foo.php b/seed/php-model/required-nullable/src/Foo.php index 5233d9fcb1fa..501788e8d351 100644 --- a/seed/php-model/required-nullable/src/Foo.php +++ b/seed/php-model/required-nullable/src/Foo.php @@ -41,15 +41,18 @@ class Foo extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->bar = $values['bar'] ?? null;$this->nullableBar = $values['nullableBar'] ?? null;$this->nullableRequiredBar = $values['nullableRequiredBar'] ?? null;$this->requiredBar = $values['requiredBar']; + ) { + $this->bar = $values['bar'] ?? null; + $this->nullableBar = $values['nullableBar'] ?? null; + $this->nullableRequiredBar = $values['nullableRequiredBar'] ?? null; + $this->requiredBar = $values['requiredBar']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/required-nullable/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/required-nullable/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/required-nullable/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/required-nullable/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/required-nullable/tests/Core/Json/EnumTest.php b/seed/php-model/required-nullable/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/required-nullable/tests/Core/Json/EnumTest.php +++ b/seed/php-model/required-nullable/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/required-nullable/tests/Core/Json/TraitTest.php b/seed/php-model/required-nullable/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/required-nullable/tests/Core/Json/TraitTest.php +++ b/seed/php-model/required-nullable/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/required-nullable/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/required-nullable/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/required-nullable/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/required-nullable/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/reserved-keywords/src/Core/Json/JsonEncoder.php b/seed/php-model/reserved-keywords/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/reserved-keywords/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/reserved-keywords/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/reserved-keywords/src/Core/Json/JsonProperty.php b/seed/php-model/reserved-keywords/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/reserved-keywords/src/Core/Json/JsonProperty.php +++ b/seed/php-model/reserved-keywords/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/reserved-keywords/src/Core/Types/ArrayType.php b/seed/php-model/reserved-keywords/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/reserved-keywords/src/Core/Types/ArrayType.php +++ b/seed/php-model/reserved-keywords/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/reserved-keywords/src/Core/Types/Constant.php b/seed/php-model/reserved-keywords/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/reserved-keywords/src/Core/Types/Constant.php +++ b/seed/php-model/reserved-keywords/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/reserved-keywords/src/Core/Types/Union.php b/seed/php-model/reserved-keywords/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/reserved-keywords/src/Core/Types/Union.php +++ b/seed/php-model/reserved-keywords/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/reserved-keywords/src/Package/Package.php b/seed/php-model/reserved-keywords/src/Package/Package.php index 0a8dd0070cea..d2045e159555 100644 --- a/seed/php-model/reserved-keywords/src/Package/Package.php +++ b/seed/php-model/reserved-keywords/src/Package/Package.php @@ -20,15 +20,15 @@ class Package extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/reserved-keywords/src/Package/Record.php b/seed/php-model/reserved-keywords/src/Package/Record.php index cb35aac50f67..4d8e52ae7f95 100644 --- a/seed/php-model/reserved-keywords/src/Package/Record.php +++ b/seed/php-model/reserved-keywords/src/Package/Record.php @@ -28,15 +28,16 @@ class Record extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->foo = $values['foo'];$this->_3D = $values['_3D']; + ) { + $this->foo = $values['foo']; + $this->_3D = $values['_3D']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/reserved-keywords/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/reserved-keywords/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/reserved-keywords/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/reserved-keywords/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/reserved-keywords/tests/Core/Json/EnumTest.php b/seed/php-model/reserved-keywords/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/reserved-keywords/tests/Core/Json/EnumTest.php +++ b/seed/php-model/reserved-keywords/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/reserved-keywords/tests/Core/Json/TraitTest.php b/seed/php-model/reserved-keywords/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/reserved-keywords/tests/Core/Json/TraitTest.php +++ b/seed/php-model/reserved-keywords/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/reserved-keywords/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/reserved-keywords/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/reserved-keywords/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/reserved-keywords/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/response-property/src/Core/Json/JsonEncoder.php b/seed/php-model/response-property/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/response-property/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/response-property/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/response-property/src/Core/Json/JsonProperty.php b/seed/php-model/response-property/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/response-property/src/Core/Json/JsonProperty.php +++ b/seed/php-model/response-property/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/response-property/src/Core/Types/ArrayType.php b/seed/php-model/response-property/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/response-property/src/Core/Types/ArrayType.php +++ b/seed/php-model/response-property/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/response-property/src/Core/Types/Constant.php b/seed/php-model/response-property/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/response-property/src/Core/Types/Constant.php +++ b/seed/php-model/response-property/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/response-property/src/Core/Types/Union.php b/seed/php-model/response-property/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/response-property/src/Core/Types/Union.php +++ b/seed/php-model/response-property/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/response-property/src/Service/Movie.php b/seed/php-model/response-property/src/Service/Movie.php index 9eabb9525429..3c8bf392d6bf 100644 --- a/seed/php-model/response-property/src/Service/Movie.php +++ b/seed/php-model/response-property/src/Service/Movie.php @@ -27,15 +27,16 @@ class Movie extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->name = $values['name']; + ) { + $this->id = $values['id']; + $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/response-property/src/Service/Response.php b/seed/php-model/response-property/src/Service/Response.php index f51a028b0100..db017f8e30f9 100644 --- a/seed/php-model/response-property/src/Service/Response.php +++ b/seed/php-model/response-property/src/Service/Response.php @@ -9,7 +9,8 @@ class Response extends JsonSerializableType { - use WithMetadata,WithDocs; + use WithMetadata; + use WithDocs; /** * @var Movie $data @@ -26,15 +27,17 @@ class Response extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->metadata = $values['metadata'];$this->docs = $values['docs'];$this->data = $values['data']; + ) { + $this->metadata = $values['metadata']; + $this->docs = $values['docs']; + $this->data = $values['data']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/response-property/src/Service/Traits/WithDocs.php b/seed/php-model/response-property/src/Service/Traits/WithDocs.php index 55675e093259..2fcb1eed8168 100644 --- a/seed/php-model/response-property/src/Service/Traits/WithDocs.php +++ b/seed/php-model/response-property/src/Service/Traits/WithDocs.php @@ -7,7 +7,7 @@ /** * @property string $docs */ -trait WithDocs +trait WithDocs { /** * @var string $docs diff --git a/seed/php-model/response-property/src/Service/WithDocs.php b/seed/php-model/response-property/src/Service/WithDocs.php index 8c125eb74e2c..3aa751c0124e 100644 --- a/seed/php-model/response-property/src/Service/WithDocs.php +++ b/seed/php-model/response-property/src/Service/WithDocs.php @@ -20,15 +20,15 @@ class WithDocs extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->docs = $values['docs']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/response-property/src/StringResponse.php b/seed/php-model/response-property/src/StringResponse.php index baa68647917f..562468578420 100644 --- a/seed/php-model/response-property/src/StringResponse.php +++ b/seed/php-model/response-property/src/StringResponse.php @@ -20,15 +20,15 @@ class StringResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->data = $values['data']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/response-property/src/Traits/WithMetadata.php b/seed/php-model/response-property/src/Traits/WithMetadata.php index a6f0f4de6fe5..944006bda0fc 100644 --- a/seed/php-model/response-property/src/Traits/WithMetadata.php +++ b/seed/php-model/response-property/src/Traits/WithMetadata.php @@ -8,7 +8,7 @@ /** * @property array $metadata */ -trait WithMetadata +trait WithMetadata { /** * @var array $metadata diff --git a/seed/php-model/response-property/src/WithMetadata.php b/seed/php-model/response-property/src/WithMetadata.php index 1d56c4a9df5d..5388434aabe0 100644 --- a/seed/php-model/response-property/src/WithMetadata.php +++ b/seed/php-model/response-property/src/WithMetadata.php @@ -21,15 +21,15 @@ class WithMetadata extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->metadata = $values['metadata']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/response-property/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/response-property/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/response-property/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/response-property/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/response-property/tests/Core/Json/EnumTest.php b/seed/php-model/response-property/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/response-property/tests/Core/Json/EnumTest.php +++ b/seed/php-model/response-property/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/response-property/tests/Core/Json/TraitTest.php b/seed/php-model/response-property/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/response-property/tests/Core/Json/TraitTest.php +++ b/seed/php-model/response-property/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/response-property/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/response-property/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/response-property/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/response-property/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/server-sent-event-examples/src/Completions/StreamedCompletion.php b/seed/php-model/server-sent-event-examples/src/Completions/StreamedCompletion.php index 96ddb3b86efe..fe4c758c4c5a 100644 --- a/seed/php-model/server-sent-event-examples/src/Completions/StreamedCompletion.php +++ b/seed/php-model/server-sent-event-examples/src/Completions/StreamedCompletion.php @@ -27,15 +27,16 @@ class StreamedCompletion extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->delta = $values['delta'];$this->tokens = $values['tokens'] ?? null; + ) { + $this->delta = $values['delta']; + $this->tokens = $values['tokens'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/server-sent-event-examples/src/Core/Json/JsonEncoder.php b/seed/php-model/server-sent-event-examples/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/server-sent-event-examples/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/server-sent-event-examples/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/server-sent-event-examples/src/Core/Json/JsonProperty.php b/seed/php-model/server-sent-event-examples/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/server-sent-event-examples/src/Core/Json/JsonProperty.php +++ b/seed/php-model/server-sent-event-examples/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/server-sent-event-examples/src/Core/Types/ArrayType.php b/seed/php-model/server-sent-event-examples/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/server-sent-event-examples/src/Core/Types/ArrayType.php +++ b/seed/php-model/server-sent-event-examples/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/server-sent-event-examples/src/Core/Types/Constant.php b/seed/php-model/server-sent-event-examples/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/server-sent-event-examples/src/Core/Types/Constant.php +++ b/seed/php-model/server-sent-event-examples/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/server-sent-event-examples/src/Core/Types/Union.php b/seed/php-model/server-sent-event-examples/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/server-sent-event-examples/src/Core/Types/Union.php +++ b/seed/php-model/server-sent-event-examples/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/server-sent-event-examples/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/server-sent-event-examples/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/server-sent-event-examples/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/server-sent-event-examples/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/server-sent-event-examples/tests/Core/Json/EnumTest.php b/seed/php-model/server-sent-event-examples/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/server-sent-event-examples/tests/Core/Json/EnumTest.php +++ b/seed/php-model/server-sent-event-examples/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/server-sent-event-examples/tests/Core/Json/TraitTest.php b/seed/php-model/server-sent-event-examples/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/server-sent-event-examples/tests/Core/Json/TraitTest.php +++ b/seed/php-model/server-sent-event-examples/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/server-sent-event-examples/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/server-sent-event-examples/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/server-sent-event-examples/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/server-sent-event-examples/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/server-sent-events/src/Completions/StreamedCompletion.php b/seed/php-model/server-sent-events/src/Completions/StreamedCompletion.php index 96ddb3b86efe..fe4c758c4c5a 100644 --- a/seed/php-model/server-sent-events/src/Completions/StreamedCompletion.php +++ b/seed/php-model/server-sent-events/src/Completions/StreamedCompletion.php @@ -27,15 +27,16 @@ class StreamedCompletion extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->delta = $values['delta'];$this->tokens = $values['tokens'] ?? null; + ) { + $this->delta = $values['delta']; + $this->tokens = $values['tokens'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/server-sent-events/src/Core/Json/JsonEncoder.php b/seed/php-model/server-sent-events/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/server-sent-events/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/server-sent-events/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/server-sent-events/src/Core/Json/JsonProperty.php b/seed/php-model/server-sent-events/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/server-sent-events/src/Core/Json/JsonProperty.php +++ b/seed/php-model/server-sent-events/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/server-sent-events/src/Core/Types/ArrayType.php b/seed/php-model/server-sent-events/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/server-sent-events/src/Core/Types/ArrayType.php +++ b/seed/php-model/server-sent-events/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/server-sent-events/src/Core/Types/Constant.php b/seed/php-model/server-sent-events/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/server-sent-events/src/Core/Types/Constant.php +++ b/seed/php-model/server-sent-events/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/server-sent-events/src/Core/Types/Union.php b/seed/php-model/server-sent-events/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/server-sent-events/src/Core/Types/Union.php +++ b/seed/php-model/server-sent-events/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/server-sent-events/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/server-sent-events/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/server-sent-events/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/server-sent-events/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/server-sent-events/tests/Core/Json/EnumTest.php b/seed/php-model/server-sent-events/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/server-sent-events/tests/Core/Json/EnumTest.php +++ b/seed/php-model/server-sent-events/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/server-sent-events/tests/Core/Json/TraitTest.php b/seed/php-model/server-sent-events/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/server-sent-events/tests/Core/Json/TraitTest.php +++ b/seed/php-model/server-sent-events/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/server-sent-events/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/server-sent-events/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/server-sent-events/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/server-sent-events/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/simple-api/src/Core/Json/JsonEncoder.php b/seed/php-model/simple-api/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/simple-api/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/simple-api/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/simple-api/src/Core/Json/JsonProperty.php b/seed/php-model/simple-api/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/simple-api/src/Core/Json/JsonProperty.php +++ b/seed/php-model/simple-api/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/simple-api/src/Core/Types/ArrayType.php b/seed/php-model/simple-api/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/simple-api/src/Core/Types/ArrayType.php +++ b/seed/php-model/simple-api/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/simple-api/src/Core/Types/Constant.php b/seed/php-model/simple-api/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/simple-api/src/Core/Types/Constant.php +++ b/seed/php-model/simple-api/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/simple-api/src/Core/Types/Union.php b/seed/php-model/simple-api/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/simple-api/src/Core/Types/Union.php +++ b/seed/php-model/simple-api/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/simple-api/src/User/User.php b/seed/php-model/simple-api/src/User/User.php index b5b1146efc1a..aa128b578d9e 100644 --- a/seed/php-model/simple-api/src/User/User.php +++ b/seed/php-model/simple-api/src/User/User.php @@ -34,15 +34,17 @@ class User extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->name = $values['name'];$this->email = $values['email']; + ) { + $this->id = $values['id']; + $this->name = $values['name']; + $this->email = $values['email']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/simple-api/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/simple-api/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/simple-api/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/simple-api/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/simple-api/tests/Core/Json/EnumTest.php b/seed/php-model/simple-api/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/simple-api/tests/Core/Json/EnumTest.php +++ b/seed/php-model/simple-api/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/simple-api/tests/Core/Json/TraitTest.php b/seed/php-model/simple-api/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/simple-api/tests/Core/Json/TraitTest.php +++ b/seed/php-model/simple-api/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/simple-api/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/simple-api/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/simple-api/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/simple-api/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/simple-fhir/src/Account.php b/seed/php-model/simple-fhir/src/Account.php index 4a33970d5411..b4c38f539787 100644 --- a/seed/php-model/simple-fhir/src/Account.php +++ b/seed/php-model/simple-fhir/src/Account.php @@ -52,15 +52,21 @@ class Account extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->relatedResources = $values['relatedResources'];$this->memo = $values['memo'];$this->resourceType = $values['resourceType'];$this->name = $values['name'];$this->patient = $values['patient'] ?? null;$this->practitioner = $values['practitioner'] ?? null; + ) { + $this->id = $values['id']; + $this->relatedResources = $values['relatedResources']; + $this->memo = $values['memo']; + $this->resourceType = $values['resourceType']; + $this->name = $values['name']; + $this->patient = $values['patient'] ?? null; + $this->practitioner = $values['practitioner'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/simple-fhir/src/BaseResource.php b/seed/php-model/simple-fhir/src/BaseResource.php index ff304a43bcbd..d79bcda34b1e 100644 --- a/seed/php-model/simple-fhir/src/BaseResource.php +++ b/seed/php-model/simple-fhir/src/BaseResource.php @@ -46,15 +46,17 @@ class BaseResource extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->relatedResources = $values['relatedResources'];$this->memo = $values['memo']; + ) { + $this->id = $values['id']; + $this->relatedResources = $values['relatedResources']; + $this->memo = $values['memo']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/simple-fhir/src/Core/Json/JsonEncoder.php b/seed/php-model/simple-fhir/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/simple-fhir/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/simple-fhir/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/simple-fhir/src/Core/Json/JsonProperty.php b/seed/php-model/simple-fhir/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/simple-fhir/src/Core/Json/JsonProperty.php +++ b/seed/php-model/simple-fhir/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/simple-fhir/src/Core/Types/ArrayType.php b/seed/php-model/simple-fhir/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/simple-fhir/src/Core/Types/ArrayType.php +++ b/seed/php-model/simple-fhir/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/simple-fhir/src/Core/Types/Constant.php b/seed/php-model/simple-fhir/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/simple-fhir/src/Core/Types/Constant.php +++ b/seed/php-model/simple-fhir/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/simple-fhir/src/Core/Types/Union.php b/seed/php-model/simple-fhir/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/simple-fhir/src/Core/Types/Union.php +++ b/seed/php-model/simple-fhir/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/simple-fhir/src/Memo.php b/seed/php-model/simple-fhir/src/Memo.php index 1bb808a925cc..729915dec98c 100644 --- a/seed/php-model/simple-fhir/src/Memo.php +++ b/seed/php-model/simple-fhir/src/Memo.php @@ -27,15 +27,16 @@ class Memo extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->description = $values['description'];$this->account = $values['account'] ?? null; + ) { + $this->description = $values['description']; + $this->account = $values['account'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/simple-fhir/src/Patient.php b/seed/php-model/simple-fhir/src/Patient.php index 2a84ad41eab3..8b626218d642 100644 --- a/seed/php-model/simple-fhir/src/Patient.php +++ b/seed/php-model/simple-fhir/src/Patient.php @@ -46,15 +46,20 @@ class Patient extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->relatedResources = $values['relatedResources'];$this->memo = $values['memo'];$this->resourceType = $values['resourceType'];$this->name = $values['name'];$this->scripts = $values['scripts']; + ) { + $this->id = $values['id']; + $this->relatedResources = $values['relatedResources']; + $this->memo = $values['memo']; + $this->resourceType = $values['resourceType']; + $this->name = $values['name']; + $this->scripts = $values['scripts']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/simple-fhir/src/Practitioner.php b/seed/php-model/simple-fhir/src/Practitioner.php index 4632dcedcedf..d1e1528baf60 100644 --- a/seed/php-model/simple-fhir/src/Practitioner.php +++ b/seed/php-model/simple-fhir/src/Practitioner.php @@ -38,15 +38,19 @@ class Practitioner extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->relatedResources = $values['relatedResources'];$this->memo = $values['memo'];$this->resourceType = $values['resourceType'];$this->name = $values['name']; + ) { + $this->id = $values['id']; + $this->relatedResources = $values['relatedResources']; + $this->memo = $values['memo']; + $this->resourceType = $values['resourceType']; + $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/simple-fhir/src/Script.php b/seed/php-model/simple-fhir/src/Script.php index c90fdd22ac9c..b6c5ad9b3936 100644 --- a/seed/php-model/simple-fhir/src/Script.php +++ b/seed/php-model/simple-fhir/src/Script.php @@ -38,15 +38,19 @@ class Script extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->relatedResources = $values['relatedResources'];$this->memo = $values['memo'];$this->resourceType = $values['resourceType'];$this->name = $values['name']; + ) { + $this->id = $values['id']; + $this->relatedResources = $values['relatedResources']; + $this->memo = $values['memo']; + $this->resourceType = $values['resourceType']; + $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/simple-fhir/src/Traits/BaseResource.php b/seed/php-model/simple-fhir/src/Traits/BaseResource.php index 6784c418f6b3..cce2e3e4a428 100644 --- a/seed/php-model/simple-fhir/src/Traits/BaseResource.php +++ b/seed/php-model/simple-fhir/src/Traits/BaseResource.php @@ -21,7 +21,7 @@ * )> $relatedResources * @property Memo $memo */ -trait BaseResource +trait BaseResource { /** * @var string $id diff --git a/seed/php-model/simple-fhir/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/simple-fhir/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/simple-fhir/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/simple-fhir/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/simple-fhir/tests/Core/Json/EnumTest.php b/seed/php-model/simple-fhir/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/simple-fhir/tests/Core/Json/EnumTest.php +++ b/seed/php-model/simple-fhir/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/simple-fhir/tests/Core/Json/TraitTest.php b/seed/php-model/simple-fhir/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/simple-fhir/tests/Core/Json/TraitTest.php +++ b/seed/php-model/simple-fhir/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/simple-fhir/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/simple-fhir/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/simple-fhir/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/simple-fhir/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/single-url-environment-default/src/Core/Json/JsonEncoder.php b/seed/php-model/single-url-environment-default/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/single-url-environment-default/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/single-url-environment-default/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/single-url-environment-default/src/Core/Json/JsonProperty.php b/seed/php-model/single-url-environment-default/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/single-url-environment-default/src/Core/Json/JsonProperty.php +++ b/seed/php-model/single-url-environment-default/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/single-url-environment-default/src/Core/Types/ArrayType.php b/seed/php-model/single-url-environment-default/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/single-url-environment-default/src/Core/Types/ArrayType.php +++ b/seed/php-model/single-url-environment-default/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/single-url-environment-default/src/Core/Types/Constant.php b/seed/php-model/single-url-environment-default/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/single-url-environment-default/src/Core/Types/Constant.php +++ b/seed/php-model/single-url-environment-default/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/single-url-environment-default/src/Core/Types/Union.php b/seed/php-model/single-url-environment-default/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/single-url-environment-default/src/Core/Types/Union.php +++ b/seed/php-model/single-url-environment-default/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/single-url-environment-default/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/single-url-environment-default/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/single-url-environment-default/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/single-url-environment-default/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/single-url-environment-default/tests/Core/Json/EnumTest.php b/seed/php-model/single-url-environment-default/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/single-url-environment-default/tests/Core/Json/EnumTest.php +++ b/seed/php-model/single-url-environment-default/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/single-url-environment-default/tests/Core/Json/TraitTest.php b/seed/php-model/single-url-environment-default/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/single-url-environment-default/tests/Core/Json/TraitTest.php +++ b/seed/php-model/single-url-environment-default/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/single-url-environment-default/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/single-url-environment-default/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/single-url-environment-default/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/single-url-environment-default/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/single-url-environment-no-default/src/Core/Json/JsonEncoder.php b/seed/php-model/single-url-environment-no-default/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/single-url-environment-no-default/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/single-url-environment-no-default/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/single-url-environment-no-default/src/Core/Json/JsonProperty.php b/seed/php-model/single-url-environment-no-default/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/single-url-environment-no-default/src/Core/Json/JsonProperty.php +++ b/seed/php-model/single-url-environment-no-default/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/single-url-environment-no-default/src/Core/Types/ArrayType.php b/seed/php-model/single-url-environment-no-default/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/single-url-environment-no-default/src/Core/Types/ArrayType.php +++ b/seed/php-model/single-url-environment-no-default/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/single-url-environment-no-default/src/Core/Types/Constant.php b/seed/php-model/single-url-environment-no-default/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/single-url-environment-no-default/src/Core/Types/Constant.php +++ b/seed/php-model/single-url-environment-no-default/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/single-url-environment-no-default/src/Core/Types/Union.php b/seed/php-model/single-url-environment-no-default/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/single-url-environment-no-default/src/Core/Types/Union.php +++ b/seed/php-model/single-url-environment-no-default/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/single-url-environment-no-default/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/single-url-environment-no-default/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/single-url-environment-no-default/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/single-url-environment-no-default/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/single-url-environment-no-default/tests/Core/Json/EnumTest.php b/seed/php-model/single-url-environment-no-default/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/single-url-environment-no-default/tests/Core/Json/EnumTest.php +++ b/seed/php-model/single-url-environment-no-default/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/single-url-environment-no-default/tests/Core/Json/TraitTest.php b/seed/php-model/single-url-environment-no-default/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/single-url-environment-no-default/tests/Core/Json/TraitTest.php +++ b/seed/php-model/single-url-environment-no-default/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/single-url-environment-no-default/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/single-url-environment-no-default/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/single-url-environment-no-default/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/single-url-environment-no-default/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/streaming-parameter/src/Core/Json/JsonEncoder.php b/seed/php-model/streaming-parameter/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/streaming-parameter/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/streaming-parameter/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/streaming-parameter/src/Core/Json/JsonProperty.php b/seed/php-model/streaming-parameter/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/streaming-parameter/src/Core/Json/JsonProperty.php +++ b/seed/php-model/streaming-parameter/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/streaming-parameter/src/Core/Types/ArrayType.php b/seed/php-model/streaming-parameter/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/streaming-parameter/src/Core/Types/ArrayType.php +++ b/seed/php-model/streaming-parameter/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/streaming-parameter/src/Core/Types/Constant.php b/seed/php-model/streaming-parameter/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/streaming-parameter/src/Core/Types/Constant.php +++ b/seed/php-model/streaming-parameter/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/streaming-parameter/src/Core/Types/Union.php b/seed/php-model/streaming-parameter/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/streaming-parameter/src/Core/Types/Union.php +++ b/seed/php-model/streaming-parameter/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/streaming-parameter/src/Dummy/RegularResponse.php b/seed/php-model/streaming-parameter/src/Dummy/RegularResponse.php index 22e99c7e0078..12542ca99fab 100644 --- a/seed/php-model/streaming-parameter/src/Dummy/RegularResponse.php +++ b/seed/php-model/streaming-parameter/src/Dummy/RegularResponse.php @@ -27,15 +27,16 @@ class RegularResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->name = $values['name'] ?? null; + ) { + $this->id = $values['id']; + $this->name = $values['name'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/streaming-parameter/src/Dummy/StreamResponse.php b/seed/php-model/streaming-parameter/src/Dummy/StreamResponse.php index 4f2293b6986a..03324aecb19c 100644 --- a/seed/php-model/streaming-parameter/src/Dummy/StreamResponse.php +++ b/seed/php-model/streaming-parameter/src/Dummy/StreamResponse.php @@ -27,15 +27,16 @@ class StreamResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->name = $values['name'] ?? null; + ) { + $this->id = $values['id']; + $this->name = $values['name'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/streaming-parameter/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/streaming-parameter/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/streaming-parameter/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/streaming-parameter/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/streaming-parameter/tests/Core/Json/EnumTest.php b/seed/php-model/streaming-parameter/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/streaming-parameter/tests/Core/Json/EnumTest.php +++ b/seed/php-model/streaming-parameter/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/streaming-parameter/tests/Core/Json/TraitTest.php b/seed/php-model/streaming-parameter/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/streaming-parameter/tests/Core/Json/TraitTest.php +++ b/seed/php-model/streaming-parameter/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/streaming-parameter/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/streaming-parameter/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/streaming-parameter/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/streaming-parameter/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/streaming/src/Core/Json/JsonEncoder.php b/seed/php-model/streaming/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/streaming/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/streaming/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/streaming/src/Core/Json/JsonProperty.php b/seed/php-model/streaming/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/streaming/src/Core/Json/JsonProperty.php +++ b/seed/php-model/streaming/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/streaming/src/Core/Types/ArrayType.php b/seed/php-model/streaming/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/streaming/src/Core/Types/ArrayType.php +++ b/seed/php-model/streaming/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/streaming/src/Core/Types/Constant.php b/seed/php-model/streaming/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/streaming/src/Core/Types/Constant.php +++ b/seed/php-model/streaming/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/streaming/src/Core/Types/Union.php b/seed/php-model/streaming/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/streaming/src/Core/Types/Union.php +++ b/seed/php-model/streaming/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/streaming/src/Dummy/StreamResponse.php b/seed/php-model/streaming/src/Dummy/StreamResponse.php index 4f2293b6986a..03324aecb19c 100644 --- a/seed/php-model/streaming/src/Dummy/StreamResponse.php +++ b/seed/php-model/streaming/src/Dummy/StreamResponse.php @@ -27,15 +27,16 @@ class StreamResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->name = $values['name'] ?? null; + ) { + $this->id = $values['id']; + $this->name = $values['name'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/streaming/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/streaming/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/streaming/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/streaming/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/streaming/tests/Core/Json/EnumTest.php b/seed/php-model/streaming/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/streaming/tests/Core/Json/EnumTest.php +++ b/seed/php-model/streaming/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/streaming/tests/Core/Json/TraitTest.php b/seed/php-model/streaming/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/streaming/tests/Core/Json/TraitTest.php +++ b/seed/php-model/streaming/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/streaming/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/streaming/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/streaming/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/streaming/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/trace/src/Admin/Test.php b/seed/php-model/trace/src/Admin/Test.php index f6ee2c6e9fe2..a38dd1e385c4 100644 --- a/seed/php-model/trace/src/Admin/Test.php +++ b/seed/php-model/trace/src/Admin/Test.php @@ -40,16 +40,17 @@ class Test extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param bool $and * @return Test */ - public static function and(bool $and): Test { + public static function and(bool $and): Test + { return new Test([ 'type' => 'and', 'value' => $and, @@ -60,7 +61,8 @@ public static function and(bool $and): Test { * @param bool $or * @return Test */ - public static function or(bool $or): Test { + public static function or(bool $or): Test + { return new Test([ 'type' => 'or', 'value' => $or, @@ -70,61 +72,67 @@ public static function or(bool $or): Test { /** * @return bool */ - public function isAnd_(): bool { - return is_bool($this->value)&& $this->type === 'and'; + public function isAnd_(): bool + { + return is_bool($this->value) && $this->type === 'and'; } /** * @return bool */ - public function asAnd_(): bool { - if (!(is_bool($this->value)&& $this->type === 'and')){ + public function asAnd_(): bool + { + if (!(is_bool($this->value) && $this->type === 'and')) { throw new Exception( "Expected and; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isOr_(): bool { - return is_bool($this->value)&& $this->type === 'or'; + public function isOr_(): bool + { + return is_bool($this->value) && $this->type === 'or'; } /** * @return bool */ - public function asOr_(): bool { - if (!(is_bool($this->value)&& $this->type === 'or')){ + public function asOr_(): bool + { + if (!(is_bool($this->value) && $this->type === 'or')) { throw new Exception( "Expected or; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'and': $value = $this->value; $result['and'] = $value; @@ -135,26 +143,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -163,38 +172,39 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'and': - if (!array_key_exists('and', $data)){ + if (!array_key_exists('and', $data)) { throw new Exception( "JSON data is missing property 'and'", ); } - + $args['value'] = $data['and']; break; case 'or': - if (!array_key_exists('or', $data)){ + if (!array_key_exists('or', $data)) { throw new Exception( "JSON data is missing property 'or'", ); } - + $args['value'] = $data['or']; break; case '_unknown': @@ -202,7 +212,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/trace/src/Commons/BinaryTreeNodeAndTreeValue.php b/seed/php-model/trace/src/Commons/BinaryTreeNodeAndTreeValue.php index 61c1ddc01d94..19ab6958abc2 100644 --- a/seed/php-model/trace/src/Commons/BinaryTreeNodeAndTreeValue.php +++ b/seed/php-model/trace/src/Commons/BinaryTreeNodeAndTreeValue.php @@ -27,15 +27,16 @@ class BinaryTreeNodeAndTreeValue extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->nodeId = $values['nodeId'];$this->fullTree = $values['fullTree']; + ) { + $this->nodeId = $values['nodeId']; + $this->fullTree = $values['fullTree']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Commons/BinaryTreeNodeValue.php b/seed/php-model/trace/src/Commons/BinaryTreeNodeValue.php index 52771a50a2d5..a13c6775796c 100644 --- a/seed/php-model/trace/src/Commons/BinaryTreeNodeValue.php +++ b/seed/php-model/trace/src/Commons/BinaryTreeNodeValue.php @@ -41,15 +41,18 @@ class BinaryTreeNodeValue extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->nodeId = $values['nodeId'];$this->val = $values['val'];$this->right = $values['right'] ?? null;$this->left = $values['left'] ?? null; + ) { + $this->nodeId = $values['nodeId']; + $this->val = $values['val']; + $this->right = $values['right'] ?? null; + $this->left = $values['left'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Commons/BinaryTreeValue.php b/seed/php-model/trace/src/Commons/BinaryTreeValue.php index 76301d44433d..58033453ed8e 100644 --- a/seed/php-model/trace/src/Commons/BinaryTreeValue.php +++ b/seed/php-model/trace/src/Commons/BinaryTreeValue.php @@ -28,15 +28,16 @@ class BinaryTreeValue extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->root = $values['root'] ?? null;$this->nodes = $values['nodes']; + ) { + $this->root = $values['root'] ?? null; + $this->nodes = $values['nodes']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Commons/DebugKeyValuePairs.php b/seed/php-model/trace/src/Commons/DebugKeyValuePairs.php index a5c39bd3c529..3c50e747ddc7 100644 --- a/seed/php-model/trace/src/Commons/DebugKeyValuePairs.php +++ b/seed/php-model/trace/src/Commons/DebugKeyValuePairs.php @@ -27,15 +27,16 @@ class DebugKeyValuePairs extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->key = $values['key'];$this->value = $values['value']; + ) { + $this->key = $values['key']; + $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Commons/DebugMapValue.php b/seed/php-model/trace/src/Commons/DebugMapValue.php index 3fe49f856da7..aa2f30841647 100644 --- a/seed/php-model/trace/src/Commons/DebugMapValue.php +++ b/seed/php-model/trace/src/Commons/DebugMapValue.php @@ -21,15 +21,15 @@ class DebugMapValue extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->keyValuePairs = $values['keyValuePairs']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Commons/DebugVariableValue.php b/seed/php-model/trace/src/Commons/DebugVariableValue.php index 29afa6614a22..78b7560c972e 100644 --- a/seed/php-model/trace/src/Commons/DebugVariableValue.php +++ b/seed/php-model/trace/src/Commons/DebugVariableValue.php @@ -82,16 +82,17 @@ class DebugVariableValue extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param int $integerValue * @return DebugVariableValue */ - public static function integerValue(int $integerValue): DebugVariableValue { + public static function integerValue(int $integerValue): DebugVariableValue + { return new DebugVariableValue([ 'type' => 'integerValue', 'value' => $integerValue, @@ -102,7 +103,8 @@ public static function integerValue(int $integerValue): DebugVariableValue { * @param bool $booleanValue * @return DebugVariableValue */ - public static function booleanValue(bool $booleanValue): DebugVariableValue { + public static function booleanValue(bool $booleanValue): DebugVariableValue + { return new DebugVariableValue([ 'type' => 'booleanValue', 'value' => $booleanValue, @@ -113,7 +115,8 @@ public static function booleanValue(bool $booleanValue): DebugVariableValue { * @param float $doubleValue * @return DebugVariableValue */ - public static function doubleValue(float $doubleValue): DebugVariableValue { + public static function doubleValue(float $doubleValue): DebugVariableValue + { return new DebugVariableValue([ 'type' => 'doubleValue', 'value' => $doubleValue, @@ -124,7 +127,8 @@ public static function doubleValue(float $doubleValue): DebugVariableValue { * @param string $stringValue * @return DebugVariableValue */ - public static function stringValue(string $stringValue): DebugVariableValue { + public static function stringValue(string $stringValue): DebugVariableValue + { return new DebugVariableValue([ 'type' => 'stringValue', 'value' => $stringValue, @@ -135,7 +139,8 @@ public static function stringValue(string $stringValue): DebugVariableValue { * @param string $charValue * @return DebugVariableValue */ - public static function charValue(string $charValue): DebugVariableValue { + public static function charValue(string $charValue): DebugVariableValue + { return new DebugVariableValue([ 'type' => 'charValue', 'value' => $charValue, @@ -146,7 +151,8 @@ public static function charValue(string $charValue): DebugVariableValue { * @param DebugMapValue $mapValue * @return DebugVariableValue */ - public static function mapValue(DebugMapValue $mapValue): DebugVariableValue { + public static function mapValue(DebugMapValue $mapValue): DebugVariableValue + { return new DebugVariableValue([ 'type' => 'mapValue', 'value' => $mapValue, @@ -157,7 +163,8 @@ public static function mapValue(DebugMapValue $mapValue): DebugVariableValue { * @param array $listValue * @return DebugVariableValue */ - public static function listValue(array $listValue): DebugVariableValue { + public static function listValue(array $listValue): DebugVariableValue + { return new DebugVariableValue([ 'type' => 'listValue', 'value' => $listValue, @@ -168,7 +175,8 @@ public static function listValue(array $listValue): DebugVariableValue { * @param BinaryTreeNodeAndTreeValue $binaryTreeNodeValue * @return DebugVariableValue */ - public static function binaryTreeNodeValue(BinaryTreeNodeAndTreeValue $binaryTreeNodeValue): DebugVariableValue { + public static function binaryTreeNodeValue(BinaryTreeNodeAndTreeValue $binaryTreeNodeValue): DebugVariableValue + { return new DebugVariableValue([ 'type' => 'binaryTreeNodeValue', 'value' => $binaryTreeNodeValue, @@ -179,7 +187,8 @@ public static function binaryTreeNodeValue(BinaryTreeNodeAndTreeValue $binaryTre * @param SinglyLinkedListNodeAndListValue $singlyLinkedListNodeValue * @return DebugVariableValue */ - public static function singlyLinkedListNodeValue(SinglyLinkedListNodeAndListValue $singlyLinkedListNodeValue): DebugVariableValue { + public static function singlyLinkedListNodeValue(SinglyLinkedListNodeAndListValue $singlyLinkedListNodeValue): DebugVariableValue + { return new DebugVariableValue([ 'type' => 'singlyLinkedListNodeValue', 'value' => $singlyLinkedListNodeValue, @@ -190,7 +199,8 @@ public static function singlyLinkedListNodeValue(SinglyLinkedListNodeAndListValu * @param DoublyLinkedListNodeAndListValue $doublyLinkedListNodeValue * @return DebugVariableValue */ - public static function doublyLinkedListNodeValue(DoublyLinkedListNodeAndListValue $doublyLinkedListNodeValue): DebugVariableValue { + public static function doublyLinkedListNodeValue(DoublyLinkedListNodeAndListValue $doublyLinkedListNodeValue): DebugVariableValue + { return new DebugVariableValue([ 'type' => 'doublyLinkedListNodeValue', 'value' => $doublyLinkedListNodeValue, @@ -200,7 +210,8 @@ public static function doublyLinkedListNodeValue(DoublyLinkedListNodeAndListValu /** * @return DebugVariableValue */ - public static function undefinedValue(): DebugVariableValue { + public static function undefinedValue(): DebugVariableValue + { return new DebugVariableValue([ 'type' => 'undefinedValue', 'value' => null, @@ -210,7 +221,8 @@ public static function undefinedValue(): DebugVariableValue { /** * @return DebugVariableValue */ - public static function nullValue(): DebugVariableValue { + public static function nullValue(): DebugVariableValue + { return new DebugVariableValue([ 'type' => 'nullValue', 'value' => null, @@ -221,7 +233,8 @@ public static function nullValue(): DebugVariableValue { * @param GenericValue $genericValue * @return DebugVariableValue */ - public static function genericValue(GenericValue $genericValue): DebugVariableValue { + public static function genericValue(GenericValue $genericValue): DebugVariableValue + { return new DebugVariableValue([ 'type' => 'genericValue', 'value' => $genericValue, @@ -231,255 +244,281 @@ public static function genericValue(GenericValue $genericValue): DebugVariableVa /** * @return bool */ - public function isIntegerValue(): bool { - return is_int($this->value)&& $this->type === 'integerValue'; + public function isIntegerValue(): bool + { + return is_int($this->value) && $this->type === 'integerValue'; } /** * @return int */ - public function asIntegerValue(): int { - if (!(is_int($this->value)&& $this->type === 'integerValue')){ + public function asIntegerValue(): int + { + if (!(is_int($this->value) && $this->type === 'integerValue')) { throw new Exception( "Expected integerValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isBooleanValue(): bool { - return is_bool($this->value)&& $this->type === 'booleanValue'; + public function isBooleanValue(): bool + { + return is_bool($this->value) && $this->type === 'booleanValue'; } /** * @return bool */ - public function asBooleanValue(): bool { - if (!(is_bool($this->value)&& $this->type === 'booleanValue')){ + public function asBooleanValue(): bool + { + if (!(is_bool($this->value) && $this->type === 'booleanValue')) { throw new Exception( "Expected booleanValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isDoubleValue(): bool { - return is_float($this->value)&& $this->type === 'doubleValue'; + public function isDoubleValue(): bool + { + return is_float($this->value) && $this->type === 'doubleValue'; } /** * @return float */ - public function asDoubleValue(): float { - if (!(is_float($this->value)&& $this->type === 'doubleValue')){ + public function asDoubleValue(): float + { + if (!(is_float($this->value) && $this->type === 'doubleValue')) { throw new Exception( "Expected doubleValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isStringValue(): bool { - return is_string($this->value)&& $this->type === 'stringValue'; + public function isStringValue(): bool + { + return is_string($this->value) && $this->type === 'stringValue'; } /** * @return string */ - public function asStringValue(): string { - if (!(is_string($this->value)&& $this->type === 'stringValue')){ + public function asStringValue(): string + { + if (!(is_string($this->value) && $this->type === 'stringValue')) { throw new Exception( "Expected stringValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isCharValue(): bool { - return is_string($this->value)&& $this->type === 'charValue'; + public function isCharValue(): bool + { + return is_string($this->value) && $this->type === 'charValue'; } /** * @return string */ - public function asCharValue(): string { - if (!(is_string($this->value)&& $this->type === 'charValue')){ + public function asCharValue(): string + { + if (!(is_string($this->value) && $this->type === 'charValue')) { throw new Exception( "Expected charValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isMapValue(): bool { - return $this->value instanceof DebugMapValue&& $this->type === 'mapValue'; + public function isMapValue(): bool + { + return $this->value instanceof DebugMapValue && $this->type === 'mapValue'; } /** * @return DebugMapValue */ - public function asMapValue(): DebugMapValue { - if (!($this->value instanceof DebugMapValue&& $this->type === 'mapValue')){ + public function asMapValue(): DebugMapValue + { + if (!($this->value instanceof DebugMapValue && $this->type === 'mapValue')) { throw new Exception( "Expected mapValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isListValue(): bool { - return is_array($this->value)&& $this->type === 'listValue'; + public function isListValue(): bool + { + return is_array($this->value) && $this->type === 'listValue'; } /** * @return array */ - public function asListValue(): array { - if (!(is_array($this->value)&& $this->type === 'listValue')){ + public function asListValue(): array + { + if (!(is_array($this->value) && $this->type === 'listValue')) { throw new Exception( "Expected listValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isBinaryTreeNodeValue(): bool { - return $this->value instanceof BinaryTreeNodeAndTreeValue&& $this->type === 'binaryTreeNodeValue'; + public function isBinaryTreeNodeValue(): bool + { + return $this->value instanceof BinaryTreeNodeAndTreeValue && $this->type === 'binaryTreeNodeValue'; } /** * @return BinaryTreeNodeAndTreeValue */ - public function asBinaryTreeNodeValue(): BinaryTreeNodeAndTreeValue { - if (!($this->value instanceof BinaryTreeNodeAndTreeValue&& $this->type === 'binaryTreeNodeValue')){ + public function asBinaryTreeNodeValue(): BinaryTreeNodeAndTreeValue + { + if (!($this->value instanceof BinaryTreeNodeAndTreeValue && $this->type === 'binaryTreeNodeValue')) { throw new Exception( "Expected binaryTreeNodeValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isSinglyLinkedListNodeValue(): bool { - return $this->value instanceof SinglyLinkedListNodeAndListValue&& $this->type === 'singlyLinkedListNodeValue'; + public function isSinglyLinkedListNodeValue(): bool + { + return $this->value instanceof SinglyLinkedListNodeAndListValue && $this->type === 'singlyLinkedListNodeValue'; } /** * @return SinglyLinkedListNodeAndListValue */ - public function asSinglyLinkedListNodeValue(): SinglyLinkedListNodeAndListValue { - if (!($this->value instanceof SinglyLinkedListNodeAndListValue&& $this->type === 'singlyLinkedListNodeValue')){ + public function asSinglyLinkedListNodeValue(): SinglyLinkedListNodeAndListValue + { + if (!($this->value instanceof SinglyLinkedListNodeAndListValue && $this->type === 'singlyLinkedListNodeValue')) { throw new Exception( "Expected singlyLinkedListNodeValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isDoublyLinkedListNodeValue(): bool { - return $this->value instanceof DoublyLinkedListNodeAndListValue&& $this->type === 'doublyLinkedListNodeValue'; + public function isDoublyLinkedListNodeValue(): bool + { + return $this->value instanceof DoublyLinkedListNodeAndListValue && $this->type === 'doublyLinkedListNodeValue'; } /** * @return DoublyLinkedListNodeAndListValue */ - public function asDoublyLinkedListNodeValue(): DoublyLinkedListNodeAndListValue { - if (!($this->value instanceof DoublyLinkedListNodeAndListValue&& $this->type === 'doublyLinkedListNodeValue')){ + public function asDoublyLinkedListNodeValue(): DoublyLinkedListNodeAndListValue + { + if (!($this->value instanceof DoublyLinkedListNodeAndListValue && $this->type === 'doublyLinkedListNodeValue')) { throw new Exception( "Expected doublyLinkedListNodeValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isUndefinedValue(): bool { - return is_null($this->value)&& $this->type === 'undefinedValue'; + public function isUndefinedValue(): bool + { + return is_null($this->value) && $this->type === 'undefinedValue'; } /** * @return bool */ - public function isNullValue(): bool { - return is_null($this->value)&& $this->type === 'nullValue'; + public function isNullValue(): bool + { + return is_null($this->value) && $this->type === 'nullValue'; } /** * @return bool */ - public function isGenericValue(): bool { - return $this->value instanceof GenericValue&& $this->type === 'genericValue'; + public function isGenericValue(): bool + { + return $this->value instanceof GenericValue && $this->type === 'genericValue'; } /** * @return GenericValue */ - public function asGenericValue(): GenericValue { - if (!($this->value instanceof GenericValue&& $this->type === 'genericValue')){ + public function asGenericValue(): GenericValue + { + if (!($this->value instanceof GenericValue && $this->type === 'genericValue')) { throw new Exception( "Expected genericValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'integerValue': $value = $this->value; $result['integerValue'] = $value; @@ -532,26 +571,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -560,77 +600,78 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'integerValue': - if (!array_key_exists('integerValue', $data)){ + if (!array_key_exists('integerValue', $data)) { throw new Exception( "JSON data is missing property 'integerValue'", ); } - + $args['value'] = $data['integerValue']; break; case 'booleanValue': - if (!array_key_exists('booleanValue', $data)){ + if (!array_key_exists('booleanValue', $data)) { throw new Exception( "JSON data is missing property 'booleanValue'", ); } - + $args['value'] = $data['booleanValue']; break; case 'doubleValue': - if (!array_key_exists('doubleValue', $data)){ + if (!array_key_exists('doubleValue', $data)) { throw new Exception( "JSON data is missing property 'doubleValue'", ); } - + $args['value'] = $data['doubleValue']; break; case 'stringValue': - if (!array_key_exists('stringValue', $data)){ + if (!array_key_exists('stringValue', $data)) { throw new Exception( "JSON data is missing property 'stringValue'", ); } - + $args['value'] = $data['stringValue']; break; case 'charValue': - if (!array_key_exists('charValue', $data)){ + if (!array_key_exists('charValue', $data)) { throw new Exception( "JSON data is missing property 'charValue'", ); } - + $args['value'] = $data['charValue']; break; case 'mapValue': $args['value'] = DebugMapValue::jsonDeserialize($data); break; case 'listValue': - if (!array_key_exists('listValue', $data)){ + if (!array_key_exists('listValue', $data)) { throw new Exception( "JSON data is missing property 'listValue'", ); } - + $args['value'] = $data['listValue']; break; case 'binaryTreeNodeValue': @@ -656,7 +697,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/trace/src/Commons/DoublyLinkedListNodeAndListValue.php b/seed/php-model/trace/src/Commons/DoublyLinkedListNodeAndListValue.php index 0330c466064d..6ce7e7ae12ad 100644 --- a/seed/php-model/trace/src/Commons/DoublyLinkedListNodeAndListValue.php +++ b/seed/php-model/trace/src/Commons/DoublyLinkedListNodeAndListValue.php @@ -27,15 +27,16 @@ class DoublyLinkedListNodeAndListValue extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->nodeId = $values['nodeId'];$this->fullList = $values['fullList']; + ) { + $this->nodeId = $values['nodeId']; + $this->fullList = $values['fullList']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Commons/DoublyLinkedListNodeValue.php b/seed/php-model/trace/src/Commons/DoublyLinkedListNodeValue.php index 92b80a6c6476..b4ea497e7050 100644 --- a/seed/php-model/trace/src/Commons/DoublyLinkedListNodeValue.php +++ b/seed/php-model/trace/src/Commons/DoublyLinkedListNodeValue.php @@ -41,15 +41,18 @@ class DoublyLinkedListNodeValue extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->nodeId = $values['nodeId'];$this->val = $values['val'];$this->next = $values['next'] ?? null;$this->prev = $values['prev'] ?? null; + ) { + $this->nodeId = $values['nodeId']; + $this->val = $values['val']; + $this->next = $values['next'] ?? null; + $this->prev = $values['prev'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Commons/DoublyLinkedListValue.php b/seed/php-model/trace/src/Commons/DoublyLinkedListValue.php index ea31bd0167b6..ef9ea179611c 100644 --- a/seed/php-model/trace/src/Commons/DoublyLinkedListValue.php +++ b/seed/php-model/trace/src/Commons/DoublyLinkedListValue.php @@ -28,15 +28,16 @@ class DoublyLinkedListValue extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->head = $values['head'] ?? null;$this->nodes = $values['nodes']; + ) { + $this->head = $values['head'] ?? null; + $this->nodes = $values['nodes']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Commons/FileInfo.php b/seed/php-model/trace/src/Commons/FileInfo.php index fe6e7566d051..8e7262ea726a 100644 --- a/seed/php-model/trace/src/Commons/FileInfo.php +++ b/seed/php-model/trace/src/Commons/FileInfo.php @@ -27,15 +27,16 @@ class FileInfo extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->filename = $values['filename'];$this->contents = $values['contents']; + ) { + $this->filename = $values['filename']; + $this->contents = $values['contents']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Commons/GenericValue.php b/seed/php-model/trace/src/Commons/GenericValue.php index 8d7f0eecc260..5116d758a680 100644 --- a/seed/php-model/trace/src/Commons/GenericValue.php +++ b/seed/php-model/trace/src/Commons/GenericValue.php @@ -27,15 +27,16 @@ class GenericValue extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->stringifiedType = $values['stringifiedType'] ?? null;$this->stringifiedValue = $values['stringifiedValue']; + ) { + $this->stringifiedType = $values['stringifiedType'] ?? null; + $this->stringifiedValue = $values['stringifiedValue']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Commons/KeyValuePair.php b/seed/php-model/trace/src/Commons/KeyValuePair.php index 069089019478..783cc6a3ace6 100644 --- a/seed/php-model/trace/src/Commons/KeyValuePair.php +++ b/seed/php-model/trace/src/Commons/KeyValuePair.php @@ -27,15 +27,16 @@ class KeyValuePair extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->key = $values['key'];$this->value = $values['value']; + ) { + $this->key = $values['key']; + $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Commons/Language.php b/seed/php-model/trace/src/Commons/Language.php index 4152e39580ea..2ddd8a2bb512 100644 --- a/seed/php-model/trace/src/Commons/Language.php +++ b/seed/php-model/trace/src/Commons/Language.php @@ -2,8 +2,8 @@ namespace Seed\Commons; -enum Language - : string { +enum Language: string +{ case Java = "JAVA"; case Javascript = "JAVASCRIPT"; case Python = "PYTHON"; diff --git a/seed/php-model/trace/src/Commons/ListType.php b/seed/php-model/trace/src/Commons/ListType.php index cd0482b472eb..d52338d43fd1 100644 --- a/seed/php-model/trace/src/Commons/ListType.php +++ b/seed/php-model/trace/src/Commons/ListType.php @@ -27,15 +27,16 @@ class ListType extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->valueType = $values['valueType'];$this->isFixedLength = $values['isFixedLength'] ?? null; + ) { + $this->valueType = $values['valueType']; + $this->isFixedLength = $values['isFixedLength'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Commons/MapType.php b/seed/php-model/trace/src/Commons/MapType.php index 79e5e25d2c5e..3f4305a851bd 100644 --- a/seed/php-model/trace/src/Commons/MapType.php +++ b/seed/php-model/trace/src/Commons/MapType.php @@ -27,15 +27,16 @@ class MapType extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->keyType = $values['keyType'];$this->valueType = $values['valueType']; + ) { + $this->keyType = $values['keyType']; + $this->valueType = $values['valueType']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Commons/MapValue.php b/seed/php-model/trace/src/Commons/MapValue.php index 9fa52eefa6dc..b1d62d1e8b5f 100644 --- a/seed/php-model/trace/src/Commons/MapValue.php +++ b/seed/php-model/trace/src/Commons/MapValue.php @@ -21,15 +21,15 @@ class MapValue extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->keyValuePairs = $values['keyValuePairs']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Commons/SinglyLinkedListNodeAndListValue.php b/seed/php-model/trace/src/Commons/SinglyLinkedListNodeAndListValue.php index 8c738b491699..fcf3e9c53169 100644 --- a/seed/php-model/trace/src/Commons/SinglyLinkedListNodeAndListValue.php +++ b/seed/php-model/trace/src/Commons/SinglyLinkedListNodeAndListValue.php @@ -27,15 +27,16 @@ class SinglyLinkedListNodeAndListValue extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->nodeId = $values['nodeId'];$this->fullList = $values['fullList']; + ) { + $this->nodeId = $values['nodeId']; + $this->fullList = $values['fullList']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Commons/SinglyLinkedListNodeValue.php b/seed/php-model/trace/src/Commons/SinglyLinkedListNodeValue.php index efb4f549b91f..ad8ef25414c2 100644 --- a/seed/php-model/trace/src/Commons/SinglyLinkedListNodeValue.php +++ b/seed/php-model/trace/src/Commons/SinglyLinkedListNodeValue.php @@ -34,15 +34,17 @@ class SinglyLinkedListNodeValue extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->nodeId = $values['nodeId'];$this->val = $values['val'];$this->next = $values['next'] ?? null; + ) { + $this->nodeId = $values['nodeId']; + $this->val = $values['val']; + $this->next = $values['next'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Commons/SinglyLinkedListValue.php b/seed/php-model/trace/src/Commons/SinglyLinkedListValue.php index 3f3a4c15e55a..bea34d0e9b4a 100644 --- a/seed/php-model/trace/src/Commons/SinglyLinkedListValue.php +++ b/seed/php-model/trace/src/Commons/SinglyLinkedListValue.php @@ -28,15 +28,16 @@ class SinglyLinkedListValue extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->head = $values['head'] ?? null;$this->nodes = $values['nodes']; + ) { + $this->head = $values['head'] ?? null; + $this->nodes = $values['nodes']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Commons/TestCase.php b/seed/php-model/trace/src/Commons/TestCase.php index cd29948bb5d8..6878519b6f08 100644 --- a/seed/php-model/trace/src/Commons/TestCase.php +++ b/seed/php-model/trace/src/Commons/TestCase.php @@ -28,15 +28,16 @@ class TestCase extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->params = $values['params']; + ) { + $this->id = $values['id']; + $this->params = $values['params']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Commons/TestCaseWithExpectedResult.php b/seed/php-model/trace/src/Commons/TestCaseWithExpectedResult.php index 84483019b292..2a190f4db281 100644 --- a/seed/php-model/trace/src/Commons/TestCaseWithExpectedResult.php +++ b/seed/php-model/trace/src/Commons/TestCaseWithExpectedResult.php @@ -27,15 +27,16 @@ class TestCaseWithExpectedResult extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->testCase = $values['testCase'];$this->expectedResult = $values['expectedResult']; + ) { + $this->testCase = $values['testCase']; + $this->expectedResult = $values['expectedResult']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Commons/VariableType.php b/seed/php-model/trace/src/Commons/VariableType.php index 955b4ff04759..560c5f0a6f4e 100644 --- a/seed/php-model/trace/src/Commons/VariableType.php +++ b/seed/php-model/trace/src/Commons/VariableType.php @@ -60,15 +60,16 @@ class VariableType extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @return VariableType */ - public static function integerType(): VariableType { + public static function integerType(): VariableType + { return new VariableType([ 'type' => 'integerType', 'value' => null, @@ -78,7 +79,8 @@ public static function integerType(): VariableType { /** * @return VariableType */ - public static function doubleType(): VariableType { + public static function doubleType(): VariableType + { return new VariableType([ 'type' => 'doubleType', 'value' => null, @@ -88,7 +90,8 @@ public static function doubleType(): VariableType { /** * @return VariableType */ - public static function booleanType(): VariableType { + public static function booleanType(): VariableType + { return new VariableType([ 'type' => 'booleanType', 'value' => null, @@ -98,7 +101,8 @@ public static function booleanType(): VariableType { /** * @return VariableType */ - public static function stringType(): VariableType { + public static function stringType(): VariableType + { return new VariableType([ 'type' => 'stringType', 'value' => null, @@ -108,7 +112,8 @@ public static function stringType(): VariableType { /** * @return VariableType */ - public static function charType(): VariableType { + public static function charType(): VariableType + { return new VariableType([ 'type' => 'charType', 'value' => null, @@ -119,7 +124,8 @@ public static function charType(): VariableType { * @param ListType $listType * @return VariableType */ - public static function listType(ListType $listType): VariableType { + public static function listType(ListType $listType): VariableType + { return new VariableType([ 'type' => 'listType', 'value' => $listType, @@ -130,7 +136,8 @@ public static function listType(ListType $listType): VariableType { * @param MapType $mapType * @return VariableType */ - public static function mapType(MapType $mapType): VariableType { + public static function mapType(MapType $mapType): VariableType + { return new VariableType([ 'type' => 'mapType', 'value' => $mapType, @@ -140,7 +147,8 @@ public static function mapType(MapType $mapType): VariableType { /** * @return VariableType */ - public static function binaryTreeType(): VariableType { + public static function binaryTreeType(): VariableType + { return new VariableType([ 'type' => 'binaryTreeType', 'value' => null, @@ -150,7 +158,8 @@ public static function binaryTreeType(): VariableType { /** * @return VariableType */ - public static function singlyLinkedListType(): VariableType { + public static function singlyLinkedListType(): VariableType + { return new VariableType([ 'type' => 'singlyLinkedListType', 'value' => null, @@ -160,7 +169,8 @@ public static function singlyLinkedListType(): VariableType { /** * @return VariableType */ - public static function doublyLinkedListType(): VariableType { + public static function doublyLinkedListType(): VariableType + { return new VariableType([ 'type' => 'doublyLinkedListType', 'value' => null, @@ -170,117 +180,131 @@ public static function doublyLinkedListType(): VariableType { /** * @return bool */ - public function isIntegerType(): bool { - return is_null($this->value)&& $this->type === 'integerType'; + public function isIntegerType(): bool + { + return is_null($this->value) && $this->type === 'integerType'; } /** * @return bool */ - public function isDoubleType(): bool { - return is_null($this->value)&& $this->type === 'doubleType'; + public function isDoubleType(): bool + { + return is_null($this->value) && $this->type === 'doubleType'; } /** * @return bool */ - public function isBooleanType(): bool { - return is_null($this->value)&& $this->type === 'booleanType'; + public function isBooleanType(): bool + { + return is_null($this->value) && $this->type === 'booleanType'; } /** * @return bool */ - public function isStringType(): bool { - return is_null($this->value)&& $this->type === 'stringType'; + public function isStringType(): bool + { + return is_null($this->value) && $this->type === 'stringType'; } /** * @return bool */ - public function isCharType(): bool { - return is_null($this->value)&& $this->type === 'charType'; + public function isCharType(): bool + { + return is_null($this->value) && $this->type === 'charType'; } /** * @return bool */ - public function isListType(): bool { - return $this->value instanceof ListType&& $this->type === 'listType'; + public function isListType(): bool + { + return $this->value instanceof ListType && $this->type === 'listType'; } /** * @return ListType */ - public function asListType(): ListType { - if (!($this->value instanceof ListType&& $this->type === 'listType')){ + public function asListType(): ListType + { + if (!($this->value instanceof ListType && $this->type === 'listType')) { throw new Exception( "Expected listType; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isMapType(): bool { - return $this->value instanceof MapType&& $this->type === 'mapType'; + public function isMapType(): bool + { + return $this->value instanceof MapType && $this->type === 'mapType'; } /** * @return MapType */ - public function asMapType(): MapType { - if (!($this->value instanceof MapType&& $this->type === 'mapType')){ + public function asMapType(): MapType + { + if (!($this->value instanceof MapType && $this->type === 'mapType')) { throw new Exception( "Expected mapType; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isBinaryTreeType(): bool { - return is_null($this->value)&& $this->type === 'binaryTreeType'; + public function isBinaryTreeType(): bool + { + return is_null($this->value) && $this->type === 'binaryTreeType'; } /** * @return bool */ - public function isSinglyLinkedListType(): bool { - return is_null($this->value)&& $this->type === 'singlyLinkedListType'; + public function isSinglyLinkedListType(): bool + { + return is_null($this->value) && $this->type === 'singlyLinkedListType'; } /** * @return bool */ - public function isDoublyLinkedListType(): bool { - return is_null($this->value)&& $this->type === 'doublyLinkedListType'; + public function isDoublyLinkedListType(): bool + { + return is_null($this->value) && $this->type === 'doublyLinkedListType'; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'integerType': $result['integerType'] = []; break; @@ -315,26 +339,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -343,22 +368,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'integerType': $args['value'] = null; break; @@ -394,7 +420,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/trace/src/Commons/VariableValue.php b/seed/php-model/trace/src/Commons/VariableValue.php index 4e5877977178..ca8bbbb7da1d 100644 --- a/seed/php-model/trace/src/Commons/VariableValue.php +++ b/seed/php-model/trace/src/Commons/VariableValue.php @@ -76,16 +76,17 @@ class VariableValue extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param int $integerValue * @return VariableValue */ - public static function integerValue(int $integerValue): VariableValue { + public static function integerValue(int $integerValue): VariableValue + { return new VariableValue([ 'type' => 'integerValue', 'value' => $integerValue, @@ -96,7 +97,8 @@ public static function integerValue(int $integerValue): VariableValue { * @param bool $booleanValue * @return VariableValue */ - public static function booleanValue(bool $booleanValue): VariableValue { + public static function booleanValue(bool $booleanValue): VariableValue + { return new VariableValue([ 'type' => 'booleanValue', 'value' => $booleanValue, @@ -107,7 +109,8 @@ public static function booleanValue(bool $booleanValue): VariableValue { * @param float $doubleValue * @return VariableValue */ - public static function doubleValue(float $doubleValue): VariableValue { + public static function doubleValue(float $doubleValue): VariableValue + { return new VariableValue([ 'type' => 'doubleValue', 'value' => $doubleValue, @@ -118,7 +121,8 @@ public static function doubleValue(float $doubleValue): VariableValue { * @param string $stringValue * @return VariableValue */ - public static function stringValue(string $stringValue): VariableValue { + public static function stringValue(string $stringValue): VariableValue + { return new VariableValue([ 'type' => 'stringValue', 'value' => $stringValue, @@ -129,7 +133,8 @@ public static function stringValue(string $stringValue): VariableValue { * @param string $charValue * @return VariableValue */ - public static function charValue(string $charValue): VariableValue { + public static function charValue(string $charValue): VariableValue + { return new VariableValue([ 'type' => 'charValue', 'value' => $charValue, @@ -140,7 +145,8 @@ public static function charValue(string $charValue): VariableValue { * @param MapValue $mapValue * @return VariableValue */ - public static function mapValue(MapValue $mapValue): VariableValue { + public static function mapValue(MapValue $mapValue): VariableValue + { return new VariableValue([ 'type' => 'mapValue', 'value' => $mapValue, @@ -151,7 +157,8 @@ public static function mapValue(MapValue $mapValue): VariableValue { * @param array $listValue * @return VariableValue */ - public static function listValue(array $listValue): VariableValue { + public static function listValue(array $listValue): VariableValue + { return new VariableValue([ 'type' => 'listValue', 'value' => $listValue, @@ -162,7 +169,8 @@ public static function listValue(array $listValue): VariableValue { * @param BinaryTreeValue $binaryTreeValue * @return VariableValue */ - public static function binaryTreeValue(BinaryTreeValue $binaryTreeValue): VariableValue { + public static function binaryTreeValue(BinaryTreeValue $binaryTreeValue): VariableValue + { return new VariableValue([ 'type' => 'binaryTreeValue', 'value' => $binaryTreeValue, @@ -173,7 +181,8 @@ public static function binaryTreeValue(BinaryTreeValue $binaryTreeValue): Variab * @param SinglyLinkedListValue $singlyLinkedListValue * @return VariableValue */ - public static function singlyLinkedListValue(SinglyLinkedListValue $singlyLinkedListValue): VariableValue { + public static function singlyLinkedListValue(SinglyLinkedListValue $singlyLinkedListValue): VariableValue + { return new VariableValue([ 'type' => 'singlyLinkedListValue', 'value' => $singlyLinkedListValue, @@ -184,7 +193,8 @@ public static function singlyLinkedListValue(SinglyLinkedListValue $singlyLinked * @param DoublyLinkedListValue $doublyLinkedListValue * @return VariableValue */ - public static function doublyLinkedListValue(DoublyLinkedListValue $doublyLinkedListValue): VariableValue { + public static function doublyLinkedListValue(DoublyLinkedListValue $doublyLinkedListValue): VariableValue + { return new VariableValue([ 'type' => 'doublyLinkedListValue', 'value' => $doublyLinkedListValue, @@ -194,7 +204,8 @@ public static function doublyLinkedListValue(DoublyLinkedListValue $doublyLinked /** * @return VariableValue */ - public static function nullValue(): VariableValue { + public static function nullValue(): VariableValue + { return new VariableValue([ 'type' => 'nullValue', 'value' => null, @@ -204,228 +215,251 @@ public static function nullValue(): VariableValue { /** * @return bool */ - public function isIntegerValue(): bool { - return is_int($this->value)&& $this->type === 'integerValue'; + public function isIntegerValue(): bool + { + return is_int($this->value) && $this->type === 'integerValue'; } /** * @return int */ - public function asIntegerValue(): int { - if (!(is_int($this->value)&& $this->type === 'integerValue')){ + public function asIntegerValue(): int + { + if (!(is_int($this->value) && $this->type === 'integerValue')) { throw new Exception( "Expected integerValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isBooleanValue(): bool { - return is_bool($this->value)&& $this->type === 'booleanValue'; + public function isBooleanValue(): bool + { + return is_bool($this->value) && $this->type === 'booleanValue'; } /** * @return bool */ - public function asBooleanValue(): bool { - if (!(is_bool($this->value)&& $this->type === 'booleanValue')){ + public function asBooleanValue(): bool + { + if (!(is_bool($this->value) && $this->type === 'booleanValue')) { throw new Exception( "Expected booleanValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isDoubleValue(): bool { - return is_float($this->value)&& $this->type === 'doubleValue'; + public function isDoubleValue(): bool + { + return is_float($this->value) && $this->type === 'doubleValue'; } /** * @return float */ - public function asDoubleValue(): float { - if (!(is_float($this->value)&& $this->type === 'doubleValue')){ + public function asDoubleValue(): float + { + if (!(is_float($this->value) && $this->type === 'doubleValue')) { throw new Exception( "Expected doubleValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isStringValue(): bool { - return is_string($this->value)&& $this->type === 'stringValue'; + public function isStringValue(): bool + { + return is_string($this->value) && $this->type === 'stringValue'; } /** * @return string */ - public function asStringValue(): string { - if (!(is_string($this->value)&& $this->type === 'stringValue')){ + public function asStringValue(): string + { + if (!(is_string($this->value) && $this->type === 'stringValue')) { throw new Exception( "Expected stringValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isCharValue(): bool { - return is_string($this->value)&& $this->type === 'charValue'; + public function isCharValue(): bool + { + return is_string($this->value) && $this->type === 'charValue'; } /** * @return string */ - public function asCharValue(): string { - if (!(is_string($this->value)&& $this->type === 'charValue')){ + public function asCharValue(): string + { + if (!(is_string($this->value) && $this->type === 'charValue')) { throw new Exception( "Expected charValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isMapValue(): bool { - return $this->value instanceof MapValue&& $this->type === 'mapValue'; + public function isMapValue(): bool + { + return $this->value instanceof MapValue && $this->type === 'mapValue'; } /** * @return MapValue */ - public function asMapValue(): MapValue { - if (!($this->value instanceof MapValue&& $this->type === 'mapValue')){ + public function asMapValue(): MapValue + { + if (!($this->value instanceof MapValue && $this->type === 'mapValue')) { throw new Exception( "Expected mapValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isListValue(): bool { - return is_array($this->value)&& $this->type === 'listValue'; + public function isListValue(): bool + { + return is_array($this->value) && $this->type === 'listValue'; } /** * @return array */ - public function asListValue(): array { - if (!(is_array($this->value)&& $this->type === 'listValue')){ + public function asListValue(): array + { + if (!(is_array($this->value) && $this->type === 'listValue')) { throw new Exception( "Expected listValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isBinaryTreeValue(): bool { - return $this->value instanceof BinaryTreeValue&& $this->type === 'binaryTreeValue'; + public function isBinaryTreeValue(): bool + { + return $this->value instanceof BinaryTreeValue && $this->type === 'binaryTreeValue'; } /** * @return BinaryTreeValue */ - public function asBinaryTreeValue(): BinaryTreeValue { - if (!($this->value instanceof BinaryTreeValue&& $this->type === 'binaryTreeValue')){ + public function asBinaryTreeValue(): BinaryTreeValue + { + if (!($this->value instanceof BinaryTreeValue && $this->type === 'binaryTreeValue')) { throw new Exception( "Expected binaryTreeValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isSinglyLinkedListValue(): bool { - return $this->value instanceof SinglyLinkedListValue&& $this->type === 'singlyLinkedListValue'; + public function isSinglyLinkedListValue(): bool + { + return $this->value instanceof SinglyLinkedListValue && $this->type === 'singlyLinkedListValue'; } /** * @return SinglyLinkedListValue */ - public function asSinglyLinkedListValue(): SinglyLinkedListValue { - if (!($this->value instanceof SinglyLinkedListValue&& $this->type === 'singlyLinkedListValue')){ + public function asSinglyLinkedListValue(): SinglyLinkedListValue + { + if (!($this->value instanceof SinglyLinkedListValue && $this->type === 'singlyLinkedListValue')) { throw new Exception( "Expected singlyLinkedListValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isDoublyLinkedListValue(): bool { - return $this->value instanceof DoublyLinkedListValue&& $this->type === 'doublyLinkedListValue'; + public function isDoublyLinkedListValue(): bool + { + return $this->value instanceof DoublyLinkedListValue && $this->type === 'doublyLinkedListValue'; } /** * @return DoublyLinkedListValue */ - public function asDoublyLinkedListValue(): DoublyLinkedListValue { - if (!($this->value instanceof DoublyLinkedListValue&& $this->type === 'doublyLinkedListValue')){ + public function asDoublyLinkedListValue(): DoublyLinkedListValue + { + if (!($this->value instanceof DoublyLinkedListValue && $this->type === 'doublyLinkedListValue')) { throw new Exception( "Expected doublyLinkedListValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isNullValue(): bool { - return is_null($this->value)&& $this->type === 'nullValue'; + public function isNullValue(): bool + { + return is_null($this->value) && $this->type === 'nullValue'; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'integerValue': $value = $this->value; $result['integerValue'] = $value; @@ -471,26 +505,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -499,77 +534,78 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'integerValue': - if (!array_key_exists('integerValue', $data)){ + if (!array_key_exists('integerValue', $data)) { throw new Exception( "JSON data is missing property 'integerValue'", ); } - + $args['value'] = $data['integerValue']; break; case 'booleanValue': - if (!array_key_exists('booleanValue', $data)){ + if (!array_key_exists('booleanValue', $data)) { throw new Exception( "JSON data is missing property 'booleanValue'", ); } - + $args['value'] = $data['booleanValue']; break; case 'doubleValue': - if (!array_key_exists('doubleValue', $data)){ + if (!array_key_exists('doubleValue', $data)) { throw new Exception( "JSON data is missing property 'doubleValue'", ); } - + $args['value'] = $data['doubleValue']; break; case 'stringValue': - if (!array_key_exists('stringValue', $data)){ + if (!array_key_exists('stringValue', $data)) { throw new Exception( "JSON data is missing property 'stringValue'", ); } - + $args['value'] = $data['stringValue']; break; case 'charValue': - if (!array_key_exists('charValue', $data)){ + if (!array_key_exists('charValue', $data)) { throw new Exception( "JSON data is missing property 'charValue'", ); } - + $args['value'] = $data['charValue']; break; case 'mapValue': $args['value'] = MapValue::jsonDeserialize($data); break; case 'listValue': - if (!array_key_exists('listValue', $data)){ + if (!array_key_exists('listValue', $data)) { throw new Exception( "JSON data is missing property 'listValue'", ); } - + $args['value'] = $data['listValue']; break; case 'binaryTreeValue': @@ -589,7 +625,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/trace/src/Core/Json/JsonEncoder.php b/seed/php-model/trace/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/trace/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/trace/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/trace/src/Core/Json/JsonProperty.php b/seed/php-model/trace/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/trace/src/Core/Json/JsonProperty.php +++ b/seed/php-model/trace/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/trace/src/Core/Types/ArrayType.php b/seed/php-model/trace/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/trace/src/Core/Types/ArrayType.php +++ b/seed/php-model/trace/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/trace/src/Core/Types/Constant.php b/seed/php-model/trace/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/trace/src/Core/Types/Constant.php +++ b/seed/php-model/trace/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/trace/src/Core/Types/Union.php b/seed/php-model/trace/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/trace/src/Core/Types/Union.php +++ b/seed/php-model/trace/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/trace/src/LangServer/LangServerRequest.php b/seed/php-model/trace/src/LangServer/LangServerRequest.php index a639c042957d..c8bb083c12c2 100644 --- a/seed/php-model/trace/src/LangServer/LangServerRequest.php +++ b/seed/php-model/trace/src/LangServer/LangServerRequest.php @@ -20,15 +20,15 @@ class LangServerRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->request = $values['request']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/LangServer/LangServerResponse.php b/seed/php-model/trace/src/LangServer/LangServerResponse.php index 01718854a340..eca61372998a 100644 --- a/seed/php-model/trace/src/LangServer/LangServerResponse.php +++ b/seed/php-model/trace/src/LangServer/LangServerResponse.php @@ -20,15 +20,15 @@ class LangServerResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->response = $values['response']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Migration/Migration.php b/seed/php-model/trace/src/Migration/Migration.php index 174d7e7ffcf9..02c85f87da8d 100644 --- a/seed/php-model/trace/src/Migration/Migration.php +++ b/seed/php-model/trace/src/Migration/Migration.php @@ -27,15 +27,16 @@ class Migration extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->status = $values['status']; + ) { + $this->name = $values['name']; + $this->status = $values['status']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Migration/MigrationStatus.php b/seed/php-model/trace/src/Migration/MigrationStatus.php index 18e8c46ac7f7..5542e1e2d4af 100644 --- a/seed/php-model/trace/src/Migration/MigrationStatus.php +++ b/seed/php-model/trace/src/Migration/MigrationStatus.php @@ -2,8 +2,8 @@ namespace Seed\Migration; -enum MigrationStatus - : string { +enum MigrationStatus: string +{ case Running = "RUNNING"; case Failed = "FAILED"; case Finished = "FINISHED"; diff --git a/seed/php-model/trace/src/Playlist/Playlist.php b/seed/php-model/trace/src/Playlist/Playlist.php index b205a71a9244..6e2c5239a695 100644 --- a/seed/php-model/trace/src/Playlist/Playlist.php +++ b/seed/php-model/trace/src/Playlist/Playlist.php @@ -32,15 +32,18 @@ class Playlist extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->problems = $values['problems'];$this->playlistId = $values['playlistId'];$this->ownerId = $values['ownerId']; + ) { + $this->name = $values['name']; + $this->problems = $values['problems']; + $this->playlistId = $values['playlistId']; + $this->ownerId = $values['ownerId']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Playlist/PlaylistCreateRequest.php b/seed/php-model/trace/src/Playlist/PlaylistCreateRequest.php index 904b3caaaf63..5acd5174eddd 100644 --- a/seed/php-model/trace/src/Playlist/PlaylistCreateRequest.php +++ b/seed/php-model/trace/src/Playlist/PlaylistCreateRequest.php @@ -28,15 +28,16 @@ class PlaylistCreateRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->problems = $values['problems']; + ) { + $this->name = $values['name']; + $this->problems = $values['problems']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Playlist/PlaylistIdNotFoundErrorBody.php b/seed/php-model/trace/src/Playlist/PlaylistIdNotFoundErrorBody.php index eacf2be402fe..43697aa76fdc 100644 --- a/seed/php-model/trace/src/Playlist/PlaylistIdNotFoundErrorBody.php +++ b/seed/php-model/trace/src/Playlist/PlaylistIdNotFoundErrorBody.php @@ -38,16 +38,17 @@ class PlaylistIdNotFoundErrorBody extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param string $playlistId * @return PlaylistIdNotFoundErrorBody */ - public static function playlistId(string $playlistId): PlaylistIdNotFoundErrorBody { + public static function playlistId(string $playlistId): PlaylistIdNotFoundErrorBody + { return new PlaylistIdNotFoundErrorBody([ 'type' => 'playlistId', 'value' => $playlistId, @@ -57,67 +58,72 @@ public static function playlistId(string $playlistId): PlaylistIdNotFoundErrorBo /** * @return bool */ - public function isPlaylistId(): bool { - return is_string($this->value)&& $this->type === 'playlistId'; + public function isPlaylistId(): bool + { + return is_string($this->value) && $this->type === 'playlistId'; } /** * @return string */ - public function asPlaylistId(): string { - if (!(is_string($this->value)&& $this->type === 'playlistId')){ + public function asPlaylistId(): string + { + if (!(is_string($this->value) && $this->type === 'playlistId')) { throw new Exception( "Expected playlistId; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'playlistId': $value = $this->value; $result['playlistId'] = $value; break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -126,29 +132,30 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'playlistId': - if (!array_key_exists('playlistId', $data)){ + if (!array_key_exists('playlistId', $data)) { throw new Exception( "JSON data is missing property 'playlistId'", ); } - + $args['value'] = $data['playlistId']; break; case '_unknown': @@ -156,7 +163,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/trace/src/Playlist/ReservedKeywordEnum.php b/seed/php-model/trace/src/Playlist/ReservedKeywordEnum.php index d0185eabfefe..3a9c9feaff70 100644 --- a/seed/php-model/trace/src/Playlist/ReservedKeywordEnum.php +++ b/seed/php-model/trace/src/Playlist/ReservedKeywordEnum.php @@ -2,8 +2,8 @@ namespace Seed\Playlist; -enum ReservedKeywordEnum - : string { +enum ReservedKeywordEnum: string +{ case Is = "is"; case As_ = "as"; } diff --git a/seed/php-model/trace/src/Playlist/Traits/PlaylistCreateRequest.php b/seed/php-model/trace/src/Playlist/Traits/PlaylistCreateRequest.php index 92344af598e6..782f7f4a3755 100644 --- a/seed/php-model/trace/src/Playlist/Traits/PlaylistCreateRequest.php +++ b/seed/php-model/trace/src/Playlist/Traits/PlaylistCreateRequest.php @@ -9,7 +9,7 @@ * @property string $name * @property array $problems */ -trait PlaylistCreateRequest +trait PlaylistCreateRequest { /** * @var string $name diff --git a/seed/php-model/trace/src/Playlist/UpdatePlaylistRequest.php b/seed/php-model/trace/src/Playlist/UpdatePlaylistRequest.php index 5ec683dbb5f3..847842d22ef1 100644 --- a/seed/php-model/trace/src/Playlist/UpdatePlaylistRequest.php +++ b/seed/php-model/trace/src/Playlist/UpdatePlaylistRequest.php @@ -28,15 +28,16 @@ class UpdatePlaylistRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->problems = $values['problems']; + ) { + $this->name = $values['name']; + $this->problems = $values['problems']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Problem/CreateProblemError.php b/seed/php-model/trace/src/Problem/CreateProblemError.php index 156dae27cebe..ef4f89d568f0 100644 --- a/seed/php-model/trace/src/Problem/CreateProblemError.php +++ b/seed/php-model/trace/src/Problem/CreateProblemError.php @@ -38,16 +38,17 @@ class CreateProblemError extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->errorType = $values['errorType'];$this->value = $values['value']; + ) { + $this->errorType = $values['errorType']; + $this->value = $values['value']; } /** * @param GenericCreateProblemError $generic * @return CreateProblemError */ - public static function generic(GenericCreateProblemError $generic): CreateProblemError { + public static function generic(GenericCreateProblemError $generic): CreateProblemError + { return new CreateProblemError([ 'errorType' => 'generic', 'value' => $generic, @@ -57,67 +58,72 @@ public static function generic(GenericCreateProblemError $generic): CreateProble /** * @return bool */ - public function isGeneric(): bool { - return $this->value instanceof GenericCreateProblemError&& $this->errorType === 'generic'; + public function isGeneric(): bool + { + return $this->value instanceof GenericCreateProblemError && $this->errorType === 'generic'; } /** * @return GenericCreateProblemError */ - public function asGeneric(): GenericCreateProblemError { - if (!($this->value instanceof GenericCreateProblemError&& $this->errorType === 'generic')){ + public function asGeneric(): GenericCreateProblemError + { + if (!($this->value instanceof GenericCreateProblemError && $this->errorType === 'generic')) { throw new Exception( "Expected generic; got " . $this->errorType . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['_type'] = $this->errorType; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->errorType){ + + switch ($this->errorType) { case 'generic': $value = $this->asGeneric()->jsonSerialize(); $result = array_merge($value, $result); break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -126,22 +132,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('_type', $data)){ + if (!array_key_exists('_type', $data)) { throw new Exception( "JSON data is missing property '_type'", ); } $errorType = $data['_type']; - if (!(is_string($errorType))){ + if (!(is_string($errorType))) { throw new Exception( "Expected property 'errorType' in JSON data to be string, instead received " . get_debug_type($data['_type']), ); } - + $args['errorType'] = $errorType; - switch ($errorType){ + switch ($errorType) { case 'generic': $args['value'] = GenericCreateProblemError::jsonDeserialize($data); break; @@ -150,7 +157,7 @@ public static function jsonDeserialize(array $data): static { $args['errorType'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/trace/src/Problem/CreateProblemRequest.php b/seed/php-model/trace/src/Problem/CreateProblemRequest.php index 444fd978c1a3..eb7d6f1ebf7d 100644 --- a/seed/php-model/trace/src/Problem/CreateProblemRequest.php +++ b/seed/php-model/trace/src/Problem/CreateProblemRequest.php @@ -66,15 +66,21 @@ class CreateProblemRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->problemName = $values['problemName'];$this->problemDescription = $values['problemDescription'];$this->files = $values['files'];$this->inputParams = $values['inputParams'];$this->outputType = $values['outputType'];$this->testcases = $values['testcases'];$this->methodName = $values['methodName']; + ) { + $this->problemName = $values['problemName']; + $this->problemDescription = $values['problemDescription']; + $this->files = $values['files']; + $this->inputParams = $values['inputParams']; + $this->outputType = $values['outputType']; + $this->testcases = $values['testcases']; + $this->methodName = $values['methodName']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Problem/CreateProblemResponse.php b/seed/php-model/trace/src/Problem/CreateProblemResponse.php index a8874cc217b1..a48903658e47 100644 --- a/seed/php-model/trace/src/Problem/CreateProblemResponse.php +++ b/seed/php-model/trace/src/Problem/CreateProblemResponse.php @@ -42,16 +42,17 @@ class CreateProblemResponse extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param string $success * @return CreateProblemResponse */ - public static function success(string $success): CreateProblemResponse { + public static function success(string $success): CreateProblemResponse + { return new CreateProblemResponse([ 'type' => 'success', 'value' => $success, @@ -62,7 +63,8 @@ public static function success(string $success): CreateProblemResponse { * @param CreateProblemError $error * @return CreateProblemResponse */ - public static function error(CreateProblemError $error): CreateProblemResponse { + public static function error(CreateProblemError $error): CreateProblemResponse + { return new CreateProblemResponse([ 'type' => 'error', 'value' => $error, @@ -72,61 +74,67 @@ public static function error(CreateProblemError $error): CreateProblemResponse { /** * @return bool */ - public function isSuccess(): bool { - return is_string($this->value)&& $this->type === 'success'; + public function isSuccess(): bool + { + return is_string($this->value) && $this->type === 'success'; } /** * @return string */ - public function asSuccess(): string { - if (!(is_string($this->value)&& $this->type === 'success')){ + public function asSuccess(): string + { + if (!(is_string($this->value) && $this->type === 'success')) { throw new Exception( "Expected success; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isError(): bool { - return $this->value instanceof CreateProblemError&& $this->type === 'error'; + public function isError(): bool + { + return $this->value instanceof CreateProblemError && $this->type === 'error'; } /** * @return CreateProblemError */ - public function asError(): CreateProblemError { - if (!($this->value instanceof CreateProblemError&& $this->type === 'error')){ + public function asError(): CreateProblemError + { + if (!($this->value instanceof CreateProblemError && $this->type === 'error')) { throw new Exception( "Expected error; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'success': $value = $this->value; $result['success'] = $value; @@ -137,26 +145,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -165,39 +174,40 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'success': - if (!array_key_exists('success', $data)){ + if (!array_key_exists('success', $data)) { throw new Exception( "JSON data is missing property 'success'", ); } - + $args['value'] = $data['success']; break; case 'error': - if (!array_key_exists('error', $data)){ + if (!array_key_exists('error', $data)) { throw new Exception( "JSON data is missing property 'error'", ); } - - if (!(is_array($data['error']))){ + + if (!(is_array($data['error']))) { throw new Exception( "Expected property 'error' in JSON data to be array, instead received " . get_debug_type($data['error']), ); @@ -209,7 +219,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/trace/src/Problem/GenericCreateProblemError.php b/seed/php-model/trace/src/Problem/GenericCreateProblemError.php index 69bcb0349b08..9bd1a7c63086 100644 --- a/seed/php-model/trace/src/Problem/GenericCreateProblemError.php +++ b/seed/php-model/trace/src/Problem/GenericCreateProblemError.php @@ -34,15 +34,17 @@ class GenericCreateProblemError extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->message = $values['message'];$this->type = $values['type'];$this->stacktrace = $values['stacktrace']; + ) { + $this->message = $values['message']; + $this->type = $values['type']; + $this->stacktrace = $values['stacktrace']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Problem/GetDefaultStarterFilesResponse.php b/seed/php-model/trace/src/Problem/GetDefaultStarterFilesResponse.php index f340f0483bca..19d9093a81f6 100644 --- a/seed/php-model/trace/src/Problem/GetDefaultStarterFilesResponse.php +++ b/seed/php-model/trace/src/Problem/GetDefaultStarterFilesResponse.php @@ -22,15 +22,15 @@ class GetDefaultStarterFilesResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->files = $values['files']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Problem/ProblemDescription.php b/seed/php-model/trace/src/Problem/ProblemDescription.php index 645176ea9d0f..c58430953e98 100644 --- a/seed/php-model/trace/src/Problem/ProblemDescription.php +++ b/seed/php-model/trace/src/Problem/ProblemDescription.php @@ -21,15 +21,15 @@ class ProblemDescription extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->boards = $values['boards']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Problem/ProblemDescriptionBoard.php b/seed/php-model/trace/src/Problem/ProblemDescriptionBoard.php index ae493b4f83b3..69b25399b64b 100644 --- a/seed/php-model/trace/src/Problem/ProblemDescriptionBoard.php +++ b/seed/php-model/trace/src/Problem/ProblemDescriptionBoard.php @@ -45,16 +45,17 @@ class ProblemDescriptionBoard extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param string $html * @return ProblemDescriptionBoard */ - public static function html(string $html): ProblemDescriptionBoard { + public static function html(string $html): ProblemDescriptionBoard + { return new ProblemDescriptionBoard([ 'type' => 'html', 'value' => $html, @@ -65,7 +66,8 @@ public static function html(string $html): ProblemDescriptionBoard { * @param VariableValue $variable * @return ProblemDescriptionBoard */ - public static function variable(VariableValue $variable): ProblemDescriptionBoard { + public static function variable(VariableValue $variable): ProblemDescriptionBoard + { return new ProblemDescriptionBoard([ 'type' => 'variable', 'value' => $variable, @@ -76,7 +78,8 @@ public static function variable(VariableValue $variable): ProblemDescriptionBoar * @param string $testCaseId * @return ProblemDescriptionBoard */ - public static function testCaseId(string $testCaseId): ProblemDescriptionBoard { + public static function testCaseId(string $testCaseId): ProblemDescriptionBoard + { return new ProblemDescriptionBoard([ 'type' => 'testCaseId', 'value' => $testCaseId, @@ -86,81 +89,89 @@ public static function testCaseId(string $testCaseId): ProblemDescriptionBoard { /** * @return bool */ - public function isHtml(): bool { - return is_string($this->value)&& $this->type === 'html'; + public function isHtml(): bool + { + return is_string($this->value) && $this->type === 'html'; } /** * @return string */ - public function asHtml(): string { - if (!(is_string($this->value)&& $this->type === 'html')){ + public function asHtml(): string + { + if (!(is_string($this->value) && $this->type === 'html')) { throw new Exception( "Expected html; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isVariable(): bool { - return $this->value instanceof VariableValue&& $this->type === 'variable'; + public function isVariable(): bool + { + return $this->value instanceof VariableValue && $this->type === 'variable'; } /** * @return VariableValue */ - public function asVariable(): VariableValue { - if (!($this->value instanceof VariableValue&& $this->type === 'variable')){ + public function asVariable(): VariableValue + { + if (!($this->value instanceof VariableValue && $this->type === 'variable')) { throw new Exception( "Expected variable; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isTestCaseId(): bool { - return is_string($this->value)&& $this->type === 'testCaseId'; + public function isTestCaseId(): bool + { + return is_string($this->value) && $this->type === 'testCaseId'; } /** * @return string */ - public function asTestCaseId(): string { - if (!(is_string($this->value)&& $this->type === 'testCaseId')){ + public function asTestCaseId(): string + { + if (!(is_string($this->value) && $this->type === 'testCaseId')) { throw new Exception( "Expected testCaseId; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'html': $value = $this->value; $result['html'] = $value; @@ -175,26 +186,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -203,39 +215,40 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'html': - if (!array_key_exists('html', $data)){ + if (!array_key_exists('html', $data)) { throw new Exception( "JSON data is missing property 'html'", ); } - + $args['value'] = $data['html']; break; case 'variable': - if (!array_key_exists('variable', $data)){ + if (!array_key_exists('variable', $data)) { throw new Exception( "JSON data is missing property 'variable'", ); } - - if (!(is_array($data['variable']))){ + + if (!(is_array($data['variable']))) { throw new Exception( "Expected property 'variable' in JSON data to be array, instead received " . get_debug_type($data['variable']), ); @@ -243,12 +256,12 @@ public static function jsonDeserialize(array $data): static { $args['value'] = VariableValue::jsonDeserialize($data['variable']); break; case 'testCaseId': - if (!array_key_exists('testCaseId', $data)){ + if (!array_key_exists('testCaseId', $data)) { throw new Exception( "JSON data is missing property 'testCaseId'", ); } - + $args['value'] = $data['testCaseId']; break; case '_unknown': @@ -256,7 +269,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/trace/src/Problem/ProblemFiles.php b/seed/php-model/trace/src/Problem/ProblemFiles.php index e51fa30e73aa..a79a83fdc12b 100644 --- a/seed/php-model/trace/src/Problem/ProblemFiles.php +++ b/seed/php-model/trace/src/Problem/ProblemFiles.php @@ -29,15 +29,16 @@ class ProblemFiles extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->solutionFile = $values['solutionFile'];$this->readOnlyFiles = $values['readOnlyFiles']; + ) { + $this->solutionFile = $values['solutionFile']; + $this->readOnlyFiles = $values['readOnlyFiles']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Problem/ProblemInfo.php b/seed/php-model/trace/src/Problem/ProblemInfo.php index 327aee52ce54..b19caf57dde4 100644 --- a/seed/php-model/trace/src/Problem/ProblemInfo.php +++ b/seed/php-model/trace/src/Problem/ProblemInfo.php @@ -87,15 +87,24 @@ class ProblemInfo extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->problemId = $values['problemId'];$this->problemDescription = $values['problemDescription'];$this->problemName = $values['problemName'];$this->problemVersion = $values['problemVersion'];$this->files = $values['files'];$this->inputParams = $values['inputParams'];$this->outputType = $values['outputType'];$this->testcases = $values['testcases'];$this->methodName = $values['methodName'];$this->supportsCustomTestCases = $values['supportsCustomTestCases']; + ) { + $this->problemId = $values['problemId']; + $this->problemDescription = $values['problemDescription']; + $this->problemName = $values['problemName']; + $this->problemVersion = $values['problemVersion']; + $this->files = $values['files']; + $this->inputParams = $values['inputParams']; + $this->outputType = $values['outputType']; + $this->testcases = $values['testcases']; + $this->methodName = $values['methodName']; + $this->supportsCustomTestCases = $values['supportsCustomTestCases']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Problem/UpdateProblemResponse.php b/seed/php-model/trace/src/Problem/UpdateProblemResponse.php index 7a84f9067c94..75e425f50e49 100644 --- a/seed/php-model/trace/src/Problem/UpdateProblemResponse.php +++ b/seed/php-model/trace/src/Problem/UpdateProblemResponse.php @@ -20,15 +20,15 @@ class UpdateProblemResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->problemVersion = $values['problemVersion']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Problem/VariableTypeAndName.php b/seed/php-model/trace/src/Problem/VariableTypeAndName.php index 8001f551b784..190aaedd8ded 100644 --- a/seed/php-model/trace/src/Problem/VariableTypeAndName.php +++ b/seed/php-model/trace/src/Problem/VariableTypeAndName.php @@ -28,15 +28,16 @@ class VariableTypeAndName extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->variableType = $values['variableType'];$this->name = $values['name']; + ) { + $this->variableType = $values['variableType']; + $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/ActualResult.php b/seed/php-model/trace/src/Submission/ActualResult.php index 2e1cdb1a7f61..8026b5c8fa43 100644 --- a/seed/php-model/trace/src/Submission/ActualResult.php +++ b/seed/php-model/trace/src/Submission/ActualResult.php @@ -47,16 +47,17 @@ class ActualResult extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param VariableValue $value * @return ActualResult */ - public static function value(VariableValue $value): ActualResult { + public static function value(VariableValue $value): ActualResult + { return new ActualResult([ 'type' => 'value', 'value' => $value, @@ -67,7 +68,8 @@ public static function value(VariableValue $value): ActualResult { * @param ExceptionInfo $exception * @return ActualResult */ - public static function exception(ExceptionInfo $exception): ActualResult { + public static function exception(ExceptionInfo $exception): ActualResult + { return new ActualResult([ 'type' => 'exception', 'value' => $exception, @@ -78,7 +80,8 @@ public static function exception(ExceptionInfo $exception): ActualResult { * @param ExceptionV2 $exceptionV2 * @return ActualResult */ - public static function exceptionV2(ExceptionV2 $exceptionV2): ActualResult { + public static function exceptionV2(ExceptionV2 $exceptionV2): ActualResult + { return new ActualResult([ 'type' => 'exceptionV2', 'value' => $exceptionV2, @@ -88,81 +91,89 @@ public static function exceptionV2(ExceptionV2 $exceptionV2): ActualResult { /** * @return bool */ - public function isValue(): bool { - return $this->value instanceof VariableValue&& $this->type === 'value'; + public function isValue(): bool + { + return $this->value instanceof VariableValue && $this->type === 'value'; } /** * @return VariableValue */ - public function asValue(): VariableValue { - if (!($this->value instanceof VariableValue&& $this->type === 'value')){ + public function asValue(): VariableValue + { + if (!($this->value instanceof VariableValue && $this->type === 'value')) { throw new Exception( "Expected value; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isException(): bool { - return $this->value instanceof ExceptionInfo&& $this->type === 'exception'; + public function isException(): bool + { + return $this->value instanceof ExceptionInfo && $this->type === 'exception'; } /** * @return ExceptionInfo */ - public function asException(): ExceptionInfo { - if (!($this->value instanceof ExceptionInfo&& $this->type === 'exception')){ + public function asException(): ExceptionInfo + { + if (!($this->value instanceof ExceptionInfo && $this->type === 'exception')) { throw new Exception( "Expected exception; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isExceptionV2(): bool { - return $this->value instanceof ExceptionV2&& $this->type === 'exceptionV2'; + public function isExceptionV2(): bool + { + return $this->value instanceof ExceptionV2 && $this->type === 'exceptionV2'; } /** * @return ExceptionV2 */ - public function asExceptionV2(): ExceptionV2 { - if (!($this->value instanceof ExceptionV2&& $this->type === 'exceptionV2')){ + public function asExceptionV2(): ExceptionV2 + { + if (!($this->value instanceof ExceptionV2 && $this->type === 'exceptionV2')) { throw new Exception( "Expected exceptionV2; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'value': $value = $this->asValue()->jsonSerialize(); $result['value'] = $value; @@ -177,26 +188,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -205,30 +217,31 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'value': - if (!array_key_exists('value', $data)){ + if (!array_key_exists('value', $data)) { throw new Exception( "JSON data is missing property 'value'", ); } - - if (!(is_array($data['value']))){ + + if (!(is_array($data['value']))) { throw new Exception( "Expected property 'value' in JSON data to be array, instead received " . get_debug_type($data['value']), ); @@ -239,13 +252,13 @@ public static function jsonDeserialize(array $data): static { $args['value'] = ExceptionInfo::jsonDeserialize($data); break; case 'exceptionV2': - if (!array_key_exists('exceptionV2', $data)){ + if (!array_key_exists('exceptionV2', $data)) { throw new Exception( "JSON data is missing property 'exceptionV2'", ); } - - if (!(is_array($data['exceptionV2']))){ + + if (!(is_array($data['exceptionV2']))) { throw new Exception( "Expected property 'exceptionV2' in JSON data to be array, instead received " . get_debug_type($data['exceptionV2']), ); @@ -257,7 +270,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/trace/src/Submission/BuildingExecutorResponse.php b/seed/php-model/trace/src/Submission/BuildingExecutorResponse.php index 8c47fa1f618f..226beaa6df58 100644 --- a/seed/php-model/trace/src/Submission/BuildingExecutorResponse.php +++ b/seed/php-model/trace/src/Submission/BuildingExecutorResponse.php @@ -27,15 +27,16 @@ class BuildingExecutorResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->submissionId = $values['submissionId'];$this->status = $values['status']; + ) { + $this->submissionId = $values['submissionId']; + $this->status = $values['status']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/CodeExecutionUpdate.php b/seed/php-model/trace/src/Submission/CodeExecutionUpdate.php index b0d7b6bb40c1..c75b1acc82ec 100644 --- a/seed/php-model/trace/src/Submission/CodeExecutionUpdate.php +++ b/seed/php-model/trace/src/Submission/CodeExecutionUpdate.php @@ -78,16 +78,17 @@ class CodeExecutionUpdate extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param BuildingExecutorResponse $buildingExecutor * @return CodeExecutionUpdate */ - public static function buildingExecutor(BuildingExecutorResponse $buildingExecutor): CodeExecutionUpdate { + public static function buildingExecutor(BuildingExecutorResponse $buildingExecutor): CodeExecutionUpdate + { return new CodeExecutionUpdate([ 'type' => 'buildingExecutor', 'value' => $buildingExecutor, @@ -98,7 +99,8 @@ public static function buildingExecutor(BuildingExecutorResponse $buildingExecut * @param RunningResponse $running * @return CodeExecutionUpdate */ - public static function running(RunningResponse $running): CodeExecutionUpdate { + public static function running(RunningResponse $running): CodeExecutionUpdate + { return new CodeExecutionUpdate([ 'type' => 'running', 'value' => $running, @@ -109,7 +111,8 @@ public static function running(RunningResponse $running): CodeExecutionUpdate { * @param ErroredResponse $errored * @return CodeExecutionUpdate */ - public static function errored(ErroredResponse $errored): CodeExecutionUpdate { + public static function errored(ErroredResponse $errored): CodeExecutionUpdate + { return new CodeExecutionUpdate([ 'type' => 'errored', 'value' => $errored, @@ -120,7 +123,8 @@ public static function errored(ErroredResponse $errored): CodeExecutionUpdate { * @param StoppedResponse $stopped * @return CodeExecutionUpdate */ - public static function stopped(StoppedResponse $stopped): CodeExecutionUpdate { + public static function stopped(StoppedResponse $stopped): CodeExecutionUpdate + { return new CodeExecutionUpdate([ 'type' => 'stopped', 'value' => $stopped, @@ -131,7 +135,8 @@ public static function stopped(StoppedResponse $stopped): CodeExecutionUpdate { * @param GradedResponse $graded * @return CodeExecutionUpdate */ - public static function graded(GradedResponse $graded): CodeExecutionUpdate { + public static function graded(GradedResponse $graded): CodeExecutionUpdate + { return new CodeExecutionUpdate([ 'type' => 'graded', 'value' => $graded, @@ -142,7 +147,8 @@ public static function graded(GradedResponse $graded): CodeExecutionUpdate { * @param GradedResponseV2 $gradedV2 * @return CodeExecutionUpdate */ - public static function gradedV2(GradedResponseV2 $gradedV2): CodeExecutionUpdate { + public static function gradedV2(GradedResponseV2 $gradedV2): CodeExecutionUpdate + { return new CodeExecutionUpdate([ 'type' => 'gradedV2', 'value' => $gradedV2, @@ -153,7 +159,8 @@ public static function gradedV2(GradedResponseV2 $gradedV2): CodeExecutionUpdate * @param WorkspaceRanResponse $workspaceRan * @return CodeExecutionUpdate */ - public static function workspaceRan(WorkspaceRanResponse $workspaceRan): CodeExecutionUpdate { + public static function workspaceRan(WorkspaceRanResponse $workspaceRan): CodeExecutionUpdate + { return new CodeExecutionUpdate([ 'type' => 'workspaceRan', 'value' => $workspaceRan, @@ -164,7 +171,8 @@ public static function workspaceRan(WorkspaceRanResponse $workspaceRan): CodeExe * @param RecordingResponseNotification $recording * @return CodeExecutionUpdate */ - public static function recording(RecordingResponseNotification $recording): CodeExecutionUpdate { + public static function recording(RecordingResponseNotification $recording): CodeExecutionUpdate + { return new CodeExecutionUpdate([ 'type' => 'recording', 'value' => $recording, @@ -175,7 +183,8 @@ public static function recording(RecordingResponseNotification $recording): Code * @param RecordedResponseNotification $recorded * @return CodeExecutionUpdate */ - public static function recorded(RecordedResponseNotification $recorded): CodeExecutionUpdate { + public static function recorded(RecordedResponseNotification $recorded): CodeExecutionUpdate + { return new CodeExecutionUpdate([ 'type' => 'recorded', 'value' => $recorded, @@ -186,7 +195,8 @@ public static function recorded(RecordedResponseNotification $recorded): CodeExe * @param InvalidRequestResponse $invalidRequest * @return CodeExecutionUpdate */ - public static function invalidRequest(InvalidRequestResponse $invalidRequest): CodeExecutionUpdate { + public static function invalidRequest(InvalidRequestResponse $invalidRequest): CodeExecutionUpdate + { return new CodeExecutionUpdate([ 'type' => 'invalidRequest', 'value' => $invalidRequest, @@ -197,7 +207,8 @@ public static function invalidRequest(InvalidRequestResponse $invalidRequest): C * @param FinishedResponse $finished * @return CodeExecutionUpdate */ - public static function finished(FinishedResponse $finished): CodeExecutionUpdate { + public static function finished(FinishedResponse $finished): CodeExecutionUpdate + { return new CodeExecutionUpdate([ 'type' => 'finished', 'value' => $finished, @@ -207,241 +218,265 @@ public static function finished(FinishedResponse $finished): CodeExecutionUpdate /** * @return bool */ - public function isBuildingExecutor(): bool { - return $this->value instanceof BuildingExecutorResponse&& $this->type === 'buildingExecutor'; + public function isBuildingExecutor(): bool + { + return $this->value instanceof BuildingExecutorResponse && $this->type === 'buildingExecutor'; } /** * @return BuildingExecutorResponse */ - public function asBuildingExecutor(): BuildingExecutorResponse { - if (!($this->value instanceof BuildingExecutorResponse&& $this->type === 'buildingExecutor')){ + public function asBuildingExecutor(): BuildingExecutorResponse + { + if (!($this->value instanceof BuildingExecutorResponse && $this->type === 'buildingExecutor')) { throw new Exception( "Expected buildingExecutor; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isRunning(): bool { - return $this->value instanceof RunningResponse&& $this->type === 'running'; + public function isRunning(): bool + { + return $this->value instanceof RunningResponse && $this->type === 'running'; } /** * @return RunningResponse */ - public function asRunning(): RunningResponse { - if (!($this->value instanceof RunningResponse&& $this->type === 'running')){ + public function asRunning(): RunningResponse + { + if (!($this->value instanceof RunningResponse && $this->type === 'running')) { throw new Exception( "Expected running; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isErrored(): bool { - return $this->value instanceof ErroredResponse&& $this->type === 'errored'; + public function isErrored(): bool + { + return $this->value instanceof ErroredResponse && $this->type === 'errored'; } /** * @return ErroredResponse */ - public function asErrored(): ErroredResponse { - if (!($this->value instanceof ErroredResponse&& $this->type === 'errored')){ + public function asErrored(): ErroredResponse + { + if (!($this->value instanceof ErroredResponse && $this->type === 'errored')) { throw new Exception( "Expected errored; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isStopped(): bool { - return $this->value instanceof StoppedResponse&& $this->type === 'stopped'; + public function isStopped(): bool + { + return $this->value instanceof StoppedResponse && $this->type === 'stopped'; } /** * @return StoppedResponse */ - public function asStopped(): StoppedResponse { - if (!($this->value instanceof StoppedResponse&& $this->type === 'stopped')){ + public function asStopped(): StoppedResponse + { + if (!($this->value instanceof StoppedResponse && $this->type === 'stopped')) { throw new Exception( "Expected stopped; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isGraded(): bool { - return $this->value instanceof GradedResponse&& $this->type === 'graded'; + public function isGraded(): bool + { + return $this->value instanceof GradedResponse && $this->type === 'graded'; } /** * @return GradedResponse */ - public function asGraded(): GradedResponse { - if (!($this->value instanceof GradedResponse&& $this->type === 'graded')){ + public function asGraded(): GradedResponse + { + if (!($this->value instanceof GradedResponse && $this->type === 'graded')) { throw new Exception( "Expected graded; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isGradedV2(): bool { - return $this->value instanceof GradedResponseV2&& $this->type === 'gradedV2'; + public function isGradedV2(): bool + { + return $this->value instanceof GradedResponseV2 && $this->type === 'gradedV2'; } /** * @return GradedResponseV2 */ - public function asGradedV2(): GradedResponseV2 { - if (!($this->value instanceof GradedResponseV2&& $this->type === 'gradedV2')){ + public function asGradedV2(): GradedResponseV2 + { + if (!($this->value instanceof GradedResponseV2 && $this->type === 'gradedV2')) { throw new Exception( "Expected gradedV2; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isWorkspaceRan(): bool { - return $this->value instanceof WorkspaceRanResponse&& $this->type === 'workspaceRan'; + public function isWorkspaceRan(): bool + { + return $this->value instanceof WorkspaceRanResponse && $this->type === 'workspaceRan'; } /** * @return WorkspaceRanResponse */ - public function asWorkspaceRan(): WorkspaceRanResponse { - if (!($this->value instanceof WorkspaceRanResponse&& $this->type === 'workspaceRan')){ + public function asWorkspaceRan(): WorkspaceRanResponse + { + if (!($this->value instanceof WorkspaceRanResponse && $this->type === 'workspaceRan')) { throw new Exception( "Expected workspaceRan; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isRecording(): bool { - return $this->value instanceof RecordingResponseNotification&& $this->type === 'recording'; + public function isRecording(): bool + { + return $this->value instanceof RecordingResponseNotification && $this->type === 'recording'; } /** * @return RecordingResponseNotification */ - public function asRecording(): RecordingResponseNotification { - if (!($this->value instanceof RecordingResponseNotification&& $this->type === 'recording')){ + public function asRecording(): RecordingResponseNotification + { + if (!($this->value instanceof RecordingResponseNotification && $this->type === 'recording')) { throw new Exception( "Expected recording; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isRecorded(): bool { - return $this->value instanceof RecordedResponseNotification&& $this->type === 'recorded'; + public function isRecorded(): bool + { + return $this->value instanceof RecordedResponseNotification && $this->type === 'recorded'; } /** * @return RecordedResponseNotification */ - public function asRecorded(): RecordedResponseNotification { - if (!($this->value instanceof RecordedResponseNotification&& $this->type === 'recorded')){ + public function asRecorded(): RecordedResponseNotification + { + if (!($this->value instanceof RecordedResponseNotification && $this->type === 'recorded')) { throw new Exception( "Expected recorded; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isInvalidRequest(): bool { - return $this->value instanceof InvalidRequestResponse&& $this->type === 'invalidRequest'; + public function isInvalidRequest(): bool + { + return $this->value instanceof InvalidRequestResponse && $this->type === 'invalidRequest'; } /** * @return InvalidRequestResponse */ - public function asInvalidRequest(): InvalidRequestResponse { - if (!($this->value instanceof InvalidRequestResponse&& $this->type === 'invalidRequest')){ + public function asInvalidRequest(): InvalidRequestResponse + { + if (!($this->value instanceof InvalidRequestResponse && $this->type === 'invalidRequest')) { throw new Exception( "Expected invalidRequest; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isFinished(): bool { - return $this->value instanceof FinishedResponse&& $this->type === 'finished'; + public function isFinished(): bool + { + return $this->value instanceof FinishedResponse && $this->type === 'finished'; } /** * @return FinishedResponse */ - public function asFinished(): FinishedResponse { - if (!($this->value instanceof FinishedResponse&& $this->type === 'finished')){ + public function asFinished(): FinishedResponse + { + if (!($this->value instanceof FinishedResponse && $this->type === 'finished')) { throw new Exception( "Expected finished; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'buildingExecutor': $value = $this->asBuildingExecutor()->jsonSerialize(); $result = array_merge($value, $result); @@ -488,26 +523,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -516,22 +552,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'buildingExecutor': $args['value'] = BuildingExecutorResponse::jsonDeserialize($data); break; @@ -570,7 +607,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/trace/src/Submission/CompileError.php b/seed/php-model/trace/src/Submission/CompileError.php index 9f2e87a2a64c..af6a9787fe71 100644 --- a/seed/php-model/trace/src/Submission/CompileError.php +++ b/seed/php-model/trace/src/Submission/CompileError.php @@ -20,15 +20,15 @@ class CompileError extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->message = $values['message']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/CustomTestCasesUnsupported.php b/seed/php-model/trace/src/Submission/CustomTestCasesUnsupported.php index bd62f207398f..dc1379469f13 100644 --- a/seed/php-model/trace/src/Submission/CustomTestCasesUnsupported.php +++ b/seed/php-model/trace/src/Submission/CustomTestCasesUnsupported.php @@ -27,15 +27,16 @@ class CustomTestCasesUnsupported extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->problemId = $values['problemId'];$this->submissionId = $values['submissionId']; + ) { + $this->problemId = $values['problemId']; + $this->submissionId = $values['submissionId']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/ErrorInfo.php b/seed/php-model/trace/src/Submission/ErrorInfo.php index 29fb08438a2b..49d18367a16f 100644 --- a/seed/php-model/trace/src/Submission/ErrorInfo.php +++ b/seed/php-model/trace/src/Submission/ErrorInfo.php @@ -46,16 +46,17 @@ class ErrorInfo extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param CompileError $compileError * @return ErrorInfo */ - public static function compileError(CompileError $compileError): ErrorInfo { + public static function compileError(CompileError $compileError): ErrorInfo + { return new ErrorInfo([ 'type' => 'compileError', 'value' => $compileError, @@ -66,7 +67,8 @@ public static function compileError(CompileError $compileError): ErrorInfo { * @param RuntimeError $runtimeError * @return ErrorInfo */ - public static function runtimeError(RuntimeError $runtimeError): ErrorInfo { + public static function runtimeError(RuntimeError $runtimeError): ErrorInfo + { return new ErrorInfo([ 'type' => 'runtimeError', 'value' => $runtimeError, @@ -77,7 +79,8 @@ public static function runtimeError(RuntimeError $runtimeError): ErrorInfo { * @param InternalError $internalError * @return ErrorInfo */ - public static function internalError(InternalError $internalError): ErrorInfo { + public static function internalError(InternalError $internalError): ErrorInfo + { return new ErrorInfo([ 'type' => 'internalError', 'value' => $internalError, @@ -87,81 +90,89 @@ public static function internalError(InternalError $internalError): ErrorInfo { /** * @return bool */ - public function isCompileError(): bool { - return $this->value instanceof CompileError&& $this->type === 'compileError'; + public function isCompileError(): bool + { + return $this->value instanceof CompileError && $this->type === 'compileError'; } /** * @return CompileError */ - public function asCompileError(): CompileError { - if (!($this->value instanceof CompileError&& $this->type === 'compileError')){ + public function asCompileError(): CompileError + { + if (!($this->value instanceof CompileError && $this->type === 'compileError')) { throw new Exception( "Expected compileError; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isRuntimeError(): bool { - return $this->value instanceof RuntimeError&& $this->type === 'runtimeError'; + public function isRuntimeError(): bool + { + return $this->value instanceof RuntimeError && $this->type === 'runtimeError'; } /** * @return RuntimeError */ - public function asRuntimeError(): RuntimeError { - if (!($this->value instanceof RuntimeError&& $this->type === 'runtimeError')){ + public function asRuntimeError(): RuntimeError + { + if (!($this->value instanceof RuntimeError && $this->type === 'runtimeError')) { throw new Exception( "Expected runtimeError; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isInternalError(): bool { - return $this->value instanceof InternalError&& $this->type === 'internalError'; + public function isInternalError(): bool + { + return $this->value instanceof InternalError && $this->type === 'internalError'; } /** * @return InternalError */ - public function asInternalError(): InternalError { - if (!($this->value instanceof InternalError&& $this->type === 'internalError')){ + public function asInternalError(): InternalError + { + if (!($this->value instanceof InternalError && $this->type === 'internalError')) { throw new Exception( "Expected internalError; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'compileError': $value = $this->asCompileError()->jsonSerialize(); $result = array_merge($value, $result); @@ -176,26 +187,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -204,22 +216,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'compileError': $args['value'] = CompileError::jsonDeserialize($data); break; @@ -234,7 +247,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/trace/src/Submission/ErroredResponse.php b/seed/php-model/trace/src/Submission/ErroredResponse.php index 2e2dc15a1161..24e150a76cde 100644 --- a/seed/php-model/trace/src/Submission/ErroredResponse.php +++ b/seed/php-model/trace/src/Submission/ErroredResponse.php @@ -27,15 +27,16 @@ class ErroredResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->submissionId = $values['submissionId'];$this->errorInfo = $values['errorInfo']; + ) { + $this->submissionId = $values['submissionId']; + $this->errorInfo = $values['errorInfo']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/ExceptionInfo.php b/seed/php-model/trace/src/Submission/ExceptionInfo.php index 778de1c4bd24..ec41303bfbec 100644 --- a/seed/php-model/trace/src/Submission/ExceptionInfo.php +++ b/seed/php-model/trace/src/Submission/ExceptionInfo.php @@ -34,15 +34,17 @@ class ExceptionInfo extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->exceptionType = $values['exceptionType'];$this->exceptionMessage = $values['exceptionMessage'];$this->exceptionStacktrace = $values['exceptionStacktrace']; + ) { + $this->exceptionType = $values['exceptionType']; + $this->exceptionMessage = $values['exceptionMessage']; + $this->exceptionStacktrace = $values['exceptionStacktrace']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/ExceptionV2.php b/seed/php-model/trace/src/Submission/ExceptionV2.php index 08e3dd6007e1..688001826040 100644 --- a/seed/php-model/trace/src/Submission/ExceptionV2.php +++ b/seed/php-model/trace/src/Submission/ExceptionV2.php @@ -42,16 +42,17 @@ class ExceptionV2 extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param ExceptionInfo $generic * @return ExceptionV2 */ - public static function generic(ExceptionInfo $generic): ExceptionV2 { + public static function generic(ExceptionInfo $generic): ExceptionV2 + { return new ExceptionV2([ 'type' => 'generic', 'value' => $generic, @@ -61,7 +62,8 @@ public static function generic(ExceptionInfo $generic): ExceptionV2 { /** * @return ExceptionV2 */ - public static function timeout(): ExceptionV2 { + public static function timeout(): ExceptionV2 + { return new ExceptionV2([ 'type' => 'timeout', 'value' => null, @@ -71,48 +73,53 @@ public static function timeout(): ExceptionV2 { /** * @return bool */ - public function isGeneric(): bool { - return $this->value instanceof ExceptionInfo&& $this->type === 'generic'; + public function isGeneric(): bool + { + return $this->value instanceof ExceptionInfo && $this->type === 'generic'; } /** * @return ExceptionInfo */ - public function asGeneric(): ExceptionInfo { - if (!($this->value instanceof ExceptionInfo&& $this->type === 'generic')){ + public function asGeneric(): ExceptionInfo + { + if (!($this->value instanceof ExceptionInfo && $this->type === 'generic')) { throw new Exception( "Expected generic; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isTimeout(): bool { - return is_null($this->value)&& $this->type === 'timeout'; + public function isTimeout(): bool + { + return is_null($this->value) && $this->type === 'timeout'; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'generic': $value = $this->asGeneric()->jsonSerialize(); $result = array_merge($value, $result); @@ -122,26 +129,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -150,22 +158,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'generic': $args['value'] = ExceptionInfo::jsonDeserialize($data); break; @@ -177,7 +186,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/trace/src/Submission/ExecutionSessionResponse.php b/seed/php-model/trace/src/Submission/ExecutionSessionResponse.php index 47ac17adb2ae..c5f8e3ced9d5 100644 --- a/seed/php-model/trace/src/Submission/ExecutionSessionResponse.php +++ b/seed/php-model/trace/src/Submission/ExecutionSessionResponse.php @@ -42,15 +42,18 @@ class ExecutionSessionResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->sessionId = $values['sessionId'];$this->executionSessionUrl = $values['executionSessionUrl'] ?? null;$this->language = $values['language'];$this->status = $values['status']; + ) { + $this->sessionId = $values['sessionId']; + $this->executionSessionUrl = $values['executionSessionUrl'] ?? null; + $this->language = $values['language']; + $this->status = $values['status']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/ExecutionSessionState.php b/seed/php-model/trace/src/Submission/ExecutionSessionState.php index 4bce3cfd8e18..8c8103722fd6 100644 --- a/seed/php-model/trace/src/Submission/ExecutionSessionState.php +++ b/seed/php-model/trace/src/Submission/ExecutionSessionState.php @@ -56,15 +56,20 @@ class ExecutionSessionState extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->lastTimeContacted = $values['lastTimeContacted'] ?? null;$this->sessionId = $values['sessionId'];$this->isWarmInstance = $values['isWarmInstance'];$this->awsTaskId = $values['awsTaskId'] ?? null;$this->language = $values['language'];$this->status = $values['status']; + ) { + $this->lastTimeContacted = $values['lastTimeContacted'] ?? null; + $this->sessionId = $values['sessionId']; + $this->isWarmInstance = $values['isWarmInstance']; + $this->awsTaskId = $values['awsTaskId'] ?? null; + $this->language = $values['language']; + $this->status = $values['status']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/ExecutionSessionStatus.php b/seed/php-model/trace/src/Submission/ExecutionSessionStatus.php index f9490ab6164f..a52241c41bb5 100644 --- a/seed/php-model/trace/src/Submission/ExecutionSessionStatus.php +++ b/seed/php-model/trace/src/Submission/ExecutionSessionStatus.php @@ -2,8 +2,8 @@ namespace Seed\Submission; -enum ExecutionSessionStatus - : string { +enum ExecutionSessionStatus: string +{ case CreatingContainer = "CREATING_CONTAINER"; case ProvisioningContainer = "PROVISIONING_CONTAINER"; case PendingContainer = "PENDING_CONTAINER"; diff --git a/seed/php-model/trace/src/Submission/ExistingSubmissionExecuting.php b/seed/php-model/trace/src/Submission/ExistingSubmissionExecuting.php index cfb65c40fcea..ad3ad52848b4 100644 --- a/seed/php-model/trace/src/Submission/ExistingSubmissionExecuting.php +++ b/seed/php-model/trace/src/Submission/ExistingSubmissionExecuting.php @@ -20,15 +20,15 @@ class ExistingSubmissionExecuting extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->submissionId = $values['submissionId']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/ExpressionLocation.php b/seed/php-model/trace/src/Submission/ExpressionLocation.php index 5ac09b15fe7b..702e30618e2a 100644 --- a/seed/php-model/trace/src/Submission/ExpressionLocation.php +++ b/seed/php-model/trace/src/Submission/ExpressionLocation.php @@ -27,15 +27,16 @@ class ExpressionLocation extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->start = $values['start'];$this->offset = $values['offset']; + ) { + $this->start = $values['start']; + $this->offset = $values['offset']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/FinishedResponse.php b/seed/php-model/trace/src/Submission/FinishedResponse.php index f238650dad70..5c84af8ee740 100644 --- a/seed/php-model/trace/src/Submission/FinishedResponse.php +++ b/seed/php-model/trace/src/Submission/FinishedResponse.php @@ -20,15 +20,15 @@ class FinishedResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->submissionId = $values['submissionId']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/GetExecutionSessionStateResponse.php b/seed/php-model/trace/src/Submission/GetExecutionSessionStateResponse.php index 5ea02a3b34f7..d401ce052401 100644 --- a/seed/php-model/trace/src/Submission/GetExecutionSessionStateResponse.php +++ b/seed/php-model/trace/src/Submission/GetExecutionSessionStateResponse.php @@ -35,15 +35,17 @@ class GetExecutionSessionStateResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->states = $values['states'];$this->numWarmingInstances = $values['numWarmingInstances'] ?? null;$this->warmingSessionIds = $values['warmingSessionIds']; + ) { + $this->states = $values['states']; + $this->numWarmingInstances = $values['numWarmingInstances'] ?? null; + $this->warmingSessionIds = $values['warmingSessionIds']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/GetSubmissionStateResponse.php b/seed/php-model/trace/src/Submission/GetSubmissionStateResponse.php index bd05899c73c1..f40faead4289 100644 --- a/seed/php-model/trace/src/Submission/GetSubmissionStateResponse.php +++ b/seed/php-model/trace/src/Submission/GetSubmissionStateResponse.php @@ -44,15 +44,18 @@ class GetSubmissionStateResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->timeSubmitted = $values['timeSubmitted'] ?? null;$this->submission = $values['submission'];$this->language = $values['language'];$this->submissionTypeState = $values['submissionTypeState']; + ) { + $this->timeSubmitted = $values['timeSubmitted'] ?? null; + $this->submission = $values['submission']; + $this->language = $values['language']; + $this->submissionTypeState = $values['submissionTypeState']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/GetTraceResponsesPageRequest.php b/seed/php-model/trace/src/Submission/GetTraceResponsesPageRequest.php index 844f13ad2af5..44ab9df49d2a 100644 --- a/seed/php-model/trace/src/Submission/GetTraceResponsesPageRequest.php +++ b/seed/php-model/trace/src/Submission/GetTraceResponsesPageRequest.php @@ -20,15 +20,15 @@ class GetTraceResponsesPageRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->offset = $values['offset'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/GradedResponse.php b/seed/php-model/trace/src/Submission/GradedResponse.php index 5e4a03ae0352..b35d8ac55865 100644 --- a/seed/php-model/trace/src/Submission/GradedResponse.php +++ b/seed/php-model/trace/src/Submission/GradedResponse.php @@ -28,15 +28,16 @@ class GradedResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->submissionId = $values['submissionId'];$this->testCases = $values['testCases']; + ) { + $this->submissionId = $values['submissionId']; + $this->testCases = $values['testCases']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/GradedResponseV2.php b/seed/php-model/trace/src/Submission/GradedResponseV2.php index b83f9c4233f6..cd12edb25061 100644 --- a/seed/php-model/trace/src/Submission/GradedResponseV2.php +++ b/seed/php-model/trace/src/Submission/GradedResponseV2.php @@ -28,15 +28,16 @@ class GradedResponseV2 extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->submissionId = $values['submissionId'];$this->testCases = $values['testCases']; + ) { + $this->submissionId = $values['submissionId']; + $this->testCases = $values['testCases']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/GradedTestCaseUpdate.php b/seed/php-model/trace/src/Submission/GradedTestCaseUpdate.php index da077d2040f6..24a1add1d487 100644 --- a/seed/php-model/trace/src/Submission/GradedTestCaseUpdate.php +++ b/seed/php-model/trace/src/Submission/GradedTestCaseUpdate.php @@ -27,15 +27,16 @@ class GradedTestCaseUpdate extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->testCaseId = $values['testCaseId'];$this->grade = $values['grade']; + ) { + $this->testCaseId = $values['testCaseId']; + $this->grade = $values['grade']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/InitializeProblemRequest.php b/seed/php-model/trace/src/Submission/InitializeProblemRequest.php index b6a5ab669cd7..12dac769c2a7 100644 --- a/seed/php-model/trace/src/Submission/InitializeProblemRequest.php +++ b/seed/php-model/trace/src/Submission/InitializeProblemRequest.php @@ -27,15 +27,16 @@ class InitializeProblemRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->problemId = $values['problemId'];$this->problemVersion = $values['problemVersion'] ?? null; + ) { + $this->problemId = $values['problemId']; + $this->problemVersion = $values['problemVersion'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/InternalError.php b/seed/php-model/trace/src/Submission/InternalError.php index ca0d8b909b9d..0f2c6294a69e 100644 --- a/seed/php-model/trace/src/Submission/InternalError.php +++ b/seed/php-model/trace/src/Submission/InternalError.php @@ -20,15 +20,15 @@ class InternalError extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->exceptionInfo = $values['exceptionInfo']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/InvalidRequestCause.php b/seed/php-model/trace/src/Submission/InvalidRequestCause.php index cf84c8d581ba..162e7ecfce81 100644 --- a/seed/php-model/trace/src/Submission/InvalidRequestCause.php +++ b/seed/php-model/trace/src/Submission/InvalidRequestCause.php @@ -46,16 +46,17 @@ class InvalidRequestCause extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param SubmissionIdNotFound $submissionIdNotFound * @return InvalidRequestCause */ - public static function submissionIdNotFound(SubmissionIdNotFound $submissionIdNotFound): InvalidRequestCause { + public static function submissionIdNotFound(SubmissionIdNotFound $submissionIdNotFound): InvalidRequestCause + { return new InvalidRequestCause([ 'type' => 'submissionIdNotFound', 'value' => $submissionIdNotFound, @@ -66,7 +67,8 @@ public static function submissionIdNotFound(SubmissionIdNotFound $submissionIdNo * @param CustomTestCasesUnsupported $customTestCasesUnsupported * @return InvalidRequestCause */ - public static function customTestCasesUnsupported(CustomTestCasesUnsupported $customTestCasesUnsupported): InvalidRequestCause { + public static function customTestCasesUnsupported(CustomTestCasesUnsupported $customTestCasesUnsupported): InvalidRequestCause + { return new InvalidRequestCause([ 'type' => 'customTestCasesUnsupported', 'value' => $customTestCasesUnsupported, @@ -77,7 +79,8 @@ public static function customTestCasesUnsupported(CustomTestCasesUnsupported $cu * @param UnexpectedLanguageError $unexpectedLanguage * @return InvalidRequestCause */ - public static function unexpectedLanguage(UnexpectedLanguageError $unexpectedLanguage): InvalidRequestCause { + public static function unexpectedLanguage(UnexpectedLanguageError $unexpectedLanguage): InvalidRequestCause + { return new InvalidRequestCause([ 'type' => 'unexpectedLanguage', 'value' => $unexpectedLanguage, @@ -87,81 +90,89 @@ public static function unexpectedLanguage(UnexpectedLanguageError $unexpectedLan /** * @return bool */ - public function isSubmissionIdNotFound(): bool { - return $this->value instanceof SubmissionIdNotFound&& $this->type === 'submissionIdNotFound'; + public function isSubmissionIdNotFound(): bool + { + return $this->value instanceof SubmissionIdNotFound && $this->type === 'submissionIdNotFound'; } /** * @return SubmissionIdNotFound */ - public function asSubmissionIdNotFound(): SubmissionIdNotFound { - if (!($this->value instanceof SubmissionIdNotFound&& $this->type === 'submissionIdNotFound')){ + public function asSubmissionIdNotFound(): SubmissionIdNotFound + { + if (!($this->value instanceof SubmissionIdNotFound && $this->type === 'submissionIdNotFound')) { throw new Exception( "Expected submissionIdNotFound; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isCustomTestCasesUnsupported(): bool { - return $this->value instanceof CustomTestCasesUnsupported&& $this->type === 'customTestCasesUnsupported'; + public function isCustomTestCasesUnsupported(): bool + { + return $this->value instanceof CustomTestCasesUnsupported && $this->type === 'customTestCasesUnsupported'; } /** * @return CustomTestCasesUnsupported */ - public function asCustomTestCasesUnsupported(): CustomTestCasesUnsupported { - if (!($this->value instanceof CustomTestCasesUnsupported&& $this->type === 'customTestCasesUnsupported')){ + public function asCustomTestCasesUnsupported(): CustomTestCasesUnsupported + { + if (!($this->value instanceof CustomTestCasesUnsupported && $this->type === 'customTestCasesUnsupported')) { throw new Exception( "Expected customTestCasesUnsupported; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isUnexpectedLanguage(): bool { - return $this->value instanceof UnexpectedLanguageError&& $this->type === 'unexpectedLanguage'; + public function isUnexpectedLanguage(): bool + { + return $this->value instanceof UnexpectedLanguageError && $this->type === 'unexpectedLanguage'; } /** * @return UnexpectedLanguageError */ - public function asUnexpectedLanguage(): UnexpectedLanguageError { - if (!($this->value instanceof UnexpectedLanguageError&& $this->type === 'unexpectedLanguage')){ + public function asUnexpectedLanguage(): UnexpectedLanguageError + { + if (!($this->value instanceof UnexpectedLanguageError && $this->type === 'unexpectedLanguage')) { throw new Exception( "Expected unexpectedLanguage; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'submissionIdNotFound': $value = $this->asSubmissionIdNotFound()->jsonSerialize(); $result = array_merge($value, $result); @@ -176,26 +187,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -204,22 +216,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'submissionIdNotFound': $args['value'] = SubmissionIdNotFound::jsonDeserialize($data); break; @@ -234,7 +247,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/trace/src/Submission/InvalidRequestResponse.php b/seed/php-model/trace/src/Submission/InvalidRequestResponse.php index 1f20a0c4d395..d971ed240bdf 100644 --- a/seed/php-model/trace/src/Submission/InvalidRequestResponse.php +++ b/seed/php-model/trace/src/Submission/InvalidRequestResponse.php @@ -27,15 +27,16 @@ class InvalidRequestResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->request = $values['request'];$this->cause = $values['cause']; + ) { + $this->request = $values['request']; + $this->cause = $values['cause']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/LightweightStackframeInformation.php b/seed/php-model/trace/src/Submission/LightweightStackframeInformation.php index 66b83ba89642..a3f934992c6b 100644 --- a/seed/php-model/trace/src/Submission/LightweightStackframeInformation.php +++ b/seed/php-model/trace/src/Submission/LightweightStackframeInformation.php @@ -27,15 +27,16 @@ class LightweightStackframeInformation extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->numStackFrames = $values['numStackFrames'];$this->topStackFrameMethodName = $values['topStackFrameMethodName']; + ) { + $this->numStackFrames = $values['numStackFrames']; + $this->topStackFrameMethodName = $values['topStackFrameMethodName']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/RecordedResponseNotification.php b/seed/php-model/trace/src/Submission/RecordedResponseNotification.php index f7b4e9120cc2..92156739e57c 100644 --- a/seed/php-model/trace/src/Submission/RecordedResponseNotification.php +++ b/seed/php-model/trace/src/Submission/RecordedResponseNotification.php @@ -34,15 +34,17 @@ class RecordedResponseNotification extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->submissionId = $values['submissionId'];$this->traceResponsesSize = $values['traceResponsesSize'];$this->testCaseId = $values['testCaseId'] ?? null; + ) { + $this->submissionId = $values['submissionId']; + $this->traceResponsesSize = $values['traceResponsesSize']; + $this->testCaseId = $values['testCaseId'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/RecordedTestCaseUpdate.php b/seed/php-model/trace/src/Submission/RecordedTestCaseUpdate.php index 564357094b49..36bae1d8bd6d 100644 --- a/seed/php-model/trace/src/Submission/RecordedTestCaseUpdate.php +++ b/seed/php-model/trace/src/Submission/RecordedTestCaseUpdate.php @@ -27,15 +27,16 @@ class RecordedTestCaseUpdate extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->testCaseId = $values['testCaseId'];$this->traceResponsesSize = $values['traceResponsesSize']; + ) { + $this->testCaseId = $values['testCaseId']; + $this->traceResponsesSize = $values['traceResponsesSize']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/RecordingResponseNotification.php b/seed/php-model/trace/src/Submission/RecordingResponseNotification.php index ce0f8abe1849..7363812599ef 100644 --- a/seed/php-model/trace/src/Submission/RecordingResponseNotification.php +++ b/seed/php-model/trace/src/Submission/RecordingResponseNotification.php @@ -48,15 +48,19 @@ class RecordingResponseNotification extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->submissionId = $values['submissionId'];$this->testCaseId = $values['testCaseId'] ?? null;$this->lineNumber = $values['lineNumber'];$this->lightweightStackInfo = $values['lightweightStackInfo'];$this->tracedFile = $values['tracedFile'] ?? null; + ) { + $this->submissionId = $values['submissionId']; + $this->testCaseId = $values['testCaseId'] ?? null; + $this->lineNumber = $values['lineNumber']; + $this->lightweightStackInfo = $values['lightweightStackInfo']; + $this->tracedFile = $values['tracedFile'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/RunningResponse.php b/seed/php-model/trace/src/Submission/RunningResponse.php index 1798465dcf03..fb8be8070ecb 100644 --- a/seed/php-model/trace/src/Submission/RunningResponse.php +++ b/seed/php-model/trace/src/Submission/RunningResponse.php @@ -27,15 +27,16 @@ class RunningResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->submissionId = $values['submissionId'];$this->state = $values['state']; + ) { + $this->submissionId = $values['submissionId']; + $this->state = $values['state']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/RunningSubmissionState.php b/seed/php-model/trace/src/Submission/RunningSubmissionState.php index f95303eeb10d..5d107855d508 100644 --- a/seed/php-model/trace/src/Submission/RunningSubmissionState.php +++ b/seed/php-model/trace/src/Submission/RunningSubmissionState.php @@ -2,8 +2,8 @@ namespace Seed\Submission; -enum RunningSubmissionState - : string { +enum RunningSubmissionState: string +{ case QueueingSubmission = "QUEUEING_SUBMISSION"; case KillingHistoricalSubmissions = "KILLING_HISTORICAL_SUBMISSIONS"; case WritingSubmissionToFile = "WRITING_SUBMISSION_TO_FILE"; diff --git a/seed/php-model/trace/src/Submission/RuntimeError.php b/seed/php-model/trace/src/Submission/RuntimeError.php index 64f3d7cf53f1..1c59d452422c 100644 --- a/seed/php-model/trace/src/Submission/RuntimeError.php +++ b/seed/php-model/trace/src/Submission/RuntimeError.php @@ -20,15 +20,15 @@ class RuntimeError extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->message = $values['message']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/Scope.php b/seed/php-model/trace/src/Submission/Scope.php index 36161b3a8e10..5f112404c2be 100644 --- a/seed/php-model/trace/src/Submission/Scope.php +++ b/seed/php-model/trace/src/Submission/Scope.php @@ -22,15 +22,15 @@ class Scope extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->variables = $values['variables']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/StackFrame.php b/seed/php-model/trace/src/Submission/StackFrame.php index 5cc43ff37819..7a7572dac761 100644 --- a/seed/php-model/trace/src/Submission/StackFrame.php +++ b/seed/php-model/trace/src/Submission/StackFrame.php @@ -35,15 +35,17 @@ class StackFrame extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->methodName = $values['methodName'];$this->lineNumber = $values['lineNumber'];$this->scopes = $values['scopes']; + ) { + $this->methodName = $values['methodName']; + $this->lineNumber = $values['lineNumber']; + $this->scopes = $values['scopes']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/StackInformation.php b/seed/php-model/trace/src/Submission/StackInformation.php index 6cc79c50d73e..c49ae1c26e3e 100644 --- a/seed/php-model/trace/src/Submission/StackInformation.php +++ b/seed/php-model/trace/src/Submission/StackInformation.php @@ -27,15 +27,16 @@ class StackInformation extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->numStackFrames = $values['numStackFrames'];$this->topStackFrame = $values['topStackFrame'] ?? null; + ) { + $this->numStackFrames = $values['numStackFrames']; + $this->topStackFrame = $values['topStackFrame'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/StderrResponse.php b/seed/php-model/trace/src/Submission/StderrResponse.php index fa183816f653..91f248090b05 100644 --- a/seed/php-model/trace/src/Submission/StderrResponse.php +++ b/seed/php-model/trace/src/Submission/StderrResponse.php @@ -27,15 +27,16 @@ class StderrResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->submissionId = $values['submissionId'];$this->stderr = $values['stderr']; + ) { + $this->submissionId = $values['submissionId']; + $this->stderr = $values['stderr']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/StdoutResponse.php b/seed/php-model/trace/src/Submission/StdoutResponse.php index 455c0a72465e..197fda7ed6ea 100644 --- a/seed/php-model/trace/src/Submission/StdoutResponse.php +++ b/seed/php-model/trace/src/Submission/StdoutResponse.php @@ -27,15 +27,16 @@ class StdoutResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->submissionId = $values['submissionId'];$this->stdout = $values['stdout']; + ) { + $this->submissionId = $values['submissionId']; + $this->stdout = $values['stdout']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/StopRequest.php b/seed/php-model/trace/src/Submission/StopRequest.php index 606bc2a9987c..c2733fb80d29 100644 --- a/seed/php-model/trace/src/Submission/StopRequest.php +++ b/seed/php-model/trace/src/Submission/StopRequest.php @@ -20,15 +20,15 @@ class StopRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->submissionId = $values['submissionId']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/StoppedResponse.php b/seed/php-model/trace/src/Submission/StoppedResponse.php index 240302bedeb6..5bd8843c488e 100644 --- a/seed/php-model/trace/src/Submission/StoppedResponse.php +++ b/seed/php-model/trace/src/Submission/StoppedResponse.php @@ -20,15 +20,15 @@ class StoppedResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->submissionId = $values['submissionId']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/SubmissionFileInfo.php b/seed/php-model/trace/src/Submission/SubmissionFileInfo.php index 6e977ecd82aa..6adc35801747 100644 --- a/seed/php-model/trace/src/Submission/SubmissionFileInfo.php +++ b/seed/php-model/trace/src/Submission/SubmissionFileInfo.php @@ -34,15 +34,17 @@ class SubmissionFileInfo extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->directory = $values['directory'];$this->filename = $values['filename'];$this->contents = $values['contents']; + ) { + $this->directory = $values['directory']; + $this->filename = $values['filename']; + $this->contents = $values['contents']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/SubmissionIdNotFound.php b/seed/php-model/trace/src/Submission/SubmissionIdNotFound.php index 2d709cece1e8..d431da1ba94b 100644 --- a/seed/php-model/trace/src/Submission/SubmissionIdNotFound.php +++ b/seed/php-model/trace/src/Submission/SubmissionIdNotFound.php @@ -20,15 +20,15 @@ class SubmissionIdNotFound extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->missingSubmissionId = $values['missingSubmissionId']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/SubmissionRequest.php b/seed/php-model/trace/src/Submission/SubmissionRequest.php index c72533984e48..7a263183e135 100644 --- a/seed/php-model/trace/src/Submission/SubmissionRequest.php +++ b/seed/php-model/trace/src/Submission/SubmissionRequest.php @@ -54,16 +54,17 @@ class SubmissionRequest extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param InitializeProblemRequest $initializeProblemRequest * @return SubmissionRequest */ - public static function initializeProblemRequest(InitializeProblemRequest $initializeProblemRequest): SubmissionRequest { + public static function initializeProblemRequest(InitializeProblemRequest $initializeProblemRequest): SubmissionRequest + { return new SubmissionRequest([ 'type' => 'initializeProblemRequest', 'value' => $initializeProblemRequest, @@ -73,7 +74,8 @@ public static function initializeProblemRequest(InitializeProblemRequest $initia /** * @return SubmissionRequest */ - public static function initializeWorkspaceRequest(): SubmissionRequest { + public static function initializeWorkspaceRequest(): SubmissionRequest + { return new SubmissionRequest([ 'type' => 'initializeWorkspaceRequest', 'value' => null, @@ -84,7 +86,8 @@ public static function initializeWorkspaceRequest(): SubmissionRequest { * @param SubmitRequestV2 $submitV2 * @return SubmissionRequest */ - public static function submitV2(SubmitRequestV2 $submitV2): SubmissionRequest { + public static function submitV2(SubmitRequestV2 $submitV2): SubmissionRequest + { return new SubmissionRequest([ 'type' => 'submitV2', 'value' => $submitV2, @@ -95,7 +98,8 @@ public static function submitV2(SubmitRequestV2 $submitV2): SubmissionRequest { * @param WorkspaceSubmitRequest $workspaceSubmit * @return SubmissionRequest */ - public static function workspaceSubmit(WorkspaceSubmitRequest $workspaceSubmit): SubmissionRequest { + public static function workspaceSubmit(WorkspaceSubmitRequest $workspaceSubmit): SubmissionRequest + { return new SubmissionRequest([ 'type' => 'workspaceSubmit', 'value' => $workspaceSubmit, @@ -106,7 +110,8 @@ public static function workspaceSubmit(WorkspaceSubmitRequest $workspaceSubmit): * @param StopRequest $stop * @return SubmissionRequest */ - public static function stop(StopRequest $stop): SubmissionRequest { + public static function stop(StopRequest $stop): SubmissionRequest + { return new SubmissionRequest([ 'type' => 'stop', 'value' => $stop, @@ -116,108 +121,119 @@ public static function stop(StopRequest $stop): SubmissionRequest { /** * @return bool */ - public function isInitializeProblemRequest(): bool { - return $this->value instanceof InitializeProblemRequest&& $this->type === 'initializeProblemRequest'; + public function isInitializeProblemRequest(): bool + { + return $this->value instanceof InitializeProblemRequest && $this->type === 'initializeProblemRequest'; } /** * @return InitializeProblemRequest */ - public function asInitializeProblemRequest(): InitializeProblemRequest { - if (!($this->value instanceof InitializeProblemRequest&& $this->type === 'initializeProblemRequest')){ + public function asInitializeProblemRequest(): InitializeProblemRequest + { + if (!($this->value instanceof InitializeProblemRequest && $this->type === 'initializeProblemRequest')) { throw new Exception( "Expected initializeProblemRequest; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isInitializeWorkspaceRequest(): bool { - return is_null($this->value)&& $this->type === 'initializeWorkspaceRequest'; + public function isInitializeWorkspaceRequest(): bool + { + return is_null($this->value) && $this->type === 'initializeWorkspaceRequest'; } /** * @return bool */ - public function isSubmitV2(): bool { - return $this->value instanceof SubmitRequestV2&& $this->type === 'submitV2'; + public function isSubmitV2(): bool + { + return $this->value instanceof SubmitRequestV2 && $this->type === 'submitV2'; } /** * @return SubmitRequestV2 */ - public function asSubmitV2(): SubmitRequestV2 { - if (!($this->value instanceof SubmitRequestV2&& $this->type === 'submitV2')){ + public function asSubmitV2(): SubmitRequestV2 + { + if (!($this->value instanceof SubmitRequestV2 && $this->type === 'submitV2')) { throw new Exception( "Expected submitV2; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isWorkspaceSubmit(): bool { - return $this->value instanceof WorkspaceSubmitRequest&& $this->type === 'workspaceSubmit'; + public function isWorkspaceSubmit(): bool + { + return $this->value instanceof WorkspaceSubmitRequest && $this->type === 'workspaceSubmit'; } /** * @return WorkspaceSubmitRequest */ - public function asWorkspaceSubmit(): WorkspaceSubmitRequest { - if (!($this->value instanceof WorkspaceSubmitRequest&& $this->type === 'workspaceSubmit')){ + public function asWorkspaceSubmit(): WorkspaceSubmitRequest + { + if (!($this->value instanceof WorkspaceSubmitRequest && $this->type === 'workspaceSubmit')) { throw new Exception( "Expected workspaceSubmit; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isStop(): bool { - return $this->value instanceof StopRequest&& $this->type === 'stop'; + public function isStop(): bool + { + return $this->value instanceof StopRequest && $this->type === 'stop'; } /** * @return StopRequest */ - public function asStop(): StopRequest { - if (!($this->value instanceof StopRequest&& $this->type === 'stop')){ + public function asStop(): StopRequest + { + if (!($this->value instanceof StopRequest && $this->type === 'stop')) { throw new Exception( "Expected stop; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'initializeProblemRequest': $value = $this->asInitializeProblemRequest()->jsonSerialize(); $result = array_merge($value, $result); @@ -239,26 +255,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -267,22 +284,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'initializeProblemRequest': $args['value'] = InitializeProblemRequest::jsonDeserialize($data); break; @@ -303,7 +321,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/trace/src/Submission/SubmissionResponse.php b/seed/php-model/trace/src/Submission/SubmissionResponse.php index bbdfc4b6cced..918542e1035a 100644 --- a/seed/php-model/trace/src/Submission/SubmissionResponse.php +++ b/seed/php-model/trace/src/Submission/SubmissionResponse.php @@ -56,15 +56,16 @@ class SubmissionResponse extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @return SubmissionResponse */ - public static function serverInitialized(): SubmissionResponse { + public static function serverInitialized(): SubmissionResponse + { return new SubmissionResponse([ 'type' => 'serverInitialized', 'value' => null, @@ -75,7 +76,8 @@ public static function serverInitialized(): SubmissionResponse { * @param string $problemInitialized * @return SubmissionResponse */ - public static function problemInitialized(string $problemInitialized): SubmissionResponse { + public static function problemInitialized(string $problemInitialized): SubmissionResponse + { return new SubmissionResponse([ 'type' => 'problemInitialized', 'value' => $problemInitialized, @@ -85,7 +87,8 @@ public static function problemInitialized(string $problemInitialized): Submissio /** * @return SubmissionResponse */ - public static function workspaceInitialized(): SubmissionResponse { + public static function workspaceInitialized(): SubmissionResponse + { return new SubmissionResponse([ 'type' => 'workspaceInitialized', 'value' => null, @@ -96,7 +99,8 @@ public static function workspaceInitialized(): SubmissionResponse { * @param ExceptionInfo $serverErrored * @return SubmissionResponse */ - public static function serverErrored(ExceptionInfo $serverErrored): SubmissionResponse { + public static function serverErrored(ExceptionInfo $serverErrored): SubmissionResponse + { return new SubmissionResponse([ 'type' => 'serverErrored', 'value' => $serverErrored, @@ -107,7 +111,8 @@ public static function serverErrored(ExceptionInfo $serverErrored): SubmissionRe * @param CodeExecutionUpdate $codeExecutionUpdate * @return SubmissionResponse */ - public static function codeExecutionUpdate(CodeExecutionUpdate $codeExecutionUpdate): SubmissionResponse { + public static function codeExecutionUpdate(CodeExecutionUpdate $codeExecutionUpdate): SubmissionResponse + { return new SubmissionResponse([ 'type' => 'codeExecutionUpdate', 'value' => $codeExecutionUpdate, @@ -118,7 +123,8 @@ public static function codeExecutionUpdate(CodeExecutionUpdate $codeExecutionUpd * @param TerminatedResponse $terminated * @return SubmissionResponse */ - public static function terminated(TerminatedResponse $terminated): SubmissionResponse { + public static function terminated(TerminatedResponse $terminated): SubmissionResponse + { return new SubmissionResponse([ 'type' => 'terminated', 'value' => $terminated, @@ -128,115 +134,127 @@ public static function terminated(TerminatedResponse $terminated): SubmissionRes /** * @return bool */ - public function isServerInitialized(): bool { - return is_null($this->value)&& $this->type === 'serverInitialized'; + public function isServerInitialized(): bool + { + return is_null($this->value) && $this->type === 'serverInitialized'; } /** * @return bool */ - public function isProblemInitialized(): bool { - return is_string($this->value)&& $this->type === 'problemInitialized'; + public function isProblemInitialized(): bool + { + return is_string($this->value) && $this->type === 'problemInitialized'; } /** * @return string */ - public function asProblemInitialized(): string { - if (!(is_string($this->value)&& $this->type === 'problemInitialized')){ + public function asProblemInitialized(): string + { + if (!(is_string($this->value) && $this->type === 'problemInitialized')) { throw new Exception( "Expected problemInitialized; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isWorkspaceInitialized(): bool { - return is_null($this->value)&& $this->type === 'workspaceInitialized'; + public function isWorkspaceInitialized(): bool + { + return is_null($this->value) && $this->type === 'workspaceInitialized'; } /** * @return bool */ - public function isServerErrored(): bool { - return $this->value instanceof ExceptionInfo&& $this->type === 'serverErrored'; + public function isServerErrored(): bool + { + return $this->value instanceof ExceptionInfo && $this->type === 'serverErrored'; } /** * @return ExceptionInfo */ - public function asServerErrored(): ExceptionInfo { - if (!($this->value instanceof ExceptionInfo&& $this->type === 'serverErrored')){ + public function asServerErrored(): ExceptionInfo + { + if (!($this->value instanceof ExceptionInfo && $this->type === 'serverErrored')) { throw new Exception( "Expected serverErrored; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isCodeExecutionUpdate(): bool { - return $this->value instanceof CodeExecutionUpdate&& $this->type === 'codeExecutionUpdate'; + public function isCodeExecutionUpdate(): bool + { + return $this->value instanceof CodeExecutionUpdate && $this->type === 'codeExecutionUpdate'; } /** * @return CodeExecutionUpdate */ - public function asCodeExecutionUpdate(): CodeExecutionUpdate { - if (!($this->value instanceof CodeExecutionUpdate&& $this->type === 'codeExecutionUpdate')){ + public function asCodeExecutionUpdate(): CodeExecutionUpdate + { + if (!($this->value instanceof CodeExecutionUpdate && $this->type === 'codeExecutionUpdate')) { throw new Exception( "Expected codeExecutionUpdate; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isTerminated(): bool { - return $this->value instanceof TerminatedResponse&& $this->type === 'terminated'; + public function isTerminated(): bool + { + return $this->value instanceof TerminatedResponse && $this->type === 'terminated'; } /** * @return TerminatedResponse */ - public function asTerminated(): TerminatedResponse { - if (!($this->value instanceof TerminatedResponse&& $this->type === 'terminated')){ + public function asTerminated(): TerminatedResponse + { + if (!($this->value instanceof TerminatedResponse && $this->type === 'terminated')) { throw new Exception( "Expected terminated; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'serverInitialized': $result['serverInitialized'] = []; break; @@ -261,26 +279,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -289,32 +308,33 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'serverInitialized': $args['value'] = null; break; case 'problemInitialized': - if (!array_key_exists('problemInitialized', $data)){ + if (!array_key_exists('problemInitialized', $data)) { throw new Exception( "JSON data is missing property 'problemInitialized'", ); } - + $args['value'] = $data['problemInitialized']; break; case 'workspaceInitialized': @@ -324,13 +344,13 @@ public static function jsonDeserialize(array $data): static { $args['value'] = ExceptionInfo::jsonDeserialize($data); break; case 'codeExecutionUpdate': - if (!array_key_exists('codeExecutionUpdate', $data)){ + if (!array_key_exists('codeExecutionUpdate', $data)) { throw new Exception( "JSON data is missing property 'codeExecutionUpdate'", ); } - - if (!(is_array($data['codeExecutionUpdate']))){ + + if (!(is_array($data['codeExecutionUpdate']))) { throw new Exception( "Expected property 'codeExecutionUpdate' in JSON data to be array, instead received " . get_debug_type($data['codeExecutionUpdate']), ); @@ -345,7 +365,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/trace/src/Submission/SubmissionStatusForTestCase.php b/seed/php-model/trace/src/Submission/SubmissionStatusForTestCase.php index 8e41f117f3a9..d694a175f45c 100644 --- a/seed/php-model/trace/src/Submission/SubmissionStatusForTestCase.php +++ b/seed/php-model/trace/src/Submission/SubmissionStatusForTestCase.php @@ -46,16 +46,17 @@ class SubmissionStatusForTestCase extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param TestCaseResultWithStdout $graded * @return SubmissionStatusForTestCase */ - public static function graded(TestCaseResultWithStdout $graded): SubmissionStatusForTestCase { + public static function graded(TestCaseResultWithStdout $graded): SubmissionStatusForTestCase + { return new SubmissionStatusForTestCase([ 'type' => 'graded', 'value' => $graded, @@ -66,7 +67,8 @@ public static function graded(TestCaseResultWithStdout $graded): SubmissionStatu * @param TestCaseGrade $gradedV2 * @return SubmissionStatusForTestCase */ - public static function gradedV2(TestCaseGrade $gradedV2): SubmissionStatusForTestCase { + public static function gradedV2(TestCaseGrade $gradedV2): SubmissionStatusForTestCase + { return new SubmissionStatusForTestCase([ 'type' => 'gradedV2', 'value' => $gradedV2, @@ -77,7 +79,8 @@ public static function gradedV2(TestCaseGrade $gradedV2): SubmissionStatusForTes * @param TracedTestCase $traced * @return SubmissionStatusForTestCase */ - public static function traced(TracedTestCase $traced): SubmissionStatusForTestCase { + public static function traced(TracedTestCase $traced): SubmissionStatusForTestCase + { return new SubmissionStatusForTestCase([ 'type' => 'traced', 'value' => $traced, @@ -87,81 +90,89 @@ public static function traced(TracedTestCase $traced): SubmissionStatusForTestCa /** * @return bool */ - public function isGraded(): bool { - return $this->value instanceof TestCaseResultWithStdout&& $this->type === 'graded'; + public function isGraded(): bool + { + return $this->value instanceof TestCaseResultWithStdout && $this->type === 'graded'; } /** * @return TestCaseResultWithStdout */ - public function asGraded(): TestCaseResultWithStdout { - if (!($this->value instanceof TestCaseResultWithStdout&& $this->type === 'graded')){ + public function asGraded(): TestCaseResultWithStdout + { + if (!($this->value instanceof TestCaseResultWithStdout && $this->type === 'graded')) { throw new Exception( "Expected graded; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isGradedV2(): bool { - return $this->value instanceof TestCaseGrade&& $this->type === 'gradedV2'; + public function isGradedV2(): bool + { + return $this->value instanceof TestCaseGrade && $this->type === 'gradedV2'; } /** * @return TestCaseGrade */ - public function asGradedV2(): TestCaseGrade { - if (!($this->value instanceof TestCaseGrade&& $this->type === 'gradedV2')){ + public function asGradedV2(): TestCaseGrade + { + if (!($this->value instanceof TestCaseGrade && $this->type === 'gradedV2')) { throw new Exception( "Expected gradedV2; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isTraced(): bool { - return $this->value instanceof TracedTestCase&& $this->type === 'traced'; + public function isTraced(): bool + { + return $this->value instanceof TracedTestCase && $this->type === 'traced'; } /** * @return TracedTestCase */ - public function asTraced(): TracedTestCase { - if (!($this->value instanceof TracedTestCase&& $this->type === 'traced')){ + public function asTraced(): TracedTestCase + { + if (!($this->value instanceof TracedTestCase && $this->type === 'traced')) { throw new Exception( "Expected traced; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'graded': $value = $this->asGraded()->jsonSerialize(); $result = array_merge($value, $result); @@ -176,26 +187,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -204,33 +216,34 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'graded': $args['value'] = TestCaseResultWithStdout::jsonDeserialize($data); break; case 'gradedV2': - if (!array_key_exists('gradedV2', $data)){ + if (!array_key_exists('gradedV2', $data)) { throw new Exception( "JSON data is missing property 'gradedV2'", ); } - - if (!(is_array($data['gradedV2']))){ + + if (!(is_array($data['gradedV2']))) { throw new Exception( "Expected property 'gradedV2' in JSON data to be array, instead received " . get_debug_type($data['gradedV2']), ); @@ -245,7 +258,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/trace/src/Submission/SubmissionStatusV2.php b/seed/php-model/trace/src/Submission/SubmissionStatusV2.php index a9b7565d8ab4..558047167775 100644 --- a/seed/php-model/trace/src/Submission/SubmissionStatusV2.php +++ b/seed/php-model/trace/src/Submission/SubmissionStatusV2.php @@ -42,16 +42,17 @@ class SubmissionStatusV2 extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param TestSubmissionStatusV2 $test * @return SubmissionStatusV2 */ - public static function test(TestSubmissionStatusV2 $test): SubmissionStatusV2 { + public static function test(TestSubmissionStatusV2 $test): SubmissionStatusV2 + { return new SubmissionStatusV2([ 'type' => 'test', 'value' => $test, @@ -62,7 +63,8 @@ public static function test(TestSubmissionStatusV2 $test): SubmissionStatusV2 { * @param WorkspaceSubmissionStatusV2 $workspace * @return SubmissionStatusV2 */ - public static function workspace(WorkspaceSubmissionStatusV2 $workspace): SubmissionStatusV2 { + public static function workspace(WorkspaceSubmissionStatusV2 $workspace): SubmissionStatusV2 + { return new SubmissionStatusV2([ 'type' => 'workspace', 'value' => $workspace, @@ -72,61 +74,67 @@ public static function workspace(WorkspaceSubmissionStatusV2 $workspace): Submis /** * @return bool */ - public function isTest(): bool { - return $this->value instanceof TestSubmissionStatusV2&& $this->type === 'test'; + public function isTest(): bool + { + return $this->value instanceof TestSubmissionStatusV2 && $this->type === 'test'; } /** * @return TestSubmissionStatusV2 */ - public function asTest(): TestSubmissionStatusV2 { - if (!($this->value instanceof TestSubmissionStatusV2&& $this->type === 'test')){ + public function asTest(): TestSubmissionStatusV2 + { + if (!($this->value instanceof TestSubmissionStatusV2 && $this->type === 'test')) { throw new Exception( "Expected test; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isWorkspace(): bool { - return $this->value instanceof WorkspaceSubmissionStatusV2&& $this->type === 'workspace'; + public function isWorkspace(): bool + { + return $this->value instanceof WorkspaceSubmissionStatusV2 && $this->type === 'workspace'; } /** * @return WorkspaceSubmissionStatusV2 */ - public function asWorkspace(): WorkspaceSubmissionStatusV2 { - if (!($this->value instanceof WorkspaceSubmissionStatusV2&& $this->type === 'workspace')){ + public function asWorkspace(): WorkspaceSubmissionStatusV2 + { + if (!($this->value instanceof WorkspaceSubmissionStatusV2 && $this->type === 'workspace')) { throw new Exception( "Expected workspace; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'test': $value = $this->asTest()->jsonSerialize(); $result = array_merge($value, $result); @@ -137,26 +145,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -165,22 +174,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'test': $args['value'] = TestSubmissionStatusV2::jsonDeserialize($data); break; @@ -192,7 +202,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/trace/src/Submission/SubmissionTypeEnum.php b/seed/php-model/trace/src/Submission/SubmissionTypeEnum.php index 482f19640f60..6c3118411754 100644 --- a/seed/php-model/trace/src/Submission/SubmissionTypeEnum.php +++ b/seed/php-model/trace/src/Submission/SubmissionTypeEnum.php @@ -2,7 +2,7 @@ namespace Seed\Submission; -enum SubmissionTypeEnum - : string { +enum SubmissionTypeEnum: string +{ case Test = "TEST"; } diff --git a/seed/php-model/trace/src/Submission/SubmissionTypeState.php b/seed/php-model/trace/src/Submission/SubmissionTypeState.php index 7cc878b8ae83..b803738abfdc 100644 --- a/seed/php-model/trace/src/Submission/SubmissionTypeState.php +++ b/seed/php-model/trace/src/Submission/SubmissionTypeState.php @@ -42,16 +42,17 @@ class SubmissionTypeState extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param TestSubmissionState $test * @return SubmissionTypeState */ - public static function test(TestSubmissionState $test): SubmissionTypeState { + public static function test(TestSubmissionState $test): SubmissionTypeState + { return new SubmissionTypeState([ 'type' => 'test', 'value' => $test, @@ -62,7 +63,8 @@ public static function test(TestSubmissionState $test): SubmissionTypeState { * @param WorkspaceSubmissionState $workspace * @return SubmissionTypeState */ - public static function workspace(WorkspaceSubmissionState $workspace): SubmissionTypeState { + public static function workspace(WorkspaceSubmissionState $workspace): SubmissionTypeState + { return new SubmissionTypeState([ 'type' => 'workspace', 'value' => $workspace, @@ -72,61 +74,67 @@ public static function workspace(WorkspaceSubmissionState $workspace): Submissio /** * @return bool */ - public function isTest(): bool { - return $this->value instanceof TestSubmissionState&& $this->type === 'test'; + public function isTest(): bool + { + return $this->value instanceof TestSubmissionState && $this->type === 'test'; } /** * @return TestSubmissionState */ - public function asTest(): TestSubmissionState { - if (!($this->value instanceof TestSubmissionState&& $this->type === 'test')){ + public function asTest(): TestSubmissionState + { + if (!($this->value instanceof TestSubmissionState && $this->type === 'test')) { throw new Exception( "Expected test; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isWorkspace(): bool { - return $this->value instanceof WorkspaceSubmissionState&& $this->type === 'workspace'; + public function isWorkspace(): bool + { + return $this->value instanceof WorkspaceSubmissionState && $this->type === 'workspace'; } /** * @return WorkspaceSubmissionState */ - public function asWorkspace(): WorkspaceSubmissionState { - if (!($this->value instanceof WorkspaceSubmissionState&& $this->type === 'workspace')){ + public function asWorkspace(): WorkspaceSubmissionState + { + if (!($this->value instanceof WorkspaceSubmissionState && $this->type === 'workspace')) { throw new Exception( "Expected workspace; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'test': $value = $this->asTest()->jsonSerialize(); $result = array_merge($value, $result); @@ -137,26 +145,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -165,22 +174,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'test': $args['value'] = TestSubmissionState::jsonDeserialize($data); break; @@ -192,7 +202,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/trace/src/Submission/SubmitRequestV2.php b/seed/php-model/trace/src/Submission/SubmitRequestV2.php index 676e09a2aeff..cf8ac5143f3a 100644 --- a/seed/php-model/trace/src/Submission/SubmitRequestV2.php +++ b/seed/php-model/trace/src/Submission/SubmitRequestV2.php @@ -57,15 +57,20 @@ class SubmitRequestV2 extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->submissionId = $values['submissionId'];$this->language = $values['language'];$this->submissionFiles = $values['submissionFiles'];$this->problemId = $values['problemId'];$this->problemVersion = $values['problemVersion'] ?? null;$this->userId = $values['userId'] ?? null; + ) { + $this->submissionId = $values['submissionId']; + $this->language = $values['language']; + $this->submissionFiles = $values['submissionFiles']; + $this->problemId = $values['problemId']; + $this->problemVersion = $values['problemVersion'] ?? null; + $this->userId = $values['userId'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/TerminatedResponse.php b/seed/php-model/trace/src/Submission/TerminatedResponse.php index ba0afd3cf6a0..df15be42da46 100644 --- a/seed/php-model/trace/src/Submission/TerminatedResponse.php +++ b/seed/php-model/trace/src/Submission/TerminatedResponse.php @@ -6,22 +6,21 @@ class TerminatedResponse extends JsonSerializableType { - /** * @param array{ * } $values */ public function __construct( array $values = [], - ) - { + ) { unset($values); } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/TestCaseGrade.php b/seed/php-model/trace/src/Submission/TestCaseGrade.php index 085bc6c2fd53..51a7f659c9aa 100644 --- a/seed/php-model/trace/src/Submission/TestCaseGrade.php +++ b/seed/php-model/trace/src/Submission/TestCaseGrade.php @@ -42,16 +42,17 @@ class TestCaseGrade extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param TestCaseHiddenGrade $hidden * @return TestCaseGrade */ - public static function hidden(TestCaseHiddenGrade $hidden): TestCaseGrade { + public static function hidden(TestCaseHiddenGrade $hidden): TestCaseGrade + { return new TestCaseGrade([ 'type' => 'hidden', 'value' => $hidden, @@ -62,7 +63,8 @@ public static function hidden(TestCaseHiddenGrade $hidden): TestCaseGrade { * @param TestCaseNonHiddenGrade $nonHidden * @return TestCaseGrade */ - public static function nonHidden(TestCaseNonHiddenGrade $nonHidden): TestCaseGrade { + public static function nonHidden(TestCaseNonHiddenGrade $nonHidden): TestCaseGrade + { return new TestCaseGrade([ 'type' => 'nonHidden', 'value' => $nonHidden, @@ -72,61 +74,67 @@ public static function nonHidden(TestCaseNonHiddenGrade $nonHidden): TestCaseGra /** * @return bool */ - public function isHidden(): bool { - return $this->value instanceof TestCaseHiddenGrade&& $this->type === 'hidden'; + public function isHidden(): bool + { + return $this->value instanceof TestCaseHiddenGrade && $this->type === 'hidden'; } /** * @return TestCaseHiddenGrade */ - public function asHidden(): TestCaseHiddenGrade { - if (!($this->value instanceof TestCaseHiddenGrade&& $this->type === 'hidden')){ + public function asHidden(): TestCaseHiddenGrade + { + if (!($this->value instanceof TestCaseHiddenGrade && $this->type === 'hidden')) { throw new Exception( "Expected hidden; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isNonHidden(): bool { - return $this->value instanceof TestCaseNonHiddenGrade&& $this->type === 'nonHidden'; + public function isNonHidden(): bool + { + return $this->value instanceof TestCaseNonHiddenGrade && $this->type === 'nonHidden'; } /** * @return TestCaseNonHiddenGrade */ - public function asNonHidden(): TestCaseNonHiddenGrade { - if (!($this->value instanceof TestCaseNonHiddenGrade&& $this->type === 'nonHidden')){ + public function asNonHidden(): TestCaseNonHiddenGrade + { + if (!($this->value instanceof TestCaseNonHiddenGrade && $this->type === 'nonHidden')) { throw new Exception( "Expected nonHidden; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'hidden': $value = $this->asHidden()->jsonSerialize(); $result = array_merge($value, $result); @@ -137,26 +145,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -165,22 +174,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'hidden': $args['value'] = TestCaseHiddenGrade::jsonDeserialize($data); break; @@ -192,7 +202,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/trace/src/Submission/TestCaseHiddenGrade.php b/seed/php-model/trace/src/Submission/TestCaseHiddenGrade.php index 976fe6790f5f..08b421854965 100644 --- a/seed/php-model/trace/src/Submission/TestCaseHiddenGrade.php +++ b/seed/php-model/trace/src/Submission/TestCaseHiddenGrade.php @@ -20,15 +20,15 @@ class TestCaseHiddenGrade extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->passed = $values['passed']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/TestCaseNonHiddenGrade.php b/seed/php-model/trace/src/Submission/TestCaseNonHiddenGrade.php index a2ad4fa57ab0..f9d70caa89de 100644 --- a/seed/php-model/trace/src/Submission/TestCaseNonHiddenGrade.php +++ b/seed/php-model/trace/src/Submission/TestCaseNonHiddenGrade.php @@ -42,15 +42,18 @@ class TestCaseNonHiddenGrade extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->passed = $values['passed'];$this->actualResult = $values['actualResult'] ?? null;$this->exception = $values['exception'] ?? null;$this->stdout = $values['stdout']; + ) { + $this->passed = $values['passed']; + $this->actualResult = $values['actualResult'] ?? null; + $this->exception = $values['exception'] ?? null; + $this->stdout = $values['stdout']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/TestCaseResult.php b/seed/php-model/trace/src/Submission/TestCaseResult.php index 59cd45b5edf5..90f387be19ea 100644 --- a/seed/php-model/trace/src/Submission/TestCaseResult.php +++ b/seed/php-model/trace/src/Submission/TestCaseResult.php @@ -35,15 +35,17 @@ class TestCaseResult extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->expectedResult = $values['expectedResult'];$this->actualResult = $values['actualResult'];$this->passed = $values['passed']; + ) { + $this->expectedResult = $values['expectedResult']; + $this->actualResult = $values['actualResult']; + $this->passed = $values['passed']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/TestCaseResultWithStdout.php b/seed/php-model/trace/src/Submission/TestCaseResultWithStdout.php index 087c6ecdfef3..3328cc86ffd1 100644 --- a/seed/php-model/trace/src/Submission/TestCaseResultWithStdout.php +++ b/seed/php-model/trace/src/Submission/TestCaseResultWithStdout.php @@ -27,15 +27,16 @@ class TestCaseResultWithStdout extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->result = $values['result'];$this->stdout = $values['stdout']; + ) { + $this->result = $values['result']; + $this->stdout = $values['stdout']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/TestSubmissionState.php b/seed/php-model/trace/src/Submission/TestSubmissionState.php index efa742357fe9..3785157f2eb1 100644 --- a/seed/php-model/trace/src/Submission/TestSubmissionState.php +++ b/seed/php-model/trace/src/Submission/TestSubmissionState.php @@ -43,15 +43,18 @@ class TestSubmissionState extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->problemId = $values['problemId'];$this->defaultTestCases = $values['defaultTestCases'];$this->customTestCases = $values['customTestCases'];$this->status = $values['status']; + ) { + $this->problemId = $values['problemId']; + $this->defaultTestCases = $values['defaultTestCases']; + $this->customTestCases = $values['customTestCases']; + $this->status = $values['status']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/TestSubmissionStatus.php b/seed/php-model/trace/src/Submission/TestSubmissionStatus.php index 5bfb02130c86..e92a5a37bdc5 100644 --- a/seed/php-model/trace/src/Submission/TestSubmissionStatus.php +++ b/seed/php-model/trace/src/Submission/TestSubmissionStatus.php @@ -50,15 +50,16 @@ class TestSubmissionStatus extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @return TestSubmissionStatus */ - public static function stopped(): TestSubmissionStatus { + public static function stopped(): TestSubmissionStatus + { return new TestSubmissionStatus([ 'type' => 'stopped', 'value' => null, @@ -69,7 +70,8 @@ public static function stopped(): TestSubmissionStatus { * @param ErrorInfo $errored * @return TestSubmissionStatus */ - public static function errored(ErrorInfo $errored): TestSubmissionStatus { + public static function errored(ErrorInfo $errored): TestSubmissionStatus + { return new TestSubmissionStatus([ 'type' => 'errored', 'value' => $errored, @@ -80,7 +82,8 @@ public static function errored(ErrorInfo $errored): TestSubmissionStatus { * @param value-of $running * @return TestSubmissionStatus */ - public static function running(string $running): TestSubmissionStatus { + public static function running(string $running): TestSubmissionStatus + { return new TestSubmissionStatus([ 'type' => 'running', 'value' => $running, @@ -91,7 +94,8 @@ public static function running(string $running): TestSubmissionStatus { * @param array $testCaseIdToState * @return TestSubmissionStatus */ - public static function testCaseIdToState(array $testCaseIdToState): TestSubmissionStatus { + public static function testCaseIdToState(array $testCaseIdToState): TestSubmissionStatus + { return new TestSubmissionStatus([ 'type' => 'testCaseIdToState', 'value' => $testCaseIdToState, @@ -101,88 +105,97 @@ public static function testCaseIdToState(array $testCaseIdToState): TestSubmissi /** * @return bool */ - public function isStopped(): bool { - return is_null($this->value)&& $this->type === 'stopped'; + public function isStopped(): bool + { + return is_null($this->value) && $this->type === 'stopped'; } /** * @return bool */ - public function isErrored(): bool { - return $this->value instanceof ErrorInfo&& $this->type === 'errored'; + public function isErrored(): bool + { + return $this->value instanceof ErrorInfo && $this->type === 'errored'; } /** * @return ErrorInfo */ - public function asErrored(): ErrorInfo { - if (!($this->value instanceof ErrorInfo&& $this->type === 'errored')){ + public function asErrored(): ErrorInfo + { + if (!($this->value instanceof ErrorInfo && $this->type === 'errored')) { throw new Exception( "Expected errored; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isRunning(): bool { - return $this->value instanceof RunningSubmissionState&& $this->type === 'running'; + public function isRunning(): bool + { + return $this->value instanceof RunningSubmissionState && $this->type === 'running'; } /** * @return value-of */ - public function asRunning(): string { - if (!($this->value instanceof RunningSubmissionState&& $this->type === 'running')){ + public function asRunning(): string + { + if (!($this->value instanceof RunningSubmissionState && $this->type === 'running')) { throw new Exception( "Expected running; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isTestCaseIdToState(): bool { - return is_array($this->value)&& $this->type === 'testCaseIdToState'; + public function isTestCaseIdToState(): bool + { + return is_array($this->value) && $this->type === 'testCaseIdToState'; } /** * @return array */ - public function asTestCaseIdToState(): array { - if (!(is_array($this->value)&& $this->type === 'testCaseIdToState')){ + public function asTestCaseIdToState(): array + { + if (!(is_array($this->value) && $this->type === 'testCaseIdToState')) { throw new Exception( "Expected testCaseIdToState; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'stopped': $result['stopped'] = []; break; @@ -200,26 +213,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -228,33 +242,34 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'stopped': $args['value'] = null; break; case 'errored': - if (!array_key_exists('errored', $data)){ + if (!array_key_exists('errored', $data)) { throw new Exception( "JSON data is missing property 'errored'", ); } - - if (!(is_array($data['errored']))){ + + if (!(is_array($data['errored']))) { throw new Exception( "Expected property 'errored' in JSON data to be array, instead received " . get_debug_type($data['errored']), ); @@ -262,21 +277,21 @@ public static function jsonDeserialize(array $data): static { $args['value'] = ErrorInfo::jsonDeserialize($data['errored']); break; case 'running': - if (!array_key_exists('running', $data)){ + if (!array_key_exists('running', $data)) { throw new Exception( "JSON data is missing property 'running'", ); } - + $args['value'] = $data['running']; break; case 'testCaseIdToState': - if (!array_key_exists('testCaseIdToState', $data)){ + if (!array_key_exists('testCaseIdToState', $data)) { throw new Exception( "JSON data is missing property 'testCaseIdToState'", ); } - + $args['value'] = $data['testCaseIdToState']; break; case '_unknown': @@ -284,7 +299,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/trace/src/Submission/TestSubmissionStatusV2.php b/seed/php-model/trace/src/Submission/TestSubmissionStatusV2.php index 2159ebf38e07..bdf7b3bc2911 100644 --- a/seed/php-model/trace/src/Submission/TestSubmissionStatusV2.php +++ b/seed/php-model/trace/src/Submission/TestSubmissionStatusV2.php @@ -43,15 +43,18 @@ class TestSubmissionStatusV2 extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->updates = $values['updates'];$this->problemId = $values['problemId'];$this->problemVersion = $values['problemVersion'];$this->problemInfo = $values['problemInfo']; + ) { + $this->updates = $values['updates']; + $this->problemId = $values['problemId']; + $this->problemVersion = $values['problemVersion']; + $this->problemInfo = $values['problemInfo']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/TestSubmissionUpdate.php b/seed/php-model/trace/src/Submission/TestSubmissionUpdate.php index 2522f18b127f..aea54bab3913 100644 --- a/seed/php-model/trace/src/Submission/TestSubmissionUpdate.php +++ b/seed/php-model/trace/src/Submission/TestSubmissionUpdate.php @@ -29,15 +29,16 @@ class TestSubmissionUpdate extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->updateTime = $values['updateTime'];$this->updateInfo = $values['updateInfo']; + ) { + $this->updateTime = $values['updateTime']; + $this->updateInfo = $values['updateInfo']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/TestSubmissionUpdateInfo.php b/seed/php-model/trace/src/Submission/TestSubmissionUpdateInfo.php index e370e9a8a314..06afb2525c95 100644 --- a/seed/php-model/trace/src/Submission/TestSubmissionUpdateInfo.php +++ b/seed/php-model/trace/src/Submission/TestSubmissionUpdateInfo.php @@ -56,16 +56,17 @@ class TestSubmissionUpdateInfo extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param value-of $running * @return TestSubmissionUpdateInfo */ - public static function running(string $running): TestSubmissionUpdateInfo { + public static function running(string $running): TestSubmissionUpdateInfo + { return new TestSubmissionUpdateInfo([ 'type' => 'running', 'value' => $running, @@ -75,7 +76,8 @@ public static function running(string $running): TestSubmissionUpdateInfo { /** * @return TestSubmissionUpdateInfo */ - public static function stopped(): TestSubmissionUpdateInfo { + public static function stopped(): TestSubmissionUpdateInfo + { return new TestSubmissionUpdateInfo([ 'type' => 'stopped', 'value' => null, @@ -86,7 +88,8 @@ public static function stopped(): TestSubmissionUpdateInfo { * @param ErrorInfo $errored * @return TestSubmissionUpdateInfo */ - public static function errored(ErrorInfo $errored): TestSubmissionUpdateInfo { + public static function errored(ErrorInfo $errored): TestSubmissionUpdateInfo + { return new TestSubmissionUpdateInfo([ 'type' => 'errored', 'value' => $errored, @@ -97,7 +100,8 @@ public static function errored(ErrorInfo $errored): TestSubmissionUpdateInfo { * @param GradedTestCaseUpdate $gradedTestCase * @return TestSubmissionUpdateInfo */ - public static function gradedTestCase(GradedTestCaseUpdate $gradedTestCase): TestSubmissionUpdateInfo { + public static function gradedTestCase(GradedTestCaseUpdate $gradedTestCase): TestSubmissionUpdateInfo + { return new TestSubmissionUpdateInfo([ 'type' => 'gradedTestCase', 'value' => $gradedTestCase, @@ -108,7 +112,8 @@ public static function gradedTestCase(GradedTestCaseUpdate $gradedTestCase): Tes * @param RecordedTestCaseUpdate $recordedTestCase * @return TestSubmissionUpdateInfo */ - public static function recordedTestCase(RecordedTestCaseUpdate $recordedTestCase): TestSubmissionUpdateInfo { + public static function recordedTestCase(RecordedTestCaseUpdate $recordedTestCase): TestSubmissionUpdateInfo + { return new TestSubmissionUpdateInfo([ 'type' => 'recordedTestCase', 'value' => $recordedTestCase, @@ -118,7 +123,8 @@ public static function recordedTestCase(RecordedTestCaseUpdate $recordedTestCase /** * @return TestSubmissionUpdateInfo */ - public static function finished(): TestSubmissionUpdateInfo { + public static function finished(): TestSubmissionUpdateInfo + { return new TestSubmissionUpdateInfo([ 'type' => 'finished', 'value' => null, @@ -128,115 +134,127 @@ public static function finished(): TestSubmissionUpdateInfo { /** * @return bool */ - public function isRunning(): bool { - return $this->value instanceof RunningSubmissionState&& $this->type === 'running'; + public function isRunning(): bool + { + return $this->value instanceof RunningSubmissionState && $this->type === 'running'; } /** * @return value-of */ - public function asRunning(): string { - if (!($this->value instanceof RunningSubmissionState&& $this->type === 'running')){ + public function asRunning(): string + { + if (!($this->value instanceof RunningSubmissionState && $this->type === 'running')) { throw new Exception( "Expected running; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isStopped(): bool { - return is_null($this->value)&& $this->type === 'stopped'; + public function isStopped(): bool + { + return is_null($this->value) && $this->type === 'stopped'; } /** * @return bool */ - public function isErrored(): bool { - return $this->value instanceof ErrorInfo&& $this->type === 'errored'; + public function isErrored(): bool + { + return $this->value instanceof ErrorInfo && $this->type === 'errored'; } /** * @return ErrorInfo */ - public function asErrored(): ErrorInfo { - if (!($this->value instanceof ErrorInfo&& $this->type === 'errored')){ + public function asErrored(): ErrorInfo + { + if (!($this->value instanceof ErrorInfo && $this->type === 'errored')) { throw new Exception( "Expected errored; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isGradedTestCase(): bool { - return $this->value instanceof GradedTestCaseUpdate&& $this->type === 'gradedTestCase'; + public function isGradedTestCase(): bool + { + return $this->value instanceof GradedTestCaseUpdate && $this->type === 'gradedTestCase'; } /** * @return GradedTestCaseUpdate */ - public function asGradedTestCase(): GradedTestCaseUpdate { - if (!($this->value instanceof GradedTestCaseUpdate&& $this->type === 'gradedTestCase')){ + public function asGradedTestCase(): GradedTestCaseUpdate + { + if (!($this->value instanceof GradedTestCaseUpdate && $this->type === 'gradedTestCase')) { throw new Exception( "Expected gradedTestCase; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isRecordedTestCase(): bool { - return $this->value instanceof RecordedTestCaseUpdate&& $this->type === 'recordedTestCase'; + public function isRecordedTestCase(): bool + { + return $this->value instanceof RecordedTestCaseUpdate && $this->type === 'recordedTestCase'; } /** * @return RecordedTestCaseUpdate */ - public function asRecordedTestCase(): RecordedTestCaseUpdate { - if (!($this->value instanceof RecordedTestCaseUpdate&& $this->type === 'recordedTestCase')){ + public function asRecordedTestCase(): RecordedTestCaseUpdate + { + if (!($this->value instanceof RecordedTestCaseUpdate && $this->type === 'recordedTestCase')) { throw new Exception( "Expected recordedTestCase; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isFinished(): bool { - return is_null($this->value)&& $this->type === 'finished'; + public function isFinished(): bool + { + return is_null($this->value) && $this->type === 'finished'; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'running': $value = $this->value; $result['running'] = $value; @@ -261,26 +279,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -289,42 +308,43 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'running': - if (!array_key_exists('running', $data)){ + if (!array_key_exists('running', $data)) { throw new Exception( "JSON data is missing property 'running'", ); } - + $args['value'] = $data['running']; break; case 'stopped': $args['value'] = null; break; case 'errored': - if (!array_key_exists('errored', $data)){ + if (!array_key_exists('errored', $data)) { throw new Exception( "JSON data is missing property 'errored'", ); } - - if (!(is_array($data['errored']))){ + + if (!(is_array($data['errored']))) { throw new Exception( "Expected property 'errored' in JSON data to be array, instead received " . get_debug_type($data['errored']), ); @@ -345,7 +365,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/trace/src/Submission/TraceResponse.php b/seed/php-model/trace/src/Submission/TraceResponse.php index 40dcd295ead2..1248f4f4cf91 100644 --- a/seed/php-model/trace/src/Submission/TraceResponse.php +++ b/seed/php-model/trace/src/Submission/TraceResponse.php @@ -56,15 +56,20 @@ class TraceResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->submissionId = $values['submissionId'];$this->lineNumber = $values['lineNumber'];$this->returnValue = $values['returnValue'] ?? null;$this->expressionLocation = $values['expressionLocation'] ?? null;$this->stack = $values['stack'];$this->stdout = $values['stdout'] ?? null; + ) { + $this->submissionId = $values['submissionId']; + $this->lineNumber = $values['lineNumber']; + $this->returnValue = $values['returnValue'] ?? null; + $this->expressionLocation = $values['expressionLocation'] ?? null; + $this->stack = $values['stack']; + $this->stdout = $values['stdout'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/TraceResponseV2.php b/seed/php-model/trace/src/Submission/TraceResponseV2.php index d0b16b42f992..37296f515bb0 100644 --- a/seed/php-model/trace/src/Submission/TraceResponseV2.php +++ b/seed/php-model/trace/src/Submission/TraceResponseV2.php @@ -63,15 +63,21 @@ class TraceResponseV2 extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->submissionId = $values['submissionId'];$this->lineNumber = $values['lineNumber'];$this->file = $values['file'];$this->returnValue = $values['returnValue'] ?? null;$this->expressionLocation = $values['expressionLocation'] ?? null;$this->stack = $values['stack'];$this->stdout = $values['stdout'] ?? null; + ) { + $this->submissionId = $values['submissionId']; + $this->lineNumber = $values['lineNumber']; + $this->file = $values['file']; + $this->returnValue = $values['returnValue'] ?? null; + $this->expressionLocation = $values['expressionLocation'] ?? null; + $this->stack = $values['stack']; + $this->stdout = $values['stdout'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/TraceResponsesPage.php b/seed/php-model/trace/src/Submission/TraceResponsesPage.php index 0af79ae4513e..5adfdde3bbef 100644 --- a/seed/php-model/trace/src/Submission/TraceResponsesPage.php +++ b/seed/php-model/trace/src/Submission/TraceResponsesPage.php @@ -31,15 +31,16 @@ class TraceResponsesPage extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->offset = $values['offset'] ?? null;$this->traceResponses = $values['traceResponses']; + ) { + $this->offset = $values['offset'] ?? null; + $this->traceResponses = $values['traceResponses']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/TraceResponsesPageV2.php b/seed/php-model/trace/src/Submission/TraceResponsesPageV2.php index 3c4905e3e176..b0058b0ca018 100644 --- a/seed/php-model/trace/src/Submission/TraceResponsesPageV2.php +++ b/seed/php-model/trace/src/Submission/TraceResponsesPageV2.php @@ -31,15 +31,16 @@ class TraceResponsesPageV2 extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->offset = $values['offset'] ?? null;$this->traceResponses = $values['traceResponses']; + ) { + $this->offset = $values['offset'] ?? null; + $this->traceResponses = $values['traceResponses']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/TracedFile.php b/seed/php-model/trace/src/Submission/TracedFile.php index 7262673602d8..3f2954dbeaa4 100644 --- a/seed/php-model/trace/src/Submission/TracedFile.php +++ b/seed/php-model/trace/src/Submission/TracedFile.php @@ -27,15 +27,16 @@ class TracedFile extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->filename = $values['filename'];$this->directory = $values['directory']; + ) { + $this->filename = $values['filename']; + $this->directory = $values['directory']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/TracedTestCase.php b/seed/php-model/trace/src/Submission/TracedTestCase.php index ec6ab7a315ae..6acb83cfec74 100644 --- a/seed/php-model/trace/src/Submission/TracedTestCase.php +++ b/seed/php-model/trace/src/Submission/TracedTestCase.php @@ -27,15 +27,16 @@ class TracedTestCase extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->result = $values['result'];$this->traceResponsesSize = $values['traceResponsesSize']; + ) { + $this->result = $values['result']; + $this->traceResponsesSize = $values['traceResponsesSize']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/UnexpectedLanguageError.php b/seed/php-model/trace/src/Submission/UnexpectedLanguageError.php index 5a81d5d1f4a3..6be197e6494f 100644 --- a/seed/php-model/trace/src/Submission/UnexpectedLanguageError.php +++ b/seed/php-model/trace/src/Submission/UnexpectedLanguageError.php @@ -28,15 +28,16 @@ class UnexpectedLanguageError extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->expectedLanguage = $values['expectedLanguage'];$this->actualLanguage = $values['actualLanguage']; + ) { + $this->expectedLanguage = $values['expectedLanguage']; + $this->actualLanguage = $values['actualLanguage']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/WorkspaceFiles.php b/seed/php-model/trace/src/Submission/WorkspaceFiles.php index f107ac77c67a..4754662550a3 100644 --- a/seed/php-model/trace/src/Submission/WorkspaceFiles.php +++ b/seed/php-model/trace/src/Submission/WorkspaceFiles.php @@ -29,15 +29,16 @@ class WorkspaceFiles extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->mainFile = $values['mainFile'];$this->readOnlyFiles = $values['readOnlyFiles']; + ) { + $this->mainFile = $values['mainFile']; + $this->readOnlyFiles = $values['readOnlyFiles']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/WorkspaceRanResponse.php b/seed/php-model/trace/src/Submission/WorkspaceRanResponse.php index 78dbf6e404a9..0075f931b46d 100644 --- a/seed/php-model/trace/src/Submission/WorkspaceRanResponse.php +++ b/seed/php-model/trace/src/Submission/WorkspaceRanResponse.php @@ -27,15 +27,16 @@ class WorkspaceRanResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->submissionId = $values['submissionId'];$this->runDetails = $values['runDetails']; + ) { + $this->submissionId = $values['submissionId']; + $this->runDetails = $values['runDetails']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/WorkspaceRunDetails.php b/seed/php-model/trace/src/Submission/WorkspaceRunDetails.php index 6e6ef85730d0..5ffeb43efa04 100644 --- a/seed/php-model/trace/src/Submission/WorkspaceRunDetails.php +++ b/seed/php-model/trace/src/Submission/WorkspaceRunDetails.php @@ -34,15 +34,17 @@ class WorkspaceRunDetails extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->exceptionV2 = $values['exceptionV2'] ?? null;$this->exception = $values['exception'] ?? null;$this->stdout = $values['stdout']; + ) { + $this->exceptionV2 = $values['exceptionV2'] ?? null; + $this->exception = $values['exception'] ?? null; + $this->stdout = $values['stdout']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/WorkspaceStarterFilesResponse.php b/seed/php-model/trace/src/Submission/WorkspaceStarterFilesResponse.php index 59340d0eed96..3497697029bf 100644 --- a/seed/php-model/trace/src/Submission/WorkspaceStarterFilesResponse.php +++ b/seed/php-model/trace/src/Submission/WorkspaceStarterFilesResponse.php @@ -22,15 +22,15 @@ class WorkspaceStarterFilesResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->files = $values['files']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/WorkspaceStarterFilesResponseV2.php b/seed/php-model/trace/src/Submission/WorkspaceStarterFilesResponseV2.php index 10813c50906f..18769bb87d47 100644 --- a/seed/php-model/trace/src/Submission/WorkspaceStarterFilesResponseV2.php +++ b/seed/php-model/trace/src/Submission/WorkspaceStarterFilesResponseV2.php @@ -23,15 +23,15 @@ class WorkspaceStarterFilesResponseV2 extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->filesByLanguage = $values['filesByLanguage']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/WorkspaceSubmissionState.php b/seed/php-model/trace/src/Submission/WorkspaceSubmissionState.php index 4769ac2d0bdf..4d9c126126b9 100644 --- a/seed/php-model/trace/src/Submission/WorkspaceSubmissionState.php +++ b/seed/php-model/trace/src/Submission/WorkspaceSubmissionState.php @@ -20,15 +20,15 @@ class WorkspaceSubmissionState extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->status = $values['status']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/WorkspaceSubmissionStatus.php b/seed/php-model/trace/src/Submission/WorkspaceSubmissionStatus.php index fa737d802fb5..a904e08599bd 100644 --- a/seed/php-model/trace/src/Submission/WorkspaceSubmissionStatus.php +++ b/seed/php-model/trace/src/Submission/WorkspaceSubmissionStatus.php @@ -52,15 +52,16 @@ class WorkspaceSubmissionStatus extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @return WorkspaceSubmissionStatus */ - public static function stopped(): WorkspaceSubmissionStatus { + public static function stopped(): WorkspaceSubmissionStatus + { return new WorkspaceSubmissionStatus([ 'type' => 'stopped', 'value' => null, @@ -71,7 +72,8 @@ public static function stopped(): WorkspaceSubmissionStatus { * @param ErrorInfo $errored * @return WorkspaceSubmissionStatus */ - public static function errored(ErrorInfo $errored): WorkspaceSubmissionStatus { + public static function errored(ErrorInfo $errored): WorkspaceSubmissionStatus + { return new WorkspaceSubmissionStatus([ 'type' => 'errored', 'value' => $errored, @@ -82,7 +84,8 @@ public static function errored(ErrorInfo $errored): WorkspaceSubmissionStatus { * @param value-of $running * @return WorkspaceSubmissionStatus */ - public static function running(string $running): WorkspaceSubmissionStatus { + public static function running(string $running): WorkspaceSubmissionStatus + { return new WorkspaceSubmissionStatus([ 'type' => 'running', 'value' => $running, @@ -93,7 +96,8 @@ public static function running(string $running): WorkspaceSubmissionStatus { * @param WorkspaceRunDetails $ran * @return WorkspaceSubmissionStatus */ - public static function ran(WorkspaceRunDetails $ran): WorkspaceSubmissionStatus { + public static function ran(WorkspaceRunDetails $ran): WorkspaceSubmissionStatus + { return new WorkspaceSubmissionStatus([ 'type' => 'ran', 'value' => $ran, @@ -104,7 +108,8 @@ public static function ran(WorkspaceRunDetails $ran): WorkspaceSubmissionStatus * @param WorkspaceRunDetails $traced * @return WorkspaceSubmissionStatus */ - public static function traced(WorkspaceRunDetails $traced): WorkspaceSubmissionStatus { + public static function traced(WorkspaceRunDetails $traced): WorkspaceSubmissionStatus + { return new WorkspaceSubmissionStatus([ 'type' => 'traced', 'value' => $traced, @@ -114,108 +119,119 @@ public static function traced(WorkspaceRunDetails $traced): WorkspaceSubmissionS /** * @return bool */ - public function isStopped(): bool { - return is_null($this->value)&& $this->type === 'stopped'; + public function isStopped(): bool + { + return is_null($this->value) && $this->type === 'stopped'; } /** * @return bool */ - public function isErrored(): bool { - return $this->value instanceof ErrorInfo&& $this->type === 'errored'; + public function isErrored(): bool + { + return $this->value instanceof ErrorInfo && $this->type === 'errored'; } /** * @return ErrorInfo */ - public function asErrored(): ErrorInfo { - if (!($this->value instanceof ErrorInfo&& $this->type === 'errored')){ + public function asErrored(): ErrorInfo + { + if (!($this->value instanceof ErrorInfo && $this->type === 'errored')) { throw new Exception( "Expected errored; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isRunning(): bool { - return $this->value instanceof RunningSubmissionState&& $this->type === 'running'; + public function isRunning(): bool + { + return $this->value instanceof RunningSubmissionState && $this->type === 'running'; } /** * @return value-of */ - public function asRunning(): string { - if (!($this->value instanceof RunningSubmissionState&& $this->type === 'running')){ + public function asRunning(): string + { + if (!($this->value instanceof RunningSubmissionState && $this->type === 'running')) { throw new Exception( "Expected running; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isRan(): bool { - return $this->value instanceof WorkspaceRunDetails&& $this->type === 'ran'; + public function isRan(): bool + { + return $this->value instanceof WorkspaceRunDetails && $this->type === 'ran'; } /** * @return WorkspaceRunDetails */ - public function asRan(): WorkspaceRunDetails { - if (!($this->value instanceof WorkspaceRunDetails&& $this->type === 'ran')){ + public function asRan(): WorkspaceRunDetails + { + if (!($this->value instanceof WorkspaceRunDetails && $this->type === 'ran')) { throw new Exception( "Expected ran; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isTraced(): bool { - return $this->value instanceof WorkspaceRunDetails&& $this->type === 'traced'; + public function isTraced(): bool + { + return $this->value instanceof WorkspaceRunDetails && $this->type === 'traced'; } /** * @return WorkspaceRunDetails */ - public function asTraced(): WorkspaceRunDetails { - if (!($this->value instanceof WorkspaceRunDetails&& $this->type === 'traced')){ + public function asTraced(): WorkspaceRunDetails + { + if (!($this->value instanceof WorkspaceRunDetails && $this->type === 'traced')) { throw new Exception( "Expected traced; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'stopped': $result['stopped'] = []; break; @@ -237,26 +253,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -265,33 +282,34 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'stopped': $args['value'] = null; break; case 'errored': - if (!array_key_exists('errored', $data)){ + if (!array_key_exists('errored', $data)) { throw new Exception( "JSON data is missing property 'errored'", ); } - - if (!(is_array($data['errored']))){ + + if (!(is_array($data['errored']))) { throw new Exception( "Expected property 'errored' in JSON data to be array, instead received " . get_debug_type($data['errored']), ); @@ -299,12 +317,12 @@ public static function jsonDeserialize(array $data): static { $args['value'] = ErrorInfo::jsonDeserialize($data['errored']); break; case 'running': - if (!array_key_exists('running', $data)){ + if (!array_key_exists('running', $data)) { throw new Exception( "JSON data is missing property 'running'", ); } - + $args['value'] = $data['running']; break; case 'ran': @@ -318,7 +336,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/trace/src/Submission/WorkspaceSubmissionStatusV2.php b/seed/php-model/trace/src/Submission/WorkspaceSubmissionStatusV2.php index 5ad7a706ed15..ac8eeeb7ee74 100644 --- a/seed/php-model/trace/src/Submission/WorkspaceSubmissionStatusV2.php +++ b/seed/php-model/trace/src/Submission/WorkspaceSubmissionStatusV2.php @@ -21,15 +21,15 @@ class WorkspaceSubmissionStatusV2 extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->updates = $values['updates']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/WorkspaceSubmissionUpdate.php b/seed/php-model/trace/src/Submission/WorkspaceSubmissionUpdate.php index 48cce9b531eb..e86c18885882 100644 --- a/seed/php-model/trace/src/Submission/WorkspaceSubmissionUpdate.php +++ b/seed/php-model/trace/src/Submission/WorkspaceSubmissionUpdate.php @@ -29,15 +29,16 @@ class WorkspaceSubmissionUpdate extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->updateTime = $values['updateTime'];$this->updateInfo = $values['updateInfo']; + ) { + $this->updateTime = $values['updateTime']; + $this->updateInfo = $values['updateInfo']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/WorkspaceSubmissionUpdateInfo.php b/seed/php-model/trace/src/Submission/WorkspaceSubmissionUpdateInfo.php index 8ba1f5af86a7..3925f608f9ac 100644 --- a/seed/php-model/trace/src/Submission/WorkspaceSubmissionUpdateInfo.php +++ b/seed/php-model/trace/src/Submission/WorkspaceSubmissionUpdateInfo.php @@ -58,16 +58,17 @@ class WorkspaceSubmissionUpdateInfo extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param value-of $running * @return WorkspaceSubmissionUpdateInfo */ - public static function running(string $running): WorkspaceSubmissionUpdateInfo { + public static function running(string $running): WorkspaceSubmissionUpdateInfo + { return new WorkspaceSubmissionUpdateInfo([ 'type' => 'running', 'value' => $running, @@ -78,7 +79,8 @@ public static function running(string $running): WorkspaceSubmissionUpdateInfo { * @param WorkspaceRunDetails $ran * @return WorkspaceSubmissionUpdateInfo */ - public static function ran(WorkspaceRunDetails $ran): WorkspaceSubmissionUpdateInfo { + public static function ran(WorkspaceRunDetails $ran): WorkspaceSubmissionUpdateInfo + { return new WorkspaceSubmissionUpdateInfo([ 'type' => 'ran', 'value' => $ran, @@ -88,7 +90,8 @@ public static function ran(WorkspaceRunDetails $ran): WorkspaceSubmissionUpdateI /** * @return WorkspaceSubmissionUpdateInfo */ - public static function stopped(): WorkspaceSubmissionUpdateInfo { + public static function stopped(): WorkspaceSubmissionUpdateInfo + { return new WorkspaceSubmissionUpdateInfo([ 'type' => 'stopped', 'value' => null, @@ -98,7 +101,8 @@ public static function stopped(): WorkspaceSubmissionUpdateInfo { /** * @return WorkspaceSubmissionUpdateInfo */ - public static function traced(): WorkspaceSubmissionUpdateInfo { + public static function traced(): WorkspaceSubmissionUpdateInfo + { return new WorkspaceSubmissionUpdateInfo([ 'type' => 'traced', 'value' => null, @@ -109,7 +113,8 @@ public static function traced(): WorkspaceSubmissionUpdateInfo { * @param WorkspaceTracedUpdate $tracedV2 * @return WorkspaceSubmissionUpdateInfo */ - public static function tracedV2(WorkspaceTracedUpdate $tracedV2): WorkspaceSubmissionUpdateInfo { + public static function tracedV2(WorkspaceTracedUpdate $tracedV2): WorkspaceSubmissionUpdateInfo + { return new WorkspaceSubmissionUpdateInfo([ 'type' => 'tracedV2', 'value' => $tracedV2, @@ -120,7 +125,8 @@ public static function tracedV2(WorkspaceTracedUpdate $tracedV2): WorkspaceSubmi * @param ErrorInfo $errored * @return WorkspaceSubmissionUpdateInfo */ - public static function errored(ErrorInfo $errored): WorkspaceSubmissionUpdateInfo { + public static function errored(ErrorInfo $errored): WorkspaceSubmissionUpdateInfo + { return new WorkspaceSubmissionUpdateInfo([ 'type' => 'errored', 'value' => $errored, @@ -130,7 +136,8 @@ public static function errored(ErrorInfo $errored): WorkspaceSubmissionUpdateInf /** * @return WorkspaceSubmissionUpdateInfo */ - public static function finished(): WorkspaceSubmissionUpdateInfo { + public static function finished(): WorkspaceSubmissionUpdateInfo + { return new WorkspaceSubmissionUpdateInfo([ 'type' => 'finished', 'value' => null, @@ -140,122 +147,135 @@ public static function finished(): WorkspaceSubmissionUpdateInfo { /** * @return bool */ - public function isRunning(): bool { - return $this->value instanceof RunningSubmissionState&& $this->type === 'running'; + public function isRunning(): bool + { + return $this->value instanceof RunningSubmissionState && $this->type === 'running'; } /** * @return value-of */ - public function asRunning(): string { - if (!($this->value instanceof RunningSubmissionState&& $this->type === 'running')){ + public function asRunning(): string + { + if (!($this->value instanceof RunningSubmissionState && $this->type === 'running')) { throw new Exception( "Expected running; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isRan(): bool { - return $this->value instanceof WorkspaceRunDetails&& $this->type === 'ran'; + public function isRan(): bool + { + return $this->value instanceof WorkspaceRunDetails && $this->type === 'ran'; } /** * @return WorkspaceRunDetails */ - public function asRan(): WorkspaceRunDetails { - if (!($this->value instanceof WorkspaceRunDetails&& $this->type === 'ran')){ + public function asRan(): WorkspaceRunDetails + { + if (!($this->value instanceof WorkspaceRunDetails && $this->type === 'ran')) { throw new Exception( "Expected ran; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isStopped(): bool { - return is_null($this->value)&& $this->type === 'stopped'; + public function isStopped(): bool + { + return is_null($this->value) && $this->type === 'stopped'; } /** * @return bool */ - public function isTraced(): bool { - return is_null($this->value)&& $this->type === 'traced'; + public function isTraced(): bool + { + return is_null($this->value) && $this->type === 'traced'; } /** * @return bool */ - public function isTracedV2(): bool { - return $this->value instanceof WorkspaceTracedUpdate&& $this->type === 'tracedV2'; + public function isTracedV2(): bool + { + return $this->value instanceof WorkspaceTracedUpdate && $this->type === 'tracedV2'; } /** * @return WorkspaceTracedUpdate */ - public function asTracedV2(): WorkspaceTracedUpdate { - if (!($this->value instanceof WorkspaceTracedUpdate&& $this->type === 'tracedV2')){ + public function asTracedV2(): WorkspaceTracedUpdate + { + if (!($this->value instanceof WorkspaceTracedUpdate && $this->type === 'tracedV2')) { throw new Exception( "Expected tracedV2; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isErrored(): bool { - return $this->value instanceof ErrorInfo&& $this->type === 'errored'; + public function isErrored(): bool + { + return $this->value instanceof ErrorInfo && $this->type === 'errored'; } /** * @return ErrorInfo */ - public function asErrored(): ErrorInfo { - if (!($this->value instanceof ErrorInfo&& $this->type === 'errored')){ + public function asErrored(): ErrorInfo + { + if (!($this->value instanceof ErrorInfo && $this->type === 'errored')) { throw new Exception( "Expected errored; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isFinished(): bool { - return is_null($this->value)&& $this->type === 'finished'; + public function isFinished(): bool + { + return is_null($this->value) && $this->type === 'finished'; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'running': $value = $this->value; $result['running'] = $value; @@ -283,26 +303,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -311,29 +332,30 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'running': - if (!array_key_exists('running', $data)){ + if (!array_key_exists('running', $data)) { throw new Exception( "JSON data is missing property 'running'", ); } - + $args['value'] = $data['running']; break; case 'ran': @@ -349,13 +371,13 @@ public static function jsonDeserialize(array $data): static { $args['value'] = WorkspaceTracedUpdate::jsonDeserialize($data); break; case 'errored': - if (!array_key_exists('errored', $data)){ + if (!array_key_exists('errored', $data)) { throw new Exception( "JSON data is missing property 'errored'", ); } - - if (!(is_array($data['errored']))){ + + if (!(is_array($data['errored']))) { throw new Exception( "Expected property 'errored' in JSON data to be array, instead received " . get_debug_type($data['errored']), ); @@ -370,7 +392,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/trace/src/Submission/WorkspaceSubmitRequest.php b/seed/php-model/trace/src/Submission/WorkspaceSubmitRequest.php index d2084d301f30..eefb8a7be27e 100644 --- a/seed/php-model/trace/src/Submission/WorkspaceSubmitRequest.php +++ b/seed/php-model/trace/src/Submission/WorkspaceSubmitRequest.php @@ -43,15 +43,18 @@ class WorkspaceSubmitRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->submissionId = $values['submissionId'];$this->language = $values['language'];$this->submissionFiles = $values['submissionFiles'];$this->userId = $values['userId'] ?? null; + ) { + $this->submissionId = $values['submissionId']; + $this->language = $values['language']; + $this->submissionFiles = $values['submissionFiles']; + $this->userId = $values['userId'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/Submission/WorkspaceTracedUpdate.php b/seed/php-model/trace/src/Submission/WorkspaceTracedUpdate.php index eda22e9f384c..46c3bd7a6dac 100644 --- a/seed/php-model/trace/src/Submission/WorkspaceTracedUpdate.php +++ b/seed/php-model/trace/src/Submission/WorkspaceTracedUpdate.php @@ -20,15 +20,15 @@ class WorkspaceTracedUpdate extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->traceResponsesSize = $values['traceResponsesSize']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/Problem/AssertCorrectnessCheck.php b/seed/php-model/trace/src/V2/Problem/AssertCorrectnessCheck.php index 183e69c0e5b0..8c43e82a51fd 100644 --- a/seed/php-model/trace/src/V2/Problem/AssertCorrectnessCheck.php +++ b/seed/php-model/trace/src/V2/Problem/AssertCorrectnessCheck.php @@ -42,16 +42,17 @@ class AssertCorrectnessCheck extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param DeepEqualityCorrectnessCheck $deepEquality * @return AssertCorrectnessCheck */ - public static function deepEquality(DeepEqualityCorrectnessCheck $deepEquality): AssertCorrectnessCheck { + public static function deepEquality(DeepEqualityCorrectnessCheck $deepEquality): AssertCorrectnessCheck + { return new AssertCorrectnessCheck([ 'type' => 'deepEquality', 'value' => $deepEquality, @@ -62,7 +63,8 @@ public static function deepEquality(DeepEqualityCorrectnessCheck $deepEquality): * @param VoidFunctionDefinitionThatTakesActualResult $custom * @return AssertCorrectnessCheck */ - public static function custom(VoidFunctionDefinitionThatTakesActualResult $custom): AssertCorrectnessCheck { + public static function custom(VoidFunctionDefinitionThatTakesActualResult $custom): AssertCorrectnessCheck + { return new AssertCorrectnessCheck([ 'type' => 'custom', 'value' => $custom, @@ -72,61 +74,67 @@ public static function custom(VoidFunctionDefinitionThatTakesActualResult $custo /** * @return bool */ - public function isDeepEquality(): bool { - return $this->value instanceof DeepEqualityCorrectnessCheck&& $this->type === 'deepEquality'; + public function isDeepEquality(): bool + { + return $this->value instanceof DeepEqualityCorrectnessCheck && $this->type === 'deepEquality'; } /** * @return DeepEqualityCorrectnessCheck */ - public function asDeepEquality(): DeepEqualityCorrectnessCheck { - if (!($this->value instanceof DeepEqualityCorrectnessCheck&& $this->type === 'deepEquality')){ + public function asDeepEquality(): DeepEqualityCorrectnessCheck + { + if (!($this->value instanceof DeepEqualityCorrectnessCheck && $this->type === 'deepEquality')) { throw new Exception( "Expected deepEquality; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isCustom(): bool { - return $this->value instanceof VoidFunctionDefinitionThatTakesActualResult&& $this->type === 'custom'; + public function isCustom(): bool + { + return $this->value instanceof VoidFunctionDefinitionThatTakesActualResult && $this->type === 'custom'; } /** * @return VoidFunctionDefinitionThatTakesActualResult */ - public function asCustom(): VoidFunctionDefinitionThatTakesActualResult { - if (!($this->value instanceof VoidFunctionDefinitionThatTakesActualResult&& $this->type === 'custom')){ + public function asCustom(): VoidFunctionDefinitionThatTakesActualResult + { + if (!($this->value instanceof VoidFunctionDefinitionThatTakesActualResult && $this->type === 'custom')) { throw new Exception( "Expected custom; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'deepEquality': $value = $this->asDeepEquality()->jsonSerialize(); $result = array_merge($value, $result); @@ -137,26 +145,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -165,22 +174,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'deepEquality': $args['value'] = DeepEqualityCorrectnessCheck::jsonDeserialize($data); break; @@ -192,7 +202,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/trace/src/V2/Problem/BasicCustomFiles.php b/seed/php-model/trace/src/V2/Problem/BasicCustomFiles.php index a40145347572..7c30af15d697 100644 --- a/seed/php-model/trace/src/V2/Problem/BasicCustomFiles.php +++ b/seed/php-model/trace/src/V2/Problem/BasicCustomFiles.php @@ -43,15 +43,18 @@ class BasicCustomFiles extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->methodName = $values['methodName'];$this->signature = $values['signature'];$this->additionalFiles = $values['additionalFiles'];$this->basicTestCaseTemplate = $values['basicTestCaseTemplate']; + ) { + $this->methodName = $values['methodName']; + $this->signature = $values['signature']; + $this->additionalFiles = $values['additionalFiles']; + $this->basicTestCaseTemplate = $values['basicTestCaseTemplate']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/Problem/BasicTestCaseTemplate.php b/seed/php-model/trace/src/V2/Problem/BasicTestCaseTemplate.php index abd8f28d0e28..1449d428004e 100644 --- a/seed/php-model/trace/src/V2/Problem/BasicTestCaseTemplate.php +++ b/seed/php-model/trace/src/V2/Problem/BasicTestCaseTemplate.php @@ -41,15 +41,18 @@ class BasicTestCaseTemplate extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->templateId = $values['templateId'];$this->name = $values['name'];$this->description = $values['description'];$this->expectedValueParameterId = $values['expectedValueParameterId']; + ) { + $this->templateId = $values['templateId']; + $this->name = $values['name']; + $this->description = $values['description']; + $this->expectedValueParameterId = $values['expectedValueParameterId']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/Problem/CreateProblemRequestV2.php b/seed/php-model/trace/src/V2/Problem/CreateProblemRequestV2.php index 2a6a16323bd6..77b28f86219a 100644 --- a/seed/php-model/trace/src/V2/Problem/CreateProblemRequestV2.php +++ b/seed/php-model/trace/src/V2/Problem/CreateProblemRequestV2.php @@ -65,15 +65,21 @@ class CreateProblemRequestV2 extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->problemName = $values['problemName'];$this->problemDescription = $values['problemDescription'];$this->customFiles = $values['customFiles'];$this->customTestCaseTemplates = $values['customTestCaseTemplates'];$this->testcases = $values['testcases'];$this->supportedLanguages = $values['supportedLanguages'];$this->isPublic = $values['isPublic']; + ) { + $this->problemName = $values['problemName']; + $this->problemDescription = $values['problemDescription']; + $this->customFiles = $values['customFiles']; + $this->customTestCaseTemplates = $values['customTestCaseTemplates']; + $this->testcases = $values['testcases']; + $this->supportedLanguages = $values['supportedLanguages']; + $this->isPublic = $values['isPublic']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/Problem/CustomFiles.php b/seed/php-model/trace/src/V2/Problem/CustomFiles.php index 6589c4dbb715..6ff51f47d22d 100644 --- a/seed/php-model/trace/src/V2/Problem/CustomFiles.php +++ b/seed/php-model/trace/src/V2/Problem/CustomFiles.php @@ -43,16 +43,17 @@ class CustomFiles extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param BasicCustomFiles $basic * @return CustomFiles */ - public static function basic(BasicCustomFiles $basic): CustomFiles { + public static function basic(BasicCustomFiles $basic): CustomFiles + { return new CustomFiles([ 'type' => 'basic', 'value' => $basic, @@ -63,7 +64,8 @@ public static function basic(BasicCustomFiles $basic): CustomFiles { * @param array, Files> $custom * @return CustomFiles */ - public static function custom(array $custom): CustomFiles { + public static function custom(array $custom): CustomFiles + { return new CustomFiles([ 'type' => 'custom', 'value' => $custom, @@ -73,61 +75,67 @@ public static function custom(array $custom): CustomFiles { /** * @return bool */ - public function isBasic(): bool { - return $this->value instanceof BasicCustomFiles&& $this->type === 'basic'; + public function isBasic(): bool + { + return $this->value instanceof BasicCustomFiles && $this->type === 'basic'; } /** * @return BasicCustomFiles */ - public function asBasic(): BasicCustomFiles { - if (!($this->value instanceof BasicCustomFiles&& $this->type === 'basic')){ + public function asBasic(): BasicCustomFiles + { + if (!($this->value instanceof BasicCustomFiles && $this->type === 'basic')) { throw new Exception( "Expected basic; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isCustom(): bool { - return is_array($this->value)&& $this->type === 'custom'; + public function isCustom(): bool + { + return is_array($this->value) && $this->type === 'custom'; } /** * @return array, Files> */ - public function asCustom(): array { - if (!(is_array($this->value)&& $this->type === 'custom')){ + public function asCustom(): array + { + if (!(is_array($this->value) && $this->type === 'custom')) { throw new Exception( "Expected custom; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'basic': $value = $this->asBasic()->jsonSerialize(); $result = array_merge($value, $result); @@ -138,26 +146,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -166,32 +175,33 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'basic': $args['value'] = BasicCustomFiles::jsonDeserialize($data); break; case 'custom': - if (!array_key_exists('custom', $data)){ + if (!array_key_exists('custom', $data)) { throw new Exception( "JSON data is missing property 'custom'", ); } - + $args['value'] = $data['custom']; break; case '_unknown': @@ -199,7 +209,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/trace/src/V2/Problem/DeepEqualityCorrectnessCheck.php b/seed/php-model/trace/src/V2/Problem/DeepEqualityCorrectnessCheck.php index f035cb138755..efa8638348e5 100644 --- a/seed/php-model/trace/src/V2/Problem/DeepEqualityCorrectnessCheck.php +++ b/seed/php-model/trace/src/V2/Problem/DeepEqualityCorrectnessCheck.php @@ -20,15 +20,15 @@ class DeepEqualityCorrectnessCheck extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->expectedValueParameterId = $values['expectedValueParameterId']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/Problem/DefaultProvidedFile.php b/seed/php-model/trace/src/V2/Problem/DefaultProvidedFile.php index 7fa6b692db17..38f211d61c70 100644 --- a/seed/php-model/trace/src/V2/Problem/DefaultProvidedFile.php +++ b/seed/php-model/trace/src/V2/Problem/DefaultProvidedFile.php @@ -29,15 +29,16 @@ class DefaultProvidedFile extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->file = $values['file'];$this->relatedTypes = $values['relatedTypes']; + ) { + $this->file = $values['file']; + $this->relatedTypes = $values['relatedTypes']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/Problem/FileInfoV2.php b/seed/php-model/trace/src/V2/Problem/FileInfoV2.php index 2ae3784d6fde..8213014608fd 100644 --- a/seed/php-model/trace/src/V2/Problem/FileInfoV2.php +++ b/seed/php-model/trace/src/V2/Problem/FileInfoV2.php @@ -41,15 +41,18 @@ class FileInfoV2 extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->filename = $values['filename'];$this->directory = $values['directory'];$this->contents = $values['contents'];$this->editable = $values['editable']; + ) { + $this->filename = $values['filename']; + $this->directory = $values['directory']; + $this->contents = $values['contents']; + $this->editable = $values['editable']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/Problem/Files.php b/seed/php-model/trace/src/V2/Problem/Files.php index ffb2ebb2f1b0..bbd71979ad13 100644 --- a/seed/php-model/trace/src/V2/Problem/Files.php +++ b/seed/php-model/trace/src/V2/Problem/Files.php @@ -21,15 +21,15 @@ class Files extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->files = $values['files']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/Problem/FunctionImplementation.php b/seed/php-model/trace/src/V2/Problem/FunctionImplementation.php index 3e2f06911d91..13794f9b2bfc 100644 --- a/seed/php-model/trace/src/V2/Problem/FunctionImplementation.php +++ b/seed/php-model/trace/src/V2/Problem/FunctionImplementation.php @@ -27,15 +27,16 @@ class FunctionImplementation extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->impl = $values['impl'];$this->imports = $values['imports'] ?? null; + ) { + $this->impl = $values['impl']; + $this->imports = $values['imports'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/Problem/FunctionImplementationForMultipleLanguages.php b/seed/php-model/trace/src/V2/Problem/FunctionImplementationForMultipleLanguages.php index 66279ee33eb2..cfb0beb4c3ac 100644 --- a/seed/php-model/trace/src/V2/Problem/FunctionImplementationForMultipleLanguages.php +++ b/seed/php-model/trace/src/V2/Problem/FunctionImplementationForMultipleLanguages.php @@ -22,15 +22,15 @@ class FunctionImplementationForMultipleLanguages extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->codeByLanguage = $values['codeByLanguage']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/Problem/FunctionSignature.php b/seed/php-model/trace/src/V2/Problem/FunctionSignature.php index 4bc22bd932f4..0f8215ebf2c5 100644 --- a/seed/php-model/trace/src/V2/Problem/FunctionSignature.php +++ b/seed/php-model/trace/src/V2/Problem/FunctionSignature.php @@ -46,16 +46,17 @@ class FunctionSignature extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param VoidFunctionSignature $void * @return FunctionSignature */ - public static function void(VoidFunctionSignature $void): FunctionSignature { + public static function void(VoidFunctionSignature $void): FunctionSignature + { return new FunctionSignature([ 'type' => 'void', 'value' => $void, @@ -66,7 +67,8 @@ public static function void(VoidFunctionSignature $void): FunctionSignature { * @param NonVoidFunctionSignature $nonVoid * @return FunctionSignature */ - public static function nonVoid(NonVoidFunctionSignature $nonVoid): FunctionSignature { + public static function nonVoid(NonVoidFunctionSignature $nonVoid): FunctionSignature + { return new FunctionSignature([ 'type' => 'nonVoid', 'value' => $nonVoid, @@ -77,7 +79,8 @@ public static function nonVoid(NonVoidFunctionSignature $nonVoid): FunctionSigna * @param VoidFunctionSignatureThatTakesActualResult $voidThatTakesActualResult * @return FunctionSignature */ - public static function voidThatTakesActualResult(VoidFunctionSignatureThatTakesActualResult $voidThatTakesActualResult): FunctionSignature { + public static function voidThatTakesActualResult(VoidFunctionSignatureThatTakesActualResult $voidThatTakesActualResult): FunctionSignature + { return new FunctionSignature([ 'type' => 'voidThatTakesActualResult', 'value' => $voidThatTakesActualResult, @@ -87,81 +90,89 @@ public static function voidThatTakesActualResult(VoidFunctionSignatureThatTakesA /** * @return bool */ - public function isVoid(): bool { - return $this->value instanceof VoidFunctionSignature&& $this->type === 'void'; + public function isVoid(): bool + { + return $this->value instanceof VoidFunctionSignature && $this->type === 'void'; } /** * @return VoidFunctionSignature */ - public function asVoid(): VoidFunctionSignature { - if (!($this->value instanceof VoidFunctionSignature&& $this->type === 'void')){ + public function asVoid(): VoidFunctionSignature + { + if (!($this->value instanceof VoidFunctionSignature && $this->type === 'void')) { throw new Exception( "Expected void; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isNonVoid(): bool { - return $this->value instanceof NonVoidFunctionSignature&& $this->type === 'nonVoid'; + public function isNonVoid(): bool + { + return $this->value instanceof NonVoidFunctionSignature && $this->type === 'nonVoid'; } /** * @return NonVoidFunctionSignature */ - public function asNonVoid(): NonVoidFunctionSignature { - if (!($this->value instanceof NonVoidFunctionSignature&& $this->type === 'nonVoid')){ + public function asNonVoid(): NonVoidFunctionSignature + { + if (!($this->value instanceof NonVoidFunctionSignature && $this->type === 'nonVoid')) { throw new Exception( "Expected nonVoid; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isVoidThatTakesActualResult(): bool { - return $this->value instanceof VoidFunctionSignatureThatTakesActualResult&& $this->type === 'voidThatTakesActualResult'; + public function isVoidThatTakesActualResult(): bool + { + return $this->value instanceof VoidFunctionSignatureThatTakesActualResult && $this->type === 'voidThatTakesActualResult'; } /** * @return VoidFunctionSignatureThatTakesActualResult */ - public function asVoidThatTakesActualResult(): VoidFunctionSignatureThatTakesActualResult { - if (!($this->value instanceof VoidFunctionSignatureThatTakesActualResult&& $this->type === 'voidThatTakesActualResult')){ + public function asVoidThatTakesActualResult(): VoidFunctionSignatureThatTakesActualResult + { + if (!($this->value instanceof VoidFunctionSignatureThatTakesActualResult && $this->type === 'voidThatTakesActualResult')) { throw new Exception( "Expected voidThatTakesActualResult; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'void': $value = $this->asVoid()->jsonSerialize(); $result = array_merge($value, $result); @@ -176,26 +187,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -204,22 +216,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'void': $args['value'] = VoidFunctionSignature::jsonDeserialize($data); break; @@ -234,7 +247,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/trace/src/V2/Problem/GeneratedFiles.php b/seed/php-model/trace/src/V2/Problem/GeneratedFiles.php index 0463e0164a7e..a1934e8e15ce 100644 --- a/seed/php-model/trace/src/V2/Problem/GeneratedFiles.php +++ b/seed/php-model/trace/src/V2/Problem/GeneratedFiles.php @@ -36,15 +36,17 @@ class GeneratedFiles extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->generatedTestCaseFiles = $values['generatedTestCaseFiles'];$this->generatedTemplateFiles = $values['generatedTemplateFiles'];$this->other = $values['other']; + ) { + $this->generatedTestCaseFiles = $values['generatedTestCaseFiles']; + $this->generatedTemplateFiles = $values['generatedTemplateFiles']; + $this->other = $values['other']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/Problem/GetBasicSolutionFileRequest.php b/seed/php-model/trace/src/V2/Problem/GetBasicSolutionFileRequest.php index d86c520be144..8426b0b960ab 100644 --- a/seed/php-model/trace/src/V2/Problem/GetBasicSolutionFileRequest.php +++ b/seed/php-model/trace/src/V2/Problem/GetBasicSolutionFileRequest.php @@ -27,15 +27,16 @@ class GetBasicSolutionFileRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->methodName = $values['methodName'];$this->signature = $values['signature']; + ) { + $this->methodName = $values['methodName']; + $this->signature = $values['signature']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/Problem/GetBasicSolutionFileResponse.php b/seed/php-model/trace/src/V2/Problem/GetBasicSolutionFileResponse.php index 8e03975c8ae1..2f45867eb401 100644 --- a/seed/php-model/trace/src/V2/Problem/GetBasicSolutionFileResponse.php +++ b/seed/php-model/trace/src/V2/Problem/GetBasicSolutionFileResponse.php @@ -22,15 +22,15 @@ class GetBasicSolutionFileResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->solutionFileByLanguage = $values['solutionFileByLanguage']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/Problem/GetFunctionSignatureRequest.php b/seed/php-model/trace/src/V2/Problem/GetFunctionSignatureRequest.php index eea061f6ea98..eb07ac0a2a6b 100644 --- a/seed/php-model/trace/src/V2/Problem/GetFunctionSignatureRequest.php +++ b/seed/php-model/trace/src/V2/Problem/GetFunctionSignatureRequest.php @@ -20,15 +20,15 @@ class GetFunctionSignatureRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->functionSignature = $values['functionSignature']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/Problem/GetFunctionSignatureResponse.php b/seed/php-model/trace/src/V2/Problem/GetFunctionSignatureResponse.php index b9f74c79e7ae..fa5173129309 100644 --- a/seed/php-model/trace/src/V2/Problem/GetFunctionSignatureResponse.php +++ b/seed/php-model/trace/src/V2/Problem/GetFunctionSignatureResponse.php @@ -22,15 +22,15 @@ class GetFunctionSignatureResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->functionByLanguage = $values['functionByLanguage']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/Problem/GetGeneratedTestCaseFileRequest.php b/seed/php-model/trace/src/V2/Problem/GetGeneratedTestCaseFileRequest.php index d4b4965cd4a6..298f66125d5a 100644 --- a/seed/php-model/trace/src/V2/Problem/GetGeneratedTestCaseFileRequest.php +++ b/seed/php-model/trace/src/V2/Problem/GetGeneratedTestCaseFileRequest.php @@ -27,15 +27,16 @@ class GetGeneratedTestCaseFileRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->template = $values['template'] ?? null;$this->testCase = $values['testCase']; + ) { + $this->template = $values['template'] ?? null; + $this->testCase = $values['testCase']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/Problem/GetGeneratedTestCaseTemplateFileRequest.php b/seed/php-model/trace/src/V2/Problem/GetGeneratedTestCaseTemplateFileRequest.php index 8358d2951018..8297a7c3c210 100644 --- a/seed/php-model/trace/src/V2/Problem/GetGeneratedTestCaseTemplateFileRequest.php +++ b/seed/php-model/trace/src/V2/Problem/GetGeneratedTestCaseTemplateFileRequest.php @@ -20,15 +20,15 @@ class GetGeneratedTestCaseTemplateFileRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->template = $values['template']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/Problem/LightweightProblemInfoV2.php b/seed/php-model/trace/src/V2/Problem/LightweightProblemInfoV2.php index 9dd620e4363f..1a21689c0aa5 100644 --- a/seed/php-model/trace/src/V2/Problem/LightweightProblemInfoV2.php +++ b/seed/php-model/trace/src/V2/Problem/LightweightProblemInfoV2.php @@ -43,15 +43,18 @@ class LightweightProblemInfoV2 extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->problemId = $values['problemId'];$this->problemName = $values['problemName'];$this->problemVersion = $values['problemVersion'];$this->variableTypes = $values['variableTypes']; + ) { + $this->problemId = $values['problemId']; + $this->problemName = $values['problemName']; + $this->problemVersion = $values['problemVersion']; + $this->variableTypes = $values['variableTypes']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/Problem/NonVoidFunctionDefinition.php b/seed/php-model/trace/src/V2/Problem/NonVoidFunctionDefinition.php index c29d4b8c9964..3871935ac5cb 100644 --- a/seed/php-model/trace/src/V2/Problem/NonVoidFunctionDefinition.php +++ b/seed/php-model/trace/src/V2/Problem/NonVoidFunctionDefinition.php @@ -27,15 +27,16 @@ class NonVoidFunctionDefinition extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->signature = $values['signature'];$this->code = $values['code']; + ) { + $this->signature = $values['signature']; + $this->code = $values['code']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/Problem/NonVoidFunctionSignature.php b/seed/php-model/trace/src/V2/Problem/NonVoidFunctionSignature.php index de5c57ea04ab..83521de80086 100644 --- a/seed/php-model/trace/src/V2/Problem/NonVoidFunctionSignature.php +++ b/seed/php-model/trace/src/V2/Problem/NonVoidFunctionSignature.php @@ -29,15 +29,16 @@ class NonVoidFunctionSignature extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->parameters = $values['parameters'];$this->returnType = $values['returnType']; + ) { + $this->parameters = $values['parameters']; + $this->returnType = $values['returnType']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/Problem/Parameter.php b/seed/php-model/trace/src/V2/Problem/Parameter.php index e3d3c4f65151..0c16bff0da2a 100644 --- a/seed/php-model/trace/src/V2/Problem/Parameter.php +++ b/seed/php-model/trace/src/V2/Problem/Parameter.php @@ -35,15 +35,17 @@ class Parameter extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->parameterId = $values['parameterId'];$this->name = $values['name'];$this->variableType = $values['variableType']; + ) { + $this->parameterId = $values['parameterId']; + $this->name = $values['name']; + $this->variableType = $values['variableType']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/Problem/ProblemInfoV2.php b/seed/php-model/trace/src/V2/Problem/ProblemInfoV2.php index 354ca371b950..730fd9463170 100644 --- a/seed/php-model/trace/src/V2/Problem/ProblemInfoV2.php +++ b/seed/php-model/trace/src/V2/Problem/ProblemInfoV2.php @@ -86,15 +86,24 @@ class ProblemInfoV2 extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->problemId = $values['problemId'];$this->problemDescription = $values['problemDescription'];$this->problemName = $values['problemName'];$this->problemVersion = $values['problemVersion'];$this->supportedLanguages = $values['supportedLanguages'];$this->customFiles = $values['customFiles'];$this->generatedFiles = $values['generatedFiles'];$this->customTestCaseTemplates = $values['customTestCaseTemplates'];$this->testcases = $values['testcases'];$this->isPublic = $values['isPublic']; + ) { + $this->problemId = $values['problemId']; + $this->problemDescription = $values['problemDescription']; + $this->problemName = $values['problemName']; + $this->problemVersion = $values['problemVersion']; + $this->supportedLanguages = $values['supportedLanguages']; + $this->customFiles = $values['customFiles']; + $this->generatedFiles = $values['generatedFiles']; + $this->customTestCaseTemplates = $values['customTestCaseTemplates']; + $this->testcases = $values['testcases']; + $this->isPublic = $values['isPublic']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/Problem/TestCaseExpects.php b/seed/php-model/trace/src/V2/Problem/TestCaseExpects.php index ef2e91c6f75b..b738c46f758f 100644 --- a/seed/php-model/trace/src/V2/Problem/TestCaseExpects.php +++ b/seed/php-model/trace/src/V2/Problem/TestCaseExpects.php @@ -20,15 +20,15 @@ class TestCaseExpects extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->expectedStdout = $values['expectedStdout'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/Problem/TestCaseFunction.php b/seed/php-model/trace/src/V2/Problem/TestCaseFunction.php index 694ad8fef947..2b59376b405d 100644 --- a/seed/php-model/trace/src/V2/Problem/TestCaseFunction.php +++ b/seed/php-model/trace/src/V2/Problem/TestCaseFunction.php @@ -42,16 +42,17 @@ class TestCaseFunction extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param TestCaseWithActualResultImplementation $withActualResult * @return TestCaseFunction */ - public static function withActualResult(TestCaseWithActualResultImplementation $withActualResult): TestCaseFunction { + public static function withActualResult(TestCaseWithActualResultImplementation $withActualResult): TestCaseFunction + { return new TestCaseFunction([ 'type' => 'withActualResult', 'value' => $withActualResult, @@ -62,7 +63,8 @@ public static function withActualResult(TestCaseWithActualResultImplementation $ * @param VoidFunctionDefinition $custom * @return TestCaseFunction */ - public static function custom(VoidFunctionDefinition $custom): TestCaseFunction { + public static function custom(VoidFunctionDefinition $custom): TestCaseFunction + { return new TestCaseFunction([ 'type' => 'custom', 'value' => $custom, @@ -72,61 +74,67 @@ public static function custom(VoidFunctionDefinition $custom): TestCaseFunction /** * @return bool */ - public function isWithActualResult(): bool { - return $this->value instanceof TestCaseWithActualResultImplementation&& $this->type === 'withActualResult'; + public function isWithActualResult(): bool + { + return $this->value instanceof TestCaseWithActualResultImplementation && $this->type === 'withActualResult'; } /** * @return TestCaseWithActualResultImplementation */ - public function asWithActualResult(): TestCaseWithActualResultImplementation { - if (!($this->value instanceof TestCaseWithActualResultImplementation&& $this->type === 'withActualResult')){ + public function asWithActualResult(): TestCaseWithActualResultImplementation + { + if (!($this->value instanceof TestCaseWithActualResultImplementation && $this->type === 'withActualResult')) { throw new Exception( "Expected withActualResult; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isCustom(): bool { - return $this->value instanceof VoidFunctionDefinition&& $this->type === 'custom'; + public function isCustom(): bool + { + return $this->value instanceof VoidFunctionDefinition && $this->type === 'custom'; } /** * @return VoidFunctionDefinition */ - public function asCustom(): VoidFunctionDefinition { - if (!($this->value instanceof VoidFunctionDefinition&& $this->type === 'custom')){ + public function asCustom(): VoidFunctionDefinition + { + if (!($this->value instanceof VoidFunctionDefinition && $this->type === 'custom')) { throw new Exception( "Expected custom; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'withActualResult': $value = $this->asWithActualResult()->jsonSerialize(); $result = array_merge($value, $result); @@ -137,26 +145,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -165,22 +174,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'withActualResult': $args['value'] = TestCaseWithActualResultImplementation::jsonDeserialize($data); break; @@ -192,7 +202,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/trace/src/V2/Problem/TestCaseImplementation.php b/seed/php-model/trace/src/V2/Problem/TestCaseImplementation.php index f961e7250db7..7b6fb9e5a61a 100644 --- a/seed/php-model/trace/src/V2/Problem/TestCaseImplementation.php +++ b/seed/php-model/trace/src/V2/Problem/TestCaseImplementation.php @@ -27,15 +27,16 @@ class TestCaseImplementation extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->description = $values['description'];$this->function = $values['function']; + ) { + $this->description = $values['description']; + $this->function = $values['function']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/Problem/TestCaseImplementationDescription.php b/seed/php-model/trace/src/V2/Problem/TestCaseImplementationDescription.php index 79d075db8d3d..47be75e1b055 100644 --- a/seed/php-model/trace/src/V2/Problem/TestCaseImplementationDescription.php +++ b/seed/php-model/trace/src/V2/Problem/TestCaseImplementationDescription.php @@ -21,15 +21,15 @@ class TestCaseImplementationDescription extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->boards = $values['boards']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/Problem/TestCaseImplementationDescriptionBoard.php b/seed/php-model/trace/src/V2/Problem/TestCaseImplementationDescriptionBoard.php index 1af01d9fd12a..2e45a9548414 100644 --- a/seed/php-model/trace/src/V2/Problem/TestCaseImplementationDescriptionBoard.php +++ b/seed/php-model/trace/src/V2/Problem/TestCaseImplementationDescriptionBoard.php @@ -40,16 +40,17 @@ class TestCaseImplementationDescriptionBoard extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param string $html * @return TestCaseImplementationDescriptionBoard */ - public static function html(string $html): TestCaseImplementationDescriptionBoard { + public static function html(string $html): TestCaseImplementationDescriptionBoard + { return new TestCaseImplementationDescriptionBoard([ 'type' => 'html', 'value' => $html, @@ -60,7 +61,8 @@ public static function html(string $html): TestCaseImplementationDescriptionBoar * @param string $paramId * @return TestCaseImplementationDescriptionBoard */ - public static function paramId(string $paramId): TestCaseImplementationDescriptionBoard { + public static function paramId(string $paramId): TestCaseImplementationDescriptionBoard + { return new TestCaseImplementationDescriptionBoard([ 'type' => 'paramId', 'value' => $paramId, @@ -70,61 +72,67 @@ public static function paramId(string $paramId): TestCaseImplementationDescripti /** * @return bool */ - public function isHtml(): bool { - return is_string($this->value)&& $this->type === 'html'; + public function isHtml(): bool + { + return is_string($this->value) && $this->type === 'html'; } /** * @return string */ - public function asHtml(): string { - if (!(is_string($this->value)&& $this->type === 'html')){ + public function asHtml(): string + { + if (!(is_string($this->value) && $this->type === 'html')) { throw new Exception( "Expected html; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isParamId(): bool { - return is_string($this->value)&& $this->type === 'paramId'; + public function isParamId(): bool + { + return is_string($this->value) && $this->type === 'paramId'; } /** * @return string */ - public function asParamId(): string { - if (!(is_string($this->value)&& $this->type === 'paramId')){ + public function asParamId(): string + { + if (!(is_string($this->value) && $this->type === 'paramId')) { throw new Exception( "Expected paramId; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'html': $value = $this->value; $result['html'] = $value; @@ -135,26 +143,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -163,38 +172,39 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'html': - if (!array_key_exists('html', $data)){ + if (!array_key_exists('html', $data)) { throw new Exception( "JSON data is missing property 'html'", ); } - + $args['value'] = $data['html']; break; case 'paramId': - if (!array_key_exists('paramId', $data)){ + if (!array_key_exists('paramId', $data)) { throw new Exception( "JSON data is missing property 'paramId'", ); } - + $args['value'] = $data['paramId']; break; case '_unknown': @@ -202,7 +212,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/trace/src/V2/Problem/TestCaseImplementationReference.php b/seed/php-model/trace/src/V2/Problem/TestCaseImplementationReference.php index 1b0067cd1eea..2a3ca43089df 100644 --- a/seed/php-model/trace/src/V2/Problem/TestCaseImplementationReference.php +++ b/seed/php-model/trace/src/V2/Problem/TestCaseImplementationReference.php @@ -42,16 +42,17 @@ class TestCaseImplementationReference extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param string $templateId * @return TestCaseImplementationReference */ - public static function templateId(string $templateId): TestCaseImplementationReference { + public static function templateId(string $templateId): TestCaseImplementationReference + { return new TestCaseImplementationReference([ 'type' => 'templateId', 'value' => $templateId, @@ -62,7 +63,8 @@ public static function templateId(string $templateId): TestCaseImplementationRef * @param TestCaseImplementation $implementation * @return TestCaseImplementationReference */ - public static function implementation(TestCaseImplementation $implementation): TestCaseImplementationReference { + public static function implementation(TestCaseImplementation $implementation): TestCaseImplementationReference + { return new TestCaseImplementationReference([ 'type' => 'implementation', 'value' => $implementation, @@ -72,61 +74,67 @@ public static function implementation(TestCaseImplementation $implementation): T /** * @return bool */ - public function isTemplateId(): bool { - return is_string($this->value)&& $this->type === 'templateId'; + public function isTemplateId(): bool + { + return is_string($this->value) && $this->type === 'templateId'; } /** * @return string */ - public function asTemplateId(): string { - if (!(is_string($this->value)&& $this->type === 'templateId')){ + public function asTemplateId(): string + { + if (!(is_string($this->value) && $this->type === 'templateId')) { throw new Exception( "Expected templateId; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isImplementation(): bool { - return $this->value instanceof TestCaseImplementation&& $this->type === 'implementation'; + public function isImplementation(): bool + { + return $this->value instanceof TestCaseImplementation && $this->type === 'implementation'; } /** * @return TestCaseImplementation */ - public function asImplementation(): TestCaseImplementation { - if (!($this->value instanceof TestCaseImplementation&& $this->type === 'implementation')){ + public function asImplementation(): TestCaseImplementation + { + if (!($this->value instanceof TestCaseImplementation && $this->type === 'implementation')) { throw new Exception( "Expected implementation; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'templateId': $value = $this->value; $result['templateId'] = $value; @@ -137,26 +145,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -165,29 +174,30 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'templateId': - if (!array_key_exists('templateId', $data)){ + if (!array_key_exists('templateId', $data)) { throw new Exception( "JSON data is missing property 'templateId'", ); } - + $args['value'] = $data['templateId']; break; case 'implementation': @@ -198,7 +208,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/trace/src/V2/Problem/TestCaseMetadata.php b/seed/php-model/trace/src/V2/Problem/TestCaseMetadata.php index 07350d852c32..0157e0f5d46e 100644 --- a/seed/php-model/trace/src/V2/Problem/TestCaseMetadata.php +++ b/seed/php-model/trace/src/V2/Problem/TestCaseMetadata.php @@ -34,15 +34,17 @@ class TestCaseMetadata extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->name = $values['name'];$this->hidden = $values['hidden']; + ) { + $this->id = $values['id']; + $this->name = $values['name']; + $this->hidden = $values['hidden']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/Problem/TestCaseTemplate.php b/seed/php-model/trace/src/V2/Problem/TestCaseTemplate.php index a1df51eab7e7..d1f77b49be0b 100644 --- a/seed/php-model/trace/src/V2/Problem/TestCaseTemplate.php +++ b/seed/php-model/trace/src/V2/Problem/TestCaseTemplate.php @@ -34,15 +34,17 @@ class TestCaseTemplate extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->templateId = $values['templateId'];$this->name = $values['name'];$this->implementation = $values['implementation']; + ) { + $this->templateId = $values['templateId']; + $this->name = $values['name']; + $this->implementation = $values['implementation']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/Problem/TestCaseV2.php b/seed/php-model/trace/src/V2/Problem/TestCaseV2.php index 81b0f7dd63d1..aeebf4c6e9bf 100644 --- a/seed/php-model/trace/src/V2/Problem/TestCaseV2.php +++ b/seed/php-model/trace/src/V2/Problem/TestCaseV2.php @@ -43,15 +43,18 @@ class TestCaseV2 extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->metadata = $values['metadata'];$this->implementation = $values['implementation'];$this->arguments = $values['arguments'];$this->expects = $values['expects'] ?? null; + ) { + $this->metadata = $values['metadata']; + $this->implementation = $values['implementation']; + $this->arguments = $values['arguments']; + $this->expects = $values['expects'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/Problem/TestCaseWithActualResultImplementation.php b/seed/php-model/trace/src/V2/Problem/TestCaseWithActualResultImplementation.php index 852fb72cf50d..97d2d32b9cf2 100644 --- a/seed/php-model/trace/src/V2/Problem/TestCaseWithActualResultImplementation.php +++ b/seed/php-model/trace/src/V2/Problem/TestCaseWithActualResultImplementation.php @@ -27,15 +27,16 @@ class TestCaseWithActualResultImplementation extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->getActualResult = $values['getActualResult'];$this->assertCorrectnessCheck = $values['assertCorrectnessCheck']; + ) { + $this->getActualResult = $values['getActualResult']; + $this->assertCorrectnessCheck = $values['assertCorrectnessCheck']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/Problem/VoidFunctionDefinition.php b/seed/php-model/trace/src/V2/Problem/VoidFunctionDefinition.php index 0a554374602e..e702ae00e692 100644 --- a/seed/php-model/trace/src/V2/Problem/VoidFunctionDefinition.php +++ b/seed/php-model/trace/src/V2/Problem/VoidFunctionDefinition.php @@ -28,15 +28,16 @@ class VoidFunctionDefinition extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->parameters = $values['parameters'];$this->code = $values['code']; + ) { + $this->parameters = $values['parameters']; + $this->code = $values['code']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/Problem/VoidFunctionDefinitionThatTakesActualResult.php b/seed/php-model/trace/src/V2/Problem/VoidFunctionDefinitionThatTakesActualResult.php index 9b8c41d9d1c6..301fa4ebcaba 100644 --- a/seed/php-model/trace/src/V2/Problem/VoidFunctionDefinitionThatTakesActualResult.php +++ b/seed/php-model/trace/src/V2/Problem/VoidFunctionDefinitionThatTakesActualResult.php @@ -31,15 +31,16 @@ class VoidFunctionDefinitionThatTakesActualResult extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->additionalParameters = $values['additionalParameters'];$this->code = $values['code']; + ) { + $this->additionalParameters = $values['additionalParameters']; + $this->code = $values['code']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/Problem/VoidFunctionSignature.php b/seed/php-model/trace/src/V2/Problem/VoidFunctionSignature.php index 468ba2365892..3db7dd72d100 100644 --- a/seed/php-model/trace/src/V2/Problem/VoidFunctionSignature.php +++ b/seed/php-model/trace/src/V2/Problem/VoidFunctionSignature.php @@ -21,15 +21,15 @@ class VoidFunctionSignature extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->parameters = $values['parameters']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/Problem/VoidFunctionSignatureThatTakesActualResult.php b/seed/php-model/trace/src/V2/Problem/VoidFunctionSignatureThatTakesActualResult.php index bef4b9ed371d..d62dc03ca1df 100644 --- a/seed/php-model/trace/src/V2/Problem/VoidFunctionSignatureThatTakesActualResult.php +++ b/seed/php-model/trace/src/V2/Problem/VoidFunctionSignatureThatTakesActualResult.php @@ -29,15 +29,16 @@ class VoidFunctionSignatureThatTakesActualResult extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->parameters = $values['parameters'];$this->actualResultType = $values['actualResultType']; + ) { + $this->parameters = $values['parameters']; + $this->actualResultType = $values['actualResultType']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/V3/Problem/AssertCorrectnessCheck.php b/seed/php-model/trace/src/V2/V3/Problem/AssertCorrectnessCheck.php index 6342ea2f7f5a..3a562c2d2f6e 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/AssertCorrectnessCheck.php +++ b/seed/php-model/trace/src/V2/V3/Problem/AssertCorrectnessCheck.php @@ -42,16 +42,17 @@ class AssertCorrectnessCheck extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param DeepEqualityCorrectnessCheck $deepEquality * @return AssertCorrectnessCheck */ - public static function deepEquality(DeepEqualityCorrectnessCheck $deepEquality): AssertCorrectnessCheck { + public static function deepEquality(DeepEqualityCorrectnessCheck $deepEquality): AssertCorrectnessCheck + { return new AssertCorrectnessCheck([ 'type' => 'deepEquality', 'value' => $deepEquality, @@ -62,7 +63,8 @@ public static function deepEquality(DeepEqualityCorrectnessCheck $deepEquality): * @param VoidFunctionDefinitionThatTakesActualResult $custom * @return AssertCorrectnessCheck */ - public static function custom(VoidFunctionDefinitionThatTakesActualResult $custom): AssertCorrectnessCheck { + public static function custom(VoidFunctionDefinitionThatTakesActualResult $custom): AssertCorrectnessCheck + { return new AssertCorrectnessCheck([ 'type' => 'custom', 'value' => $custom, @@ -72,61 +74,67 @@ public static function custom(VoidFunctionDefinitionThatTakesActualResult $custo /** * @return bool */ - public function isDeepEquality(): bool { - return $this->value instanceof DeepEqualityCorrectnessCheck&& $this->type === 'deepEquality'; + public function isDeepEquality(): bool + { + return $this->value instanceof DeepEqualityCorrectnessCheck && $this->type === 'deepEquality'; } /** * @return DeepEqualityCorrectnessCheck */ - public function asDeepEquality(): DeepEqualityCorrectnessCheck { - if (!($this->value instanceof DeepEqualityCorrectnessCheck&& $this->type === 'deepEquality')){ + public function asDeepEquality(): DeepEqualityCorrectnessCheck + { + if (!($this->value instanceof DeepEqualityCorrectnessCheck && $this->type === 'deepEquality')) { throw new Exception( "Expected deepEquality; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isCustom(): bool { - return $this->value instanceof VoidFunctionDefinitionThatTakesActualResult&& $this->type === 'custom'; + public function isCustom(): bool + { + return $this->value instanceof VoidFunctionDefinitionThatTakesActualResult && $this->type === 'custom'; } /** * @return VoidFunctionDefinitionThatTakesActualResult */ - public function asCustom(): VoidFunctionDefinitionThatTakesActualResult { - if (!($this->value instanceof VoidFunctionDefinitionThatTakesActualResult&& $this->type === 'custom')){ + public function asCustom(): VoidFunctionDefinitionThatTakesActualResult + { + if (!($this->value instanceof VoidFunctionDefinitionThatTakesActualResult && $this->type === 'custom')) { throw new Exception( "Expected custom; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'deepEquality': $value = $this->asDeepEquality()->jsonSerialize(); $result = array_merge($value, $result); @@ -137,26 +145,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -165,22 +174,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'deepEquality': $args['value'] = DeepEqualityCorrectnessCheck::jsonDeserialize($data); break; @@ -192,7 +202,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/trace/src/V2/V3/Problem/BasicCustomFiles.php b/seed/php-model/trace/src/V2/V3/Problem/BasicCustomFiles.php index 34480c0c6397..60581011d9ec 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/BasicCustomFiles.php +++ b/seed/php-model/trace/src/V2/V3/Problem/BasicCustomFiles.php @@ -43,15 +43,18 @@ class BasicCustomFiles extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->methodName = $values['methodName'];$this->signature = $values['signature'];$this->additionalFiles = $values['additionalFiles'];$this->basicTestCaseTemplate = $values['basicTestCaseTemplate']; + ) { + $this->methodName = $values['methodName']; + $this->signature = $values['signature']; + $this->additionalFiles = $values['additionalFiles']; + $this->basicTestCaseTemplate = $values['basicTestCaseTemplate']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/V3/Problem/BasicTestCaseTemplate.php b/seed/php-model/trace/src/V2/V3/Problem/BasicTestCaseTemplate.php index 99b6a88d5cdd..9170eeb1be3d 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/BasicTestCaseTemplate.php +++ b/seed/php-model/trace/src/V2/V3/Problem/BasicTestCaseTemplate.php @@ -41,15 +41,18 @@ class BasicTestCaseTemplate extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->templateId = $values['templateId'];$this->name = $values['name'];$this->description = $values['description'];$this->expectedValueParameterId = $values['expectedValueParameterId']; + ) { + $this->templateId = $values['templateId']; + $this->name = $values['name']; + $this->description = $values['description']; + $this->expectedValueParameterId = $values['expectedValueParameterId']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/V3/Problem/CreateProblemRequestV2.php b/seed/php-model/trace/src/V2/V3/Problem/CreateProblemRequestV2.php index a11c9939644a..90f5348b6ac1 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/CreateProblemRequestV2.php +++ b/seed/php-model/trace/src/V2/V3/Problem/CreateProblemRequestV2.php @@ -65,15 +65,21 @@ class CreateProblemRequestV2 extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->problemName = $values['problemName'];$this->problemDescription = $values['problemDescription'];$this->customFiles = $values['customFiles'];$this->customTestCaseTemplates = $values['customTestCaseTemplates'];$this->testcases = $values['testcases'];$this->supportedLanguages = $values['supportedLanguages'];$this->isPublic = $values['isPublic']; + ) { + $this->problemName = $values['problemName']; + $this->problemDescription = $values['problemDescription']; + $this->customFiles = $values['customFiles']; + $this->customTestCaseTemplates = $values['customTestCaseTemplates']; + $this->testcases = $values['testcases']; + $this->supportedLanguages = $values['supportedLanguages']; + $this->isPublic = $values['isPublic']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/V3/Problem/CustomFiles.php b/seed/php-model/trace/src/V2/V3/Problem/CustomFiles.php index 6186e07b9ddf..afc390b3dcb5 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/CustomFiles.php +++ b/seed/php-model/trace/src/V2/V3/Problem/CustomFiles.php @@ -43,16 +43,17 @@ class CustomFiles extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param BasicCustomFiles $basic * @return CustomFiles */ - public static function basic(BasicCustomFiles $basic): CustomFiles { + public static function basic(BasicCustomFiles $basic): CustomFiles + { return new CustomFiles([ 'type' => 'basic', 'value' => $basic, @@ -63,7 +64,8 @@ public static function basic(BasicCustomFiles $basic): CustomFiles { * @param array, Files> $custom * @return CustomFiles */ - public static function custom(array $custom): CustomFiles { + public static function custom(array $custom): CustomFiles + { return new CustomFiles([ 'type' => 'custom', 'value' => $custom, @@ -73,61 +75,67 @@ public static function custom(array $custom): CustomFiles { /** * @return bool */ - public function isBasic(): bool { - return $this->value instanceof BasicCustomFiles&& $this->type === 'basic'; + public function isBasic(): bool + { + return $this->value instanceof BasicCustomFiles && $this->type === 'basic'; } /** * @return BasicCustomFiles */ - public function asBasic(): BasicCustomFiles { - if (!($this->value instanceof BasicCustomFiles&& $this->type === 'basic')){ + public function asBasic(): BasicCustomFiles + { + if (!($this->value instanceof BasicCustomFiles && $this->type === 'basic')) { throw new Exception( "Expected basic; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isCustom(): bool { - return is_array($this->value)&& $this->type === 'custom'; + public function isCustom(): bool + { + return is_array($this->value) && $this->type === 'custom'; } /** * @return array, Files> */ - public function asCustom(): array { - if (!(is_array($this->value)&& $this->type === 'custom')){ + public function asCustom(): array + { + if (!(is_array($this->value) && $this->type === 'custom')) { throw new Exception( "Expected custom; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'basic': $value = $this->asBasic()->jsonSerialize(); $result = array_merge($value, $result); @@ -138,26 +146,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -166,32 +175,33 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'basic': $args['value'] = BasicCustomFiles::jsonDeserialize($data); break; case 'custom': - if (!array_key_exists('custom', $data)){ + if (!array_key_exists('custom', $data)) { throw new Exception( "JSON data is missing property 'custom'", ); } - + $args['value'] = $data['custom']; break; case '_unknown': @@ -199,7 +209,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/trace/src/V2/V3/Problem/DeepEqualityCorrectnessCheck.php b/seed/php-model/trace/src/V2/V3/Problem/DeepEqualityCorrectnessCheck.php index e42c5df1fcc4..af4a5a122675 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/DeepEqualityCorrectnessCheck.php +++ b/seed/php-model/trace/src/V2/V3/Problem/DeepEqualityCorrectnessCheck.php @@ -20,15 +20,15 @@ class DeepEqualityCorrectnessCheck extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->expectedValueParameterId = $values['expectedValueParameterId']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/V3/Problem/DefaultProvidedFile.php b/seed/php-model/trace/src/V2/V3/Problem/DefaultProvidedFile.php index abd495a7fcc5..ed83b285c567 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/DefaultProvidedFile.php +++ b/seed/php-model/trace/src/V2/V3/Problem/DefaultProvidedFile.php @@ -29,15 +29,16 @@ class DefaultProvidedFile extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->file = $values['file'];$this->relatedTypes = $values['relatedTypes']; + ) { + $this->file = $values['file']; + $this->relatedTypes = $values['relatedTypes']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/V3/Problem/FileInfoV2.php b/seed/php-model/trace/src/V2/V3/Problem/FileInfoV2.php index 2524e4c314b1..1c1e6923b189 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/FileInfoV2.php +++ b/seed/php-model/trace/src/V2/V3/Problem/FileInfoV2.php @@ -41,15 +41,18 @@ class FileInfoV2 extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->filename = $values['filename'];$this->directory = $values['directory'];$this->contents = $values['contents'];$this->editable = $values['editable']; + ) { + $this->filename = $values['filename']; + $this->directory = $values['directory']; + $this->contents = $values['contents']; + $this->editable = $values['editable']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/V3/Problem/Files.php b/seed/php-model/trace/src/V2/V3/Problem/Files.php index b5edfc0b7b8f..15e556d2daff 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/Files.php +++ b/seed/php-model/trace/src/V2/V3/Problem/Files.php @@ -21,15 +21,15 @@ class Files extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->files = $values['files']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/V3/Problem/FunctionImplementation.php b/seed/php-model/trace/src/V2/V3/Problem/FunctionImplementation.php index 6410b8160fe3..37fdba3a04ed 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/FunctionImplementation.php +++ b/seed/php-model/trace/src/V2/V3/Problem/FunctionImplementation.php @@ -27,15 +27,16 @@ class FunctionImplementation extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->impl = $values['impl'];$this->imports = $values['imports'] ?? null; + ) { + $this->impl = $values['impl']; + $this->imports = $values['imports'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/V3/Problem/FunctionImplementationForMultipleLanguages.php b/seed/php-model/trace/src/V2/V3/Problem/FunctionImplementationForMultipleLanguages.php index b413b5310074..f7527bb89c30 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/FunctionImplementationForMultipleLanguages.php +++ b/seed/php-model/trace/src/V2/V3/Problem/FunctionImplementationForMultipleLanguages.php @@ -22,15 +22,15 @@ class FunctionImplementationForMultipleLanguages extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->codeByLanguage = $values['codeByLanguage']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/V3/Problem/FunctionSignature.php b/seed/php-model/trace/src/V2/V3/Problem/FunctionSignature.php index eb8d897d5ecc..5c3828c7ae02 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/FunctionSignature.php +++ b/seed/php-model/trace/src/V2/V3/Problem/FunctionSignature.php @@ -46,16 +46,17 @@ class FunctionSignature extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param VoidFunctionSignature $void * @return FunctionSignature */ - public static function void(VoidFunctionSignature $void): FunctionSignature { + public static function void(VoidFunctionSignature $void): FunctionSignature + { return new FunctionSignature([ 'type' => 'void', 'value' => $void, @@ -66,7 +67,8 @@ public static function void(VoidFunctionSignature $void): FunctionSignature { * @param NonVoidFunctionSignature $nonVoid * @return FunctionSignature */ - public static function nonVoid(NonVoidFunctionSignature $nonVoid): FunctionSignature { + public static function nonVoid(NonVoidFunctionSignature $nonVoid): FunctionSignature + { return new FunctionSignature([ 'type' => 'nonVoid', 'value' => $nonVoid, @@ -77,7 +79,8 @@ public static function nonVoid(NonVoidFunctionSignature $nonVoid): FunctionSigna * @param VoidFunctionSignatureThatTakesActualResult $voidThatTakesActualResult * @return FunctionSignature */ - public static function voidThatTakesActualResult(VoidFunctionSignatureThatTakesActualResult $voidThatTakesActualResult): FunctionSignature { + public static function voidThatTakesActualResult(VoidFunctionSignatureThatTakesActualResult $voidThatTakesActualResult): FunctionSignature + { return new FunctionSignature([ 'type' => 'voidThatTakesActualResult', 'value' => $voidThatTakesActualResult, @@ -87,81 +90,89 @@ public static function voidThatTakesActualResult(VoidFunctionSignatureThatTakesA /** * @return bool */ - public function isVoid(): bool { - return $this->value instanceof VoidFunctionSignature&& $this->type === 'void'; + public function isVoid(): bool + { + return $this->value instanceof VoidFunctionSignature && $this->type === 'void'; } /** * @return VoidFunctionSignature */ - public function asVoid(): VoidFunctionSignature { - if (!($this->value instanceof VoidFunctionSignature&& $this->type === 'void')){ + public function asVoid(): VoidFunctionSignature + { + if (!($this->value instanceof VoidFunctionSignature && $this->type === 'void')) { throw new Exception( "Expected void; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isNonVoid(): bool { - return $this->value instanceof NonVoidFunctionSignature&& $this->type === 'nonVoid'; + public function isNonVoid(): bool + { + return $this->value instanceof NonVoidFunctionSignature && $this->type === 'nonVoid'; } /** * @return NonVoidFunctionSignature */ - public function asNonVoid(): NonVoidFunctionSignature { - if (!($this->value instanceof NonVoidFunctionSignature&& $this->type === 'nonVoid')){ + public function asNonVoid(): NonVoidFunctionSignature + { + if (!($this->value instanceof NonVoidFunctionSignature && $this->type === 'nonVoid')) { throw new Exception( "Expected nonVoid; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isVoidThatTakesActualResult(): bool { - return $this->value instanceof VoidFunctionSignatureThatTakesActualResult&& $this->type === 'voidThatTakesActualResult'; + public function isVoidThatTakesActualResult(): bool + { + return $this->value instanceof VoidFunctionSignatureThatTakesActualResult && $this->type === 'voidThatTakesActualResult'; } /** * @return VoidFunctionSignatureThatTakesActualResult */ - public function asVoidThatTakesActualResult(): VoidFunctionSignatureThatTakesActualResult { - if (!($this->value instanceof VoidFunctionSignatureThatTakesActualResult&& $this->type === 'voidThatTakesActualResult')){ + public function asVoidThatTakesActualResult(): VoidFunctionSignatureThatTakesActualResult + { + if (!($this->value instanceof VoidFunctionSignatureThatTakesActualResult && $this->type === 'voidThatTakesActualResult')) { throw new Exception( "Expected voidThatTakesActualResult; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'void': $value = $this->asVoid()->jsonSerialize(); $result = array_merge($value, $result); @@ -176,26 +187,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -204,22 +216,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'void': $args['value'] = VoidFunctionSignature::jsonDeserialize($data); break; @@ -234,7 +247,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/trace/src/V2/V3/Problem/GeneratedFiles.php b/seed/php-model/trace/src/V2/V3/Problem/GeneratedFiles.php index 432dc234d1f1..53659057d580 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/GeneratedFiles.php +++ b/seed/php-model/trace/src/V2/V3/Problem/GeneratedFiles.php @@ -36,15 +36,17 @@ class GeneratedFiles extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->generatedTestCaseFiles = $values['generatedTestCaseFiles'];$this->generatedTemplateFiles = $values['generatedTemplateFiles'];$this->other = $values['other']; + ) { + $this->generatedTestCaseFiles = $values['generatedTestCaseFiles']; + $this->generatedTemplateFiles = $values['generatedTemplateFiles']; + $this->other = $values['other']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/V3/Problem/GetBasicSolutionFileRequest.php b/seed/php-model/trace/src/V2/V3/Problem/GetBasicSolutionFileRequest.php index 46ba86868bcb..8c9525a037a1 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/GetBasicSolutionFileRequest.php +++ b/seed/php-model/trace/src/V2/V3/Problem/GetBasicSolutionFileRequest.php @@ -27,15 +27,16 @@ class GetBasicSolutionFileRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->methodName = $values['methodName'];$this->signature = $values['signature']; + ) { + $this->methodName = $values['methodName']; + $this->signature = $values['signature']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/V3/Problem/GetBasicSolutionFileResponse.php b/seed/php-model/trace/src/V2/V3/Problem/GetBasicSolutionFileResponse.php index 54f249be73da..65637aa44ca3 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/GetBasicSolutionFileResponse.php +++ b/seed/php-model/trace/src/V2/V3/Problem/GetBasicSolutionFileResponse.php @@ -22,15 +22,15 @@ class GetBasicSolutionFileResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->solutionFileByLanguage = $values['solutionFileByLanguage']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/V3/Problem/GetFunctionSignatureRequest.php b/seed/php-model/trace/src/V2/V3/Problem/GetFunctionSignatureRequest.php index e00f208d6efc..9c2f4d2b0814 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/GetFunctionSignatureRequest.php +++ b/seed/php-model/trace/src/V2/V3/Problem/GetFunctionSignatureRequest.php @@ -20,15 +20,15 @@ class GetFunctionSignatureRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->functionSignature = $values['functionSignature']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/V3/Problem/GetFunctionSignatureResponse.php b/seed/php-model/trace/src/V2/V3/Problem/GetFunctionSignatureResponse.php index c85b4ce1740b..bec0ecc201a3 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/GetFunctionSignatureResponse.php +++ b/seed/php-model/trace/src/V2/V3/Problem/GetFunctionSignatureResponse.php @@ -22,15 +22,15 @@ class GetFunctionSignatureResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->functionByLanguage = $values['functionByLanguage']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/V3/Problem/GetGeneratedTestCaseFileRequest.php b/seed/php-model/trace/src/V2/V3/Problem/GetGeneratedTestCaseFileRequest.php index 12ac36d579ab..7727d1adb295 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/GetGeneratedTestCaseFileRequest.php +++ b/seed/php-model/trace/src/V2/V3/Problem/GetGeneratedTestCaseFileRequest.php @@ -27,15 +27,16 @@ class GetGeneratedTestCaseFileRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->template = $values['template'] ?? null;$this->testCase = $values['testCase']; + ) { + $this->template = $values['template'] ?? null; + $this->testCase = $values['testCase']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/V3/Problem/GetGeneratedTestCaseTemplateFileRequest.php b/seed/php-model/trace/src/V2/V3/Problem/GetGeneratedTestCaseTemplateFileRequest.php index 81f386e64a6f..779cb151e011 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/GetGeneratedTestCaseTemplateFileRequest.php +++ b/seed/php-model/trace/src/V2/V3/Problem/GetGeneratedTestCaseTemplateFileRequest.php @@ -20,15 +20,15 @@ class GetGeneratedTestCaseTemplateFileRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->template = $values['template']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/V3/Problem/LightweightProblemInfoV2.php b/seed/php-model/trace/src/V2/V3/Problem/LightweightProblemInfoV2.php index ded475b21e3a..7e4dd5c4df5f 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/LightweightProblemInfoV2.php +++ b/seed/php-model/trace/src/V2/V3/Problem/LightweightProblemInfoV2.php @@ -43,15 +43,18 @@ class LightweightProblemInfoV2 extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->problemId = $values['problemId'];$this->problemName = $values['problemName'];$this->problemVersion = $values['problemVersion'];$this->variableTypes = $values['variableTypes']; + ) { + $this->problemId = $values['problemId']; + $this->problemName = $values['problemName']; + $this->problemVersion = $values['problemVersion']; + $this->variableTypes = $values['variableTypes']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/V3/Problem/NonVoidFunctionDefinition.php b/seed/php-model/trace/src/V2/V3/Problem/NonVoidFunctionDefinition.php index 9b97a2c121f3..4fee8b801d0b 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/NonVoidFunctionDefinition.php +++ b/seed/php-model/trace/src/V2/V3/Problem/NonVoidFunctionDefinition.php @@ -27,15 +27,16 @@ class NonVoidFunctionDefinition extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->signature = $values['signature'];$this->code = $values['code']; + ) { + $this->signature = $values['signature']; + $this->code = $values['code']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/V3/Problem/NonVoidFunctionSignature.php b/seed/php-model/trace/src/V2/V3/Problem/NonVoidFunctionSignature.php index bce25f0468f3..80e3a7010b13 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/NonVoidFunctionSignature.php +++ b/seed/php-model/trace/src/V2/V3/Problem/NonVoidFunctionSignature.php @@ -29,15 +29,16 @@ class NonVoidFunctionSignature extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->parameters = $values['parameters'];$this->returnType = $values['returnType']; + ) { + $this->parameters = $values['parameters']; + $this->returnType = $values['returnType']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/V3/Problem/Parameter.php b/seed/php-model/trace/src/V2/V3/Problem/Parameter.php index 1e2568684ca9..ca76ac3d2c1f 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/Parameter.php +++ b/seed/php-model/trace/src/V2/V3/Problem/Parameter.php @@ -35,15 +35,17 @@ class Parameter extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->parameterId = $values['parameterId'];$this->name = $values['name'];$this->variableType = $values['variableType']; + ) { + $this->parameterId = $values['parameterId']; + $this->name = $values['name']; + $this->variableType = $values['variableType']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/V3/Problem/ProblemInfoV2.php b/seed/php-model/trace/src/V2/V3/Problem/ProblemInfoV2.php index 3fd7192dabcf..693b3ded1e8b 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/ProblemInfoV2.php +++ b/seed/php-model/trace/src/V2/V3/Problem/ProblemInfoV2.php @@ -86,15 +86,24 @@ class ProblemInfoV2 extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->problemId = $values['problemId'];$this->problemDescription = $values['problemDescription'];$this->problemName = $values['problemName'];$this->problemVersion = $values['problemVersion'];$this->supportedLanguages = $values['supportedLanguages'];$this->customFiles = $values['customFiles'];$this->generatedFiles = $values['generatedFiles'];$this->customTestCaseTemplates = $values['customTestCaseTemplates'];$this->testcases = $values['testcases'];$this->isPublic = $values['isPublic']; + ) { + $this->problemId = $values['problemId']; + $this->problemDescription = $values['problemDescription']; + $this->problemName = $values['problemName']; + $this->problemVersion = $values['problemVersion']; + $this->supportedLanguages = $values['supportedLanguages']; + $this->customFiles = $values['customFiles']; + $this->generatedFiles = $values['generatedFiles']; + $this->customTestCaseTemplates = $values['customTestCaseTemplates']; + $this->testcases = $values['testcases']; + $this->isPublic = $values['isPublic']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/V3/Problem/TestCaseExpects.php b/seed/php-model/trace/src/V2/V3/Problem/TestCaseExpects.php index 4f167b772612..1d3b6be492c2 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/TestCaseExpects.php +++ b/seed/php-model/trace/src/V2/V3/Problem/TestCaseExpects.php @@ -20,15 +20,15 @@ class TestCaseExpects extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->expectedStdout = $values['expectedStdout'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/V3/Problem/TestCaseFunction.php b/seed/php-model/trace/src/V2/V3/Problem/TestCaseFunction.php index af8b29b85bcf..373242da9731 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/TestCaseFunction.php +++ b/seed/php-model/trace/src/V2/V3/Problem/TestCaseFunction.php @@ -42,16 +42,17 @@ class TestCaseFunction extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param TestCaseWithActualResultImplementation $withActualResult * @return TestCaseFunction */ - public static function withActualResult(TestCaseWithActualResultImplementation $withActualResult): TestCaseFunction { + public static function withActualResult(TestCaseWithActualResultImplementation $withActualResult): TestCaseFunction + { return new TestCaseFunction([ 'type' => 'withActualResult', 'value' => $withActualResult, @@ -62,7 +63,8 @@ public static function withActualResult(TestCaseWithActualResultImplementation $ * @param VoidFunctionDefinition $custom * @return TestCaseFunction */ - public static function custom(VoidFunctionDefinition $custom): TestCaseFunction { + public static function custom(VoidFunctionDefinition $custom): TestCaseFunction + { return new TestCaseFunction([ 'type' => 'custom', 'value' => $custom, @@ -72,61 +74,67 @@ public static function custom(VoidFunctionDefinition $custom): TestCaseFunction /** * @return bool */ - public function isWithActualResult(): bool { - return $this->value instanceof TestCaseWithActualResultImplementation&& $this->type === 'withActualResult'; + public function isWithActualResult(): bool + { + return $this->value instanceof TestCaseWithActualResultImplementation && $this->type === 'withActualResult'; } /** * @return TestCaseWithActualResultImplementation */ - public function asWithActualResult(): TestCaseWithActualResultImplementation { - if (!($this->value instanceof TestCaseWithActualResultImplementation&& $this->type === 'withActualResult')){ + public function asWithActualResult(): TestCaseWithActualResultImplementation + { + if (!($this->value instanceof TestCaseWithActualResultImplementation && $this->type === 'withActualResult')) { throw new Exception( "Expected withActualResult; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isCustom(): bool { - return $this->value instanceof VoidFunctionDefinition&& $this->type === 'custom'; + public function isCustom(): bool + { + return $this->value instanceof VoidFunctionDefinition && $this->type === 'custom'; } /** * @return VoidFunctionDefinition */ - public function asCustom(): VoidFunctionDefinition { - if (!($this->value instanceof VoidFunctionDefinition&& $this->type === 'custom')){ + public function asCustom(): VoidFunctionDefinition + { + if (!($this->value instanceof VoidFunctionDefinition && $this->type === 'custom')) { throw new Exception( "Expected custom; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'withActualResult': $value = $this->asWithActualResult()->jsonSerialize(); $result = array_merge($value, $result); @@ -137,26 +145,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -165,22 +174,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'withActualResult': $args['value'] = TestCaseWithActualResultImplementation::jsonDeserialize($data); break; @@ -192,7 +202,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/trace/src/V2/V3/Problem/TestCaseImplementation.php b/seed/php-model/trace/src/V2/V3/Problem/TestCaseImplementation.php index f1d851b9cbcc..ff4fc8861308 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/TestCaseImplementation.php +++ b/seed/php-model/trace/src/V2/V3/Problem/TestCaseImplementation.php @@ -27,15 +27,16 @@ class TestCaseImplementation extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->description = $values['description'];$this->function = $values['function']; + ) { + $this->description = $values['description']; + $this->function = $values['function']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/V3/Problem/TestCaseImplementationDescription.php b/seed/php-model/trace/src/V2/V3/Problem/TestCaseImplementationDescription.php index ab7a849a30d9..597dcb8539b7 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/TestCaseImplementationDescription.php +++ b/seed/php-model/trace/src/V2/V3/Problem/TestCaseImplementationDescription.php @@ -21,15 +21,15 @@ class TestCaseImplementationDescription extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->boards = $values['boards']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/V3/Problem/TestCaseImplementationDescriptionBoard.php b/seed/php-model/trace/src/V2/V3/Problem/TestCaseImplementationDescriptionBoard.php index 0ca944180de3..d8bbc7e51844 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/TestCaseImplementationDescriptionBoard.php +++ b/seed/php-model/trace/src/V2/V3/Problem/TestCaseImplementationDescriptionBoard.php @@ -40,16 +40,17 @@ class TestCaseImplementationDescriptionBoard extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param string $html * @return TestCaseImplementationDescriptionBoard */ - public static function html(string $html): TestCaseImplementationDescriptionBoard { + public static function html(string $html): TestCaseImplementationDescriptionBoard + { return new TestCaseImplementationDescriptionBoard([ 'type' => 'html', 'value' => $html, @@ -60,7 +61,8 @@ public static function html(string $html): TestCaseImplementationDescriptionBoar * @param string $paramId * @return TestCaseImplementationDescriptionBoard */ - public static function paramId(string $paramId): TestCaseImplementationDescriptionBoard { + public static function paramId(string $paramId): TestCaseImplementationDescriptionBoard + { return new TestCaseImplementationDescriptionBoard([ 'type' => 'paramId', 'value' => $paramId, @@ -70,61 +72,67 @@ public static function paramId(string $paramId): TestCaseImplementationDescripti /** * @return bool */ - public function isHtml(): bool { - return is_string($this->value)&& $this->type === 'html'; + public function isHtml(): bool + { + return is_string($this->value) && $this->type === 'html'; } /** * @return string */ - public function asHtml(): string { - if (!(is_string($this->value)&& $this->type === 'html')){ + public function asHtml(): string + { + if (!(is_string($this->value) && $this->type === 'html')) { throw new Exception( "Expected html; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isParamId(): bool { - return is_string($this->value)&& $this->type === 'paramId'; + public function isParamId(): bool + { + return is_string($this->value) && $this->type === 'paramId'; } /** * @return string */ - public function asParamId(): string { - if (!(is_string($this->value)&& $this->type === 'paramId')){ + public function asParamId(): string + { + if (!(is_string($this->value) && $this->type === 'paramId')) { throw new Exception( "Expected paramId; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'html': $value = $this->value; $result['html'] = $value; @@ -135,26 +143,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -163,38 +172,39 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'html': - if (!array_key_exists('html', $data)){ + if (!array_key_exists('html', $data)) { throw new Exception( "JSON data is missing property 'html'", ); } - + $args['value'] = $data['html']; break; case 'paramId': - if (!array_key_exists('paramId', $data)){ + if (!array_key_exists('paramId', $data)) { throw new Exception( "JSON data is missing property 'paramId'", ); } - + $args['value'] = $data['paramId']; break; case '_unknown': @@ -202,7 +212,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/trace/src/V2/V3/Problem/TestCaseImplementationReference.php b/seed/php-model/trace/src/V2/V3/Problem/TestCaseImplementationReference.php index b55d3dfab2fe..f8555f3f5dc8 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/TestCaseImplementationReference.php +++ b/seed/php-model/trace/src/V2/V3/Problem/TestCaseImplementationReference.php @@ -42,16 +42,17 @@ class TestCaseImplementationReference extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param string $templateId * @return TestCaseImplementationReference */ - public static function templateId(string $templateId): TestCaseImplementationReference { + public static function templateId(string $templateId): TestCaseImplementationReference + { return new TestCaseImplementationReference([ 'type' => 'templateId', 'value' => $templateId, @@ -62,7 +63,8 @@ public static function templateId(string $templateId): TestCaseImplementationRef * @param TestCaseImplementation $implementation * @return TestCaseImplementationReference */ - public static function implementation(TestCaseImplementation $implementation): TestCaseImplementationReference { + public static function implementation(TestCaseImplementation $implementation): TestCaseImplementationReference + { return new TestCaseImplementationReference([ 'type' => 'implementation', 'value' => $implementation, @@ -72,61 +74,67 @@ public static function implementation(TestCaseImplementation $implementation): T /** * @return bool */ - public function isTemplateId(): bool { - return is_string($this->value)&& $this->type === 'templateId'; + public function isTemplateId(): bool + { + return is_string($this->value) && $this->type === 'templateId'; } /** * @return string */ - public function asTemplateId(): string { - if (!(is_string($this->value)&& $this->type === 'templateId')){ + public function asTemplateId(): string + { + if (!(is_string($this->value) && $this->type === 'templateId')) { throw new Exception( "Expected templateId; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isImplementation(): bool { - return $this->value instanceof TestCaseImplementation&& $this->type === 'implementation'; + public function isImplementation(): bool + { + return $this->value instanceof TestCaseImplementation && $this->type === 'implementation'; } /** * @return TestCaseImplementation */ - public function asImplementation(): TestCaseImplementation { - if (!($this->value instanceof TestCaseImplementation&& $this->type === 'implementation')){ + public function asImplementation(): TestCaseImplementation + { + if (!($this->value instanceof TestCaseImplementation && $this->type === 'implementation')) { throw new Exception( "Expected implementation; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'templateId': $value = $this->value; $result['templateId'] = $value; @@ -137,26 +145,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -165,29 +174,30 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'templateId': - if (!array_key_exists('templateId', $data)){ + if (!array_key_exists('templateId', $data)) { throw new Exception( "JSON data is missing property 'templateId'", ); } - + $args['value'] = $data['templateId']; break; case 'implementation': @@ -198,7 +208,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/trace/src/V2/V3/Problem/TestCaseMetadata.php b/seed/php-model/trace/src/V2/V3/Problem/TestCaseMetadata.php index 51fe5b7ea491..05005989e9e8 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/TestCaseMetadata.php +++ b/seed/php-model/trace/src/V2/V3/Problem/TestCaseMetadata.php @@ -34,15 +34,17 @@ class TestCaseMetadata extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->name = $values['name'];$this->hidden = $values['hidden']; + ) { + $this->id = $values['id']; + $this->name = $values['name']; + $this->hidden = $values['hidden']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/V3/Problem/TestCaseTemplate.php b/seed/php-model/trace/src/V2/V3/Problem/TestCaseTemplate.php index 6b05431e8a66..f129ec4fd21a 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/TestCaseTemplate.php +++ b/seed/php-model/trace/src/V2/V3/Problem/TestCaseTemplate.php @@ -34,15 +34,17 @@ class TestCaseTemplate extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->templateId = $values['templateId'];$this->name = $values['name'];$this->implementation = $values['implementation']; + ) { + $this->templateId = $values['templateId']; + $this->name = $values['name']; + $this->implementation = $values['implementation']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/V3/Problem/TestCaseV2.php b/seed/php-model/trace/src/V2/V3/Problem/TestCaseV2.php index 8311158caf3f..b8c9c3b7f772 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/TestCaseV2.php +++ b/seed/php-model/trace/src/V2/V3/Problem/TestCaseV2.php @@ -43,15 +43,18 @@ class TestCaseV2 extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->metadata = $values['metadata'];$this->implementation = $values['implementation'];$this->arguments = $values['arguments'];$this->expects = $values['expects'] ?? null; + ) { + $this->metadata = $values['metadata']; + $this->implementation = $values['implementation']; + $this->arguments = $values['arguments']; + $this->expects = $values['expects'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/V3/Problem/TestCaseWithActualResultImplementation.php b/seed/php-model/trace/src/V2/V3/Problem/TestCaseWithActualResultImplementation.php index 136b69242858..ecc0d2a745df 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/TestCaseWithActualResultImplementation.php +++ b/seed/php-model/trace/src/V2/V3/Problem/TestCaseWithActualResultImplementation.php @@ -27,15 +27,16 @@ class TestCaseWithActualResultImplementation extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->getActualResult = $values['getActualResult'];$this->assertCorrectnessCheck = $values['assertCorrectnessCheck']; + ) { + $this->getActualResult = $values['getActualResult']; + $this->assertCorrectnessCheck = $values['assertCorrectnessCheck']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/V3/Problem/VoidFunctionDefinition.php b/seed/php-model/trace/src/V2/V3/Problem/VoidFunctionDefinition.php index 8e4801d9eb4e..000a6fd755ae 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/VoidFunctionDefinition.php +++ b/seed/php-model/trace/src/V2/V3/Problem/VoidFunctionDefinition.php @@ -28,15 +28,16 @@ class VoidFunctionDefinition extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->parameters = $values['parameters'];$this->code = $values['code']; + ) { + $this->parameters = $values['parameters']; + $this->code = $values['code']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/V3/Problem/VoidFunctionDefinitionThatTakesActualResult.php b/seed/php-model/trace/src/V2/V3/Problem/VoidFunctionDefinitionThatTakesActualResult.php index 7bbdfca52197..71549c2ec5a2 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/VoidFunctionDefinitionThatTakesActualResult.php +++ b/seed/php-model/trace/src/V2/V3/Problem/VoidFunctionDefinitionThatTakesActualResult.php @@ -31,15 +31,16 @@ class VoidFunctionDefinitionThatTakesActualResult extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->additionalParameters = $values['additionalParameters'];$this->code = $values['code']; + ) { + $this->additionalParameters = $values['additionalParameters']; + $this->code = $values['code']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/V3/Problem/VoidFunctionSignature.php b/seed/php-model/trace/src/V2/V3/Problem/VoidFunctionSignature.php index 5a816ee1753c..29fb079e35cf 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/VoidFunctionSignature.php +++ b/seed/php-model/trace/src/V2/V3/Problem/VoidFunctionSignature.php @@ -21,15 +21,15 @@ class VoidFunctionSignature extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->parameters = $values['parameters']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/src/V2/V3/Problem/VoidFunctionSignatureThatTakesActualResult.php b/seed/php-model/trace/src/V2/V3/Problem/VoidFunctionSignatureThatTakesActualResult.php index 9ac2986cf77d..e3a930eafd44 100644 --- a/seed/php-model/trace/src/V2/V3/Problem/VoidFunctionSignatureThatTakesActualResult.php +++ b/seed/php-model/trace/src/V2/V3/Problem/VoidFunctionSignatureThatTakesActualResult.php @@ -29,15 +29,16 @@ class VoidFunctionSignatureThatTakesActualResult extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->parameters = $values['parameters'];$this->actualResultType = $values['actualResultType']; + ) { + $this->parameters = $values['parameters']; + $this->actualResultType = $values['actualResultType']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/trace/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/trace/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/trace/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/trace/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/trace/tests/Core/Json/EnumTest.php b/seed/php-model/trace/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/trace/tests/Core/Json/EnumTest.php +++ b/seed/php-model/trace/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/trace/tests/Core/Json/TraitTest.php b/seed/php-model/trace/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/trace/tests/Core/Json/TraitTest.php +++ b/seed/php-model/trace/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/trace/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/trace/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/trace/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/trace/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/undiscriminated-union-with-response-property/src/Core/Json/JsonEncoder.php b/seed/php-model/undiscriminated-union-with-response-property/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/undiscriminated-union-with-response-property/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/undiscriminated-union-with-response-property/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/undiscriminated-union-with-response-property/src/Core/Json/JsonProperty.php b/seed/php-model/undiscriminated-union-with-response-property/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/undiscriminated-union-with-response-property/src/Core/Json/JsonProperty.php +++ b/seed/php-model/undiscriminated-union-with-response-property/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/undiscriminated-union-with-response-property/src/Core/Types/ArrayType.php b/seed/php-model/undiscriminated-union-with-response-property/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/undiscriminated-union-with-response-property/src/Core/Types/ArrayType.php +++ b/seed/php-model/undiscriminated-union-with-response-property/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/undiscriminated-union-with-response-property/src/Core/Types/Constant.php b/seed/php-model/undiscriminated-union-with-response-property/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/undiscriminated-union-with-response-property/src/Core/Types/Constant.php +++ b/seed/php-model/undiscriminated-union-with-response-property/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/undiscriminated-union-with-response-property/src/Core/Types/Union.php b/seed/php-model/undiscriminated-union-with-response-property/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/undiscriminated-union-with-response-property/src/Core/Types/Union.php +++ b/seed/php-model/undiscriminated-union-with-response-property/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/undiscriminated-union-with-response-property/src/UnionListResponse.php b/seed/php-model/undiscriminated-union-with-response-property/src/UnionListResponse.php index 23e8b7dd4168..0b4c7ec3df8f 100644 --- a/seed/php-model/undiscriminated-union-with-response-property/src/UnionListResponse.php +++ b/seed/php-model/undiscriminated-union-with-response-property/src/UnionListResponse.php @@ -30,15 +30,15 @@ class UnionListResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->data = $values['data']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/undiscriminated-union-with-response-property/src/UnionResponse.php b/seed/php-model/undiscriminated-union-with-response-property/src/UnionResponse.php index 4ca1b0553f4a..3ca1d956a1c9 100644 --- a/seed/php-model/undiscriminated-union-with-response-property/src/UnionResponse.php +++ b/seed/php-model/undiscriminated-union-with-response-property/src/UnionResponse.php @@ -15,7 +15,7 @@ class UnionResponse extends JsonSerializableType * |VariantC * ) $data */ - #[JsonProperty('data'), Union(VariantA::class,VariantB::class,VariantC::class)] + #[JsonProperty('data'), Union(VariantA::class, VariantB::class, VariantC::class)] public VariantA|VariantB|VariantC $data; /** @@ -29,15 +29,15 @@ class UnionResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->data = $values['data']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/undiscriminated-union-with-response-property/src/VariantA.php b/seed/php-model/undiscriminated-union-with-response-property/src/VariantA.php index 55d4797bd94c..a17fad5a7b29 100644 --- a/seed/php-model/undiscriminated-union-with-response-property/src/VariantA.php +++ b/seed/php-model/undiscriminated-union-with-response-property/src/VariantA.php @@ -27,15 +27,16 @@ class VariantA extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->type = $values['type'];$this->valueA = $values['valueA']; + ) { + $this->type = $values['type']; + $this->valueA = $values['valueA']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/undiscriminated-union-with-response-property/src/VariantB.php b/seed/php-model/undiscriminated-union-with-response-property/src/VariantB.php index 6411d57c3fa8..1b7ca6288360 100644 --- a/seed/php-model/undiscriminated-union-with-response-property/src/VariantB.php +++ b/seed/php-model/undiscriminated-union-with-response-property/src/VariantB.php @@ -27,15 +27,16 @@ class VariantB extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->type = $values['type'];$this->valueB = $values['valueB']; + ) { + $this->type = $values['type']; + $this->valueB = $values['valueB']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/undiscriminated-union-with-response-property/src/VariantC.php b/seed/php-model/undiscriminated-union-with-response-property/src/VariantC.php index be9341294eee..e1c8d419ff5c 100644 --- a/seed/php-model/undiscriminated-union-with-response-property/src/VariantC.php +++ b/seed/php-model/undiscriminated-union-with-response-property/src/VariantC.php @@ -27,15 +27,16 @@ class VariantC extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->type = $values['type'];$this->valueC = $values['valueC']; + ) { + $this->type = $values['type']; + $this->valueC = $values['valueC']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/undiscriminated-union-with-response-property/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/undiscriminated-union-with-response-property/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/undiscriminated-union-with-response-property/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/undiscriminated-union-with-response-property/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/undiscriminated-union-with-response-property/tests/Core/Json/EnumTest.php b/seed/php-model/undiscriminated-union-with-response-property/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/undiscriminated-union-with-response-property/tests/Core/Json/EnumTest.php +++ b/seed/php-model/undiscriminated-union-with-response-property/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/undiscriminated-union-with-response-property/tests/Core/Json/TraitTest.php b/seed/php-model/undiscriminated-union-with-response-property/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/undiscriminated-union-with-response-property/tests/Core/Json/TraitTest.php +++ b/seed/php-model/undiscriminated-union-with-response-property/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/undiscriminated-union-with-response-property/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/undiscriminated-union-with-response-property/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/undiscriminated-union-with-response-property/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/undiscriminated-union-with-response-property/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/undiscriminated-unions/src/Core/Json/JsonEncoder.php b/seed/php-model/undiscriminated-unions/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/undiscriminated-unions/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/undiscriminated-unions/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/undiscriminated-unions/src/Core/Json/JsonProperty.php b/seed/php-model/undiscriminated-unions/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/undiscriminated-unions/src/Core/Json/JsonProperty.php +++ b/seed/php-model/undiscriminated-unions/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/undiscriminated-unions/src/Core/Types/ArrayType.php b/seed/php-model/undiscriminated-unions/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/undiscriminated-unions/src/Core/Types/ArrayType.php +++ b/seed/php-model/undiscriminated-unions/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/undiscriminated-unions/src/Core/Types/Constant.php b/seed/php-model/undiscriminated-unions/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/undiscriminated-unions/src/Core/Types/Constant.php +++ b/seed/php-model/undiscriminated-unions/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/undiscriminated-unions/src/Core/Types/Union.php b/seed/php-model/undiscriminated-unions/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/undiscriminated-unions/src/Core/Types/Union.php +++ b/seed/php-model/undiscriminated-unions/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/undiscriminated-unions/src/Union/ConvertToken.php b/seed/php-model/undiscriminated-unions/src/Union/ConvertToken.php index 817c339434bd..96875143d4eb 100644 --- a/seed/php-model/undiscriminated-unions/src/Union/ConvertToken.php +++ b/seed/php-model/undiscriminated-unions/src/Union/ConvertToken.php @@ -27,15 +27,16 @@ class ConvertToken extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->method = $values['method'];$this->tokenId = $values['tokenId']; + ) { + $this->method = $values['method']; + $this->tokenId = $values['tokenId']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/undiscriminated-unions/src/Union/KeyType.php b/seed/php-model/undiscriminated-unions/src/Union/KeyType.php index 03bbd7b8b154..b589840f6b51 100644 --- a/seed/php-model/undiscriminated-unions/src/Union/KeyType.php +++ b/seed/php-model/undiscriminated-unions/src/Union/KeyType.php @@ -2,8 +2,8 @@ namespace Seed\Union; -enum KeyType - : string { +enum KeyType: string +{ case Name = "name"; case Value = "value"; } diff --git a/seed/php-model/undiscriminated-unions/src/Union/NamedMetadata.php b/seed/php-model/undiscriminated-unions/src/Union/NamedMetadata.php index c2209fd40e64..c613d5c117a0 100644 --- a/seed/php-model/undiscriminated-unions/src/Union/NamedMetadata.php +++ b/seed/php-model/undiscriminated-unions/src/Union/NamedMetadata.php @@ -28,15 +28,16 @@ class NamedMetadata extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->value = $values['value']; + ) { + $this->name = $values['name']; + $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/undiscriminated-unions/src/Union/Request.php b/seed/php-model/undiscriminated-unions/src/Union/Request.php index 134bee6058fc..147b540ad124 100644 --- a/seed/php-model/undiscriminated-unions/src/Union/Request.php +++ b/seed/php-model/undiscriminated-unions/src/Union/Request.php @@ -15,7 +15,7 @@ class Request extends JsonSerializableType * |null * ) $union */ - #[JsonProperty('union'), Union(new Union(['string' => 'mixed'], 'null'),NamedMetadata::class,'null')] + #[JsonProperty('union'), Union(new Union(['string' => 'mixed'], 'null'), NamedMetadata::class, 'null')] public array|NamedMetadata|null $union; /** @@ -29,15 +29,15 @@ class Request extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->union = $values['union'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/undiscriminated-unions/src/Union/TokenizeCard.php b/seed/php-model/undiscriminated-unions/src/Union/TokenizeCard.php index 6801a3e10928..28e6a626d2e6 100644 --- a/seed/php-model/undiscriminated-unions/src/Union/TokenizeCard.php +++ b/seed/php-model/undiscriminated-unions/src/Union/TokenizeCard.php @@ -27,15 +27,16 @@ class TokenizeCard extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->method = $values['method'];$this->cardNumber = $values['cardNumber']; + ) { + $this->method = $values['method']; + $this->cardNumber = $values['cardNumber']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/undiscriminated-unions/src/Union/TypeWithOptionalUnion.php b/seed/php-model/undiscriminated-unions/src/Union/TypeWithOptionalUnion.php index f9a0312cbca6..617f3a08cb5a 100644 --- a/seed/php-model/undiscriminated-unions/src/Union/TypeWithOptionalUnion.php +++ b/seed/php-model/undiscriminated-unions/src/Union/TypeWithOptionalUnion.php @@ -17,7 +17,7 @@ class TypeWithOptionalUnion extends JsonSerializableType * |array> * )|null $myUnion */ - #[JsonProperty('myUnion'), Union('string',['string'],'integer',['integer'],[['integer']],'null')] + #[JsonProperty('myUnion'), Union('string', ['string'], 'integer', ['integer'], [['integer']], 'null')] public string|array|int|null $myUnion; /** @@ -33,15 +33,15 @@ class TypeWithOptionalUnion extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->myUnion = $values['myUnion'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/undiscriminated-unions/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/undiscriminated-unions/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/undiscriminated-unions/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/undiscriminated-unions/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/undiscriminated-unions/tests/Core/Json/EnumTest.php b/seed/php-model/undiscriminated-unions/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/undiscriminated-unions/tests/Core/Json/EnumTest.php +++ b/seed/php-model/undiscriminated-unions/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/undiscriminated-unions/tests/Core/Json/TraitTest.php b/seed/php-model/undiscriminated-unions/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/undiscriminated-unions/tests/Core/Json/TraitTest.php +++ b/seed/php-model/undiscriminated-unions/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/undiscriminated-unions/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/undiscriminated-unions/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/undiscriminated-unions/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/undiscriminated-unions/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/unions-with-local-date/src/Bigunion/ActiveDiamond.php b/seed/php-model/unions-with-local-date/src/Bigunion/ActiveDiamond.php index 849ff840ba36..ec928b7c2df1 100644 --- a/seed/php-model/unions-with-local-date/src/Bigunion/ActiveDiamond.php +++ b/seed/php-model/unions-with-local-date/src/Bigunion/ActiveDiamond.php @@ -20,15 +20,15 @@ class ActiveDiamond extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions-with-local-date/src/Bigunion/AttractiveScript.php b/seed/php-model/unions-with-local-date/src/Bigunion/AttractiveScript.php index d478c56c6270..124ebbdd36b9 100644 --- a/seed/php-model/unions-with-local-date/src/Bigunion/AttractiveScript.php +++ b/seed/php-model/unions-with-local-date/src/Bigunion/AttractiveScript.php @@ -20,15 +20,15 @@ class AttractiveScript extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions-with-local-date/src/Bigunion/BigUnion.php b/seed/php-model/unions-with-local-date/src/Bigunion/BigUnion.php index 1d99cc4eb464..6feb1900942c 100644 --- a/seed/php-model/unions-with-local-date/src/Bigunion/BigUnion.php +++ b/seed/php-model/unions-with-local-date/src/Bigunion/BigUnion.php @@ -174,9 +174,12 @@ class BigUnion extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->id = $values['id'];$this->createdAt = $values['createdAt'];$this->archivedAt = $values['archivedAt'] ?? null;$this->type = $values['type'];$this->value = $values['value']; + ) { + $this->id = $values['id']; + $this->createdAt = $values['createdAt']; + $this->archivedAt = $values['archivedAt'] ?? null; + $this->type = $values['type']; + $this->value = $values['value']; } /** @@ -186,7 +189,8 @@ private function __construct( * @param NormalSweet $normalSweet * @return BigUnion */ - public static function normalSweet(string $id, DateTime $createdAt, NormalSweet $normalSweet, ?DateTime $archivedAt = null): BigUnion { + public static function normalSweet(string $id, DateTime $createdAt, NormalSweet $normalSweet, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -203,7 +207,8 @@ public static function normalSweet(string $id, DateTime $createdAt, NormalSweet * @param ThankfulFactor $thankfulFactor * @return BigUnion */ - public static function thankfulFactor(string $id, DateTime $createdAt, ThankfulFactor $thankfulFactor, ?DateTime $archivedAt = null): BigUnion { + public static function thankfulFactor(string $id, DateTime $createdAt, ThankfulFactor $thankfulFactor, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -220,7 +225,8 @@ public static function thankfulFactor(string $id, DateTime $createdAt, ThankfulF * @param JumboEnd $jumboEnd * @return BigUnion */ - public static function jumboEnd(string $id, DateTime $createdAt, JumboEnd $jumboEnd, ?DateTime $archivedAt = null): BigUnion { + public static function jumboEnd(string $id, DateTime $createdAt, JumboEnd $jumboEnd, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -237,7 +243,8 @@ public static function jumboEnd(string $id, DateTime $createdAt, JumboEnd $jumbo * @param HastyPain $hastyPain * @return BigUnion */ - public static function hastyPain(string $id, DateTime $createdAt, HastyPain $hastyPain, ?DateTime $archivedAt = null): BigUnion { + public static function hastyPain(string $id, DateTime $createdAt, HastyPain $hastyPain, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -254,7 +261,8 @@ public static function hastyPain(string $id, DateTime $createdAt, HastyPain $has * @param MistySnow $mistySnow * @return BigUnion */ - public static function mistySnow(string $id, DateTime $createdAt, MistySnow $mistySnow, ?DateTime $archivedAt = null): BigUnion { + public static function mistySnow(string $id, DateTime $createdAt, MistySnow $mistySnow, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -271,7 +279,8 @@ public static function mistySnow(string $id, DateTime $createdAt, MistySnow $mis * @param DistinctFailure $distinctFailure * @return BigUnion */ - public static function distinctFailure(string $id, DateTime $createdAt, DistinctFailure $distinctFailure, ?DateTime $archivedAt = null): BigUnion { + public static function distinctFailure(string $id, DateTime $createdAt, DistinctFailure $distinctFailure, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -288,7 +297,8 @@ public static function distinctFailure(string $id, DateTime $createdAt, Distinct * @param PracticalPrinciple $practicalPrinciple * @return BigUnion */ - public static function practicalPrinciple(string $id, DateTime $createdAt, PracticalPrinciple $practicalPrinciple, ?DateTime $archivedAt = null): BigUnion { + public static function practicalPrinciple(string $id, DateTime $createdAt, PracticalPrinciple $practicalPrinciple, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -305,7 +315,8 @@ public static function practicalPrinciple(string $id, DateTime $createdAt, Pract * @param LimpingStep $limpingStep * @return BigUnion */ - public static function limpingStep(string $id, DateTime $createdAt, LimpingStep $limpingStep, ?DateTime $archivedAt = null): BigUnion { + public static function limpingStep(string $id, DateTime $createdAt, LimpingStep $limpingStep, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -322,7 +333,8 @@ public static function limpingStep(string $id, DateTime $createdAt, LimpingStep * @param VibrantExcitement $vibrantExcitement * @return BigUnion */ - public static function vibrantExcitement(string $id, DateTime $createdAt, VibrantExcitement $vibrantExcitement, ?DateTime $archivedAt = null): BigUnion { + public static function vibrantExcitement(string $id, DateTime $createdAt, VibrantExcitement $vibrantExcitement, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -339,7 +351,8 @@ public static function vibrantExcitement(string $id, DateTime $createdAt, Vibran * @param ActiveDiamond $activeDiamond * @return BigUnion */ - public static function activeDiamond(string $id, DateTime $createdAt, ActiveDiamond $activeDiamond, ?DateTime $archivedAt = null): BigUnion { + public static function activeDiamond(string $id, DateTime $createdAt, ActiveDiamond $activeDiamond, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -356,7 +369,8 @@ public static function activeDiamond(string $id, DateTime $createdAt, ActiveDiam * @param PopularLimit $popularLimit * @return BigUnion */ - public static function popularLimit(string $id, DateTime $createdAt, PopularLimit $popularLimit, ?DateTime $archivedAt = null): BigUnion { + public static function popularLimit(string $id, DateTime $createdAt, PopularLimit $popularLimit, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -373,7 +387,8 @@ public static function popularLimit(string $id, DateTime $createdAt, PopularLimi * @param FalseMirror $falseMirror * @return BigUnion */ - public static function falseMirror(string $id, DateTime $createdAt, FalseMirror $falseMirror, ?DateTime $archivedAt = null): BigUnion { + public static function falseMirror(string $id, DateTime $createdAt, FalseMirror $falseMirror, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -390,7 +405,8 @@ public static function falseMirror(string $id, DateTime $createdAt, FalseMirror * @param PrimaryBlock $primaryBlock * @return BigUnion */ - public static function primaryBlock(string $id, DateTime $createdAt, PrimaryBlock $primaryBlock, ?DateTime $archivedAt = null): BigUnion { + public static function primaryBlock(string $id, DateTime $createdAt, PrimaryBlock $primaryBlock, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -407,7 +423,8 @@ public static function primaryBlock(string $id, DateTime $createdAt, PrimaryBloc * @param RotatingRatio $rotatingRatio * @return BigUnion */ - public static function rotatingRatio(string $id, DateTime $createdAt, RotatingRatio $rotatingRatio, ?DateTime $archivedAt = null): BigUnion { + public static function rotatingRatio(string $id, DateTime $createdAt, RotatingRatio $rotatingRatio, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -424,7 +441,8 @@ public static function rotatingRatio(string $id, DateTime $createdAt, RotatingRa * @param ColorfulCover $colorfulCover * @return BigUnion */ - public static function colorfulCover(string $id, DateTime $createdAt, ColorfulCover $colorfulCover, ?DateTime $archivedAt = null): BigUnion { + public static function colorfulCover(string $id, DateTime $createdAt, ColorfulCover $colorfulCover, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -441,7 +459,8 @@ public static function colorfulCover(string $id, DateTime $createdAt, ColorfulCo * @param DisloyalValue $disloyalValue * @return BigUnion */ - public static function disloyalValue(string $id, DateTime $createdAt, DisloyalValue $disloyalValue, ?DateTime $archivedAt = null): BigUnion { + public static function disloyalValue(string $id, DateTime $createdAt, DisloyalValue $disloyalValue, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -458,7 +477,8 @@ public static function disloyalValue(string $id, DateTime $createdAt, DisloyalVa * @param GruesomeCoach $gruesomeCoach * @return BigUnion */ - public static function gruesomeCoach(string $id, DateTime $createdAt, GruesomeCoach $gruesomeCoach, ?DateTime $archivedAt = null): BigUnion { + public static function gruesomeCoach(string $id, DateTime $createdAt, GruesomeCoach $gruesomeCoach, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -475,7 +495,8 @@ public static function gruesomeCoach(string $id, DateTime $createdAt, GruesomeCo * @param TotalWork $totalWork * @return BigUnion */ - public static function totalWork(string $id, DateTime $createdAt, TotalWork $totalWork, ?DateTime $archivedAt = null): BigUnion { + public static function totalWork(string $id, DateTime $createdAt, TotalWork $totalWork, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -492,7 +513,8 @@ public static function totalWork(string $id, DateTime $createdAt, TotalWork $tot * @param HarmoniousPlay $harmoniousPlay * @return BigUnion */ - public static function harmoniousPlay(string $id, DateTime $createdAt, HarmoniousPlay $harmoniousPlay, ?DateTime $archivedAt = null): BigUnion { + public static function harmoniousPlay(string $id, DateTime $createdAt, HarmoniousPlay $harmoniousPlay, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -509,7 +531,8 @@ public static function harmoniousPlay(string $id, DateTime $createdAt, Harmoniou * @param UniqueStress $uniqueStress * @return BigUnion */ - public static function uniqueStress(string $id, DateTime $createdAt, UniqueStress $uniqueStress, ?DateTime $archivedAt = null): BigUnion { + public static function uniqueStress(string $id, DateTime $createdAt, UniqueStress $uniqueStress, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -526,7 +549,8 @@ public static function uniqueStress(string $id, DateTime $createdAt, UniqueStres * @param UnwillingSmoke $unwillingSmoke * @return BigUnion */ - public static function unwillingSmoke(string $id, DateTime $createdAt, UnwillingSmoke $unwillingSmoke, ?DateTime $archivedAt = null): BigUnion { + public static function unwillingSmoke(string $id, DateTime $createdAt, UnwillingSmoke $unwillingSmoke, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -543,7 +567,8 @@ public static function unwillingSmoke(string $id, DateTime $createdAt, Unwilling * @param FrozenSleep $frozenSleep * @return BigUnion */ - public static function frozenSleep(string $id, DateTime $createdAt, FrozenSleep $frozenSleep, ?DateTime $archivedAt = null): BigUnion { + public static function frozenSleep(string $id, DateTime $createdAt, FrozenSleep $frozenSleep, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -560,7 +585,8 @@ public static function frozenSleep(string $id, DateTime $createdAt, FrozenSleep * @param DiligentDeal $diligentDeal * @return BigUnion */ - public static function diligentDeal(string $id, DateTime $createdAt, DiligentDeal $diligentDeal, ?DateTime $archivedAt = null): BigUnion { + public static function diligentDeal(string $id, DateTime $createdAt, DiligentDeal $diligentDeal, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -577,7 +603,8 @@ public static function diligentDeal(string $id, DateTime $createdAt, DiligentDea * @param AttractiveScript $attractiveScript * @return BigUnion */ - public static function attractiveScript(string $id, DateTime $createdAt, AttractiveScript $attractiveScript, ?DateTime $archivedAt = null): BigUnion { + public static function attractiveScript(string $id, DateTime $createdAt, AttractiveScript $attractiveScript, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -594,7 +621,8 @@ public static function attractiveScript(string $id, DateTime $createdAt, Attract * @param HoarseMouse $hoarseMouse * @return BigUnion */ - public static function hoarseMouse(string $id, DateTime $createdAt, HoarseMouse $hoarseMouse, ?DateTime $archivedAt = null): BigUnion { + public static function hoarseMouse(string $id, DateTime $createdAt, HoarseMouse $hoarseMouse, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -611,7 +639,8 @@ public static function hoarseMouse(string $id, DateTime $createdAt, HoarseMouse * @param CircularCard $circularCard * @return BigUnion */ - public static function circularCard(string $id, DateTime $createdAt, CircularCard $circularCard, ?DateTime $archivedAt = null): BigUnion { + public static function circularCard(string $id, DateTime $createdAt, CircularCard $circularCard, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -628,7 +657,8 @@ public static function circularCard(string $id, DateTime $createdAt, CircularCar * @param PotableBad $potableBad * @return BigUnion */ - public static function potableBad(string $id, DateTime $createdAt, PotableBad $potableBad, ?DateTime $archivedAt = null): BigUnion { + public static function potableBad(string $id, DateTime $createdAt, PotableBad $potableBad, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -645,7 +675,8 @@ public static function potableBad(string $id, DateTime $createdAt, PotableBad $p * @param TriangularRepair $triangularRepair * @return BigUnion */ - public static function triangularRepair(string $id, DateTime $createdAt, TriangularRepair $triangularRepair, ?DateTime $archivedAt = null): BigUnion { + public static function triangularRepair(string $id, DateTime $createdAt, TriangularRepair $triangularRepair, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -662,7 +693,8 @@ public static function triangularRepair(string $id, DateTime $createdAt, Triangu * @param GaseousRoad $gaseousRoad * @return BigUnion */ - public static function gaseousRoad(string $id, DateTime $createdAt, GaseousRoad $gaseousRoad, ?DateTime $archivedAt = null): BigUnion { + public static function gaseousRoad(string $id, DateTime $createdAt, GaseousRoad $gaseousRoad, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -675,601 +707,661 @@ public static function gaseousRoad(string $id, DateTime $createdAt, GaseousRoad /** * @return bool */ - public function isNormalSweet(): bool { - return $this->value instanceof NormalSweet&& $this->type === 'normalSweet'; + public function isNormalSweet(): bool + { + return $this->value instanceof NormalSweet && $this->type === 'normalSweet'; } /** * @return NormalSweet */ - public function asNormalSweet(): NormalSweet { - if (!($this->value instanceof NormalSweet&& $this->type === 'normalSweet')){ + public function asNormalSweet(): NormalSweet + { + if (!($this->value instanceof NormalSweet && $this->type === 'normalSweet')) { throw new Exception( "Expected normalSweet; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isThankfulFactor(): bool { - return $this->value instanceof ThankfulFactor&& $this->type === 'thankfulFactor'; + public function isThankfulFactor(): bool + { + return $this->value instanceof ThankfulFactor && $this->type === 'thankfulFactor'; } /** * @return ThankfulFactor */ - public function asThankfulFactor(): ThankfulFactor { - if (!($this->value instanceof ThankfulFactor&& $this->type === 'thankfulFactor')){ + public function asThankfulFactor(): ThankfulFactor + { + if (!($this->value instanceof ThankfulFactor && $this->type === 'thankfulFactor')) { throw new Exception( "Expected thankfulFactor; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isJumboEnd(): bool { - return $this->value instanceof JumboEnd&& $this->type === 'jumboEnd'; + public function isJumboEnd(): bool + { + return $this->value instanceof JumboEnd && $this->type === 'jumboEnd'; } /** * @return JumboEnd */ - public function asJumboEnd(): JumboEnd { - if (!($this->value instanceof JumboEnd&& $this->type === 'jumboEnd')){ + public function asJumboEnd(): JumboEnd + { + if (!($this->value instanceof JumboEnd && $this->type === 'jumboEnd')) { throw new Exception( "Expected jumboEnd; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isHastyPain(): bool { - return $this->value instanceof HastyPain&& $this->type === 'hastyPain'; + public function isHastyPain(): bool + { + return $this->value instanceof HastyPain && $this->type === 'hastyPain'; } /** * @return HastyPain */ - public function asHastyPain(): HastyPain { - if (!($this->value instanceof HastyPain&& $this->type === 'hastyPain')){ + public function asHastyPain(): HastyPain + { + if (!($this->value instanceof HastyPain && $this->type === 'hastyPain')) { throw new Exception( "Expected hastyPain; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isMistySnow(): bool { - return $this->value instanceof MistySnow&& $this->type === 'mistySnow'; + public function isMistySnow(): bool + { + return $this->value instanceof MistySnow && $this->type === 'mistySnow'; } /** * @return MistySnow */ - public function asMistySnow(): MistySnow { - if (!($this->value instanceof MistySnow&& $this->type === 'mistySnow')){ + public function asMistySnow(): MistySnow + { + if (!($this->value instanceof MistySnow && $this->type === 'mistySnow')) { throw new Exception( "Expected mistySnow; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isDistinctFailure(): bool { - return $this->value instanceof DistinctFailure&& $this->type === 'distinctFailure'; + public function isDistinctFailure(): bool + { + return $this->value instanceof DistinctFailure && $this->type === 'distinctFailure'; } /** * @return DistinctFailure */ - public function asDistinctFailure(): DistinctFailure { - if (!($this->value instanceof DistinctFailure&& $this->type === 'distinctFailure')){ + public function asDistinctFailure(): DistinctFailure + { + if (!($this->value instanceof DistinctFailure && $this->type === 'distinctFailure')) { throw new Exception( "Expected distinctFailure; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isPracticalPrinciple(): bool { - return $this->value instanceof PracticalPrinciple&& $this->type === 'practicalPrinciple'; + public function isPracticalPrinciple(): bool + { + return $this->value instanceof PracticalPrinciple && $this->type === 'practicalPrinciple'; } /** * @return PracticalPrinciple */ - public function asPracticalPrinciple(): PracticalPrinciple { - if (!($this->value instanceof PracticalPrinciple&& $this->type === 'practicalPrinciple')){ + public function asPracticalPrinciple(): PracticalPrinciple + { + if (!($this->value instanceof PracticalPrinciple && $this->type === 'practicalPrinciple')) { throw new Exception( "Expected practicalPrinciple; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isLimpingStep(): bool { - return $this->value instanceof LimpingStep&& $this->type === 'limpingStep'; + public function isLimpingStep(): bool + { + return $this->value instanceof LimpingStep && $this->type === 'limpingStep'; } /** * @return LimpingStep */ - public function asLimpingStep(): LimpingStep { - if (!($this->value instanceof LimpingStep&& $this->type === 'limpingStep')){ + public function asLimpingStep(): LimpingStep + { + if (!($this->value instanceof LimpingStep && $this->type === 'limpingStep')) { throw new Exception( "Expected limpingStep; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isVibrantExcitement(): bool { - return $this->value instanceof VibrantExcitement&& $this->type === 'vibrantExcitement'; + public function isVibrantExcitement(): bool + { + return $this->value instanceof VibrantExcitement && $this->type === 'vibrantExcitement'; } /** * @return VibrantExcitement */ - public function asVibrantExcitement(): VibrantExcitement { - if (!($this->value instanceof VibrantExcitement&& $this->type === 'vibrantExcitement')){ + public function asVibrantExcitement(): VibrantExcitement + { + if (!($this->value instanceof VibrantExcitement && $this->type === 'vibrantExcitement')) { throw new Exception( "Expected vibrantExcitement; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isActiveDiamond(): bool { - return $this->value instanceof ActiveDiamond&& $this->type === 'activeDiamond'; + public function isActiveDiamond(): bool + { + return $this->value instanceof ActiveDiamond && $this->type === 'activeDiamond'; } /** * @return ActiveDiamond */ - public function asActiveDiamond(): ActiveDiamond { - if (!($this->value instanceof ActiveDiamond&& $this->type === 'activeDiamond')){ + public function asActiveDiamond(): ActiveDiamond + { + if (!($this->value instanceof ActiveDiamond && $this->type === 'activeDiamond')) { throw new Exception( "Expected activeDiamond; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isPopularLimit(): bool { - return $this->value instanceof PopularLimit&& $this->type === 'popularLimit'; + public function isPopularLimit(): bool + { + return $this->value instanceof PopularLimit && $this->type === 'popularLimit'; } /** * @return PopularLimit */ - public function asPopularLimit(): PopularLimit { - if (!($this->value instanceof PopularLimit&& $this->type === 'popularLimit')){ + public function asPopularLimit(): PopularLimit + { + if (!($this->value instanceof PopularLimit && $this->type === 'popularLimit')) { throw new Exception( "Expected popularLimit; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isFalseMirror(): bool { - return $this->value instanceof FalseMirror&& $this->type === 'falseMirror'; + public function isFalseMirror(): bool + { + return $this->value instanceof FalseMirror && $this->type === 'falseMirror'; } /** * @return FalseMirror */ - public function asFalseMirror(): FalseMirror { - if (!($this->value instanceof FalseMirror&& $this->type === 'falseMirror')){ + public function asFalseMirror(): FalseMirror + { + if (!($this->value instanceof FalseMirror && $this->type === 'falseMirror')) { throw new Exception( "Expected falseMirror; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isPrimaryBlock(): bool { - return $this->value instanceof PrimaryBlock&& $this->type === 'primaryBlock'; + public function isPrimaryBlock(): bool + { + return $this->value instanceof PrimaryBlock && $this->type === 'primaryBlock'; } /** * @return PrimaryBlock */ - public function asPrimaryBlock(): PrimaryBlock { - if (!($this->value instanceof PrimaryBlock&& $this->type === 'primaryBlock')){ + public function asPrimaryBlock(): PrimaryBlock + { + if (!($this->value instanceof PrimaryBlock && $this->type === 'primaryBlock')) { throw new Exception( "Expected primaryBlock; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isRotatingRatio(): bool { - return $this->value instanceof RotatingRatio&& $this->type === 'rotatingRatio'; + public function isRotatingRatio(): bool + { + return $this->value instanceof RotatingRatio && $this->type === 'rotatingRatio'; } /** * @return RotatingRatio */ - public function asRotatingRatio(): RotatingRatio { - if (!($this->value instanceof RotatingRatio&& $this->type === 'rotatingRatio')){ + public function asRotatingRatio(): RotatingRatio + { + if (!($this->value instanceof RotatingRatio && $this->type === 'rotatingRatio')) { throw new Exception( "Expected rotatingRatio; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isColorfulCover(): bool { - return $this->value instanceof ColorfulCover&& $this->type === 'colorfulCover'; + public function isColorfulCover(): bool + { + return $this->value instanceof ColorfulCover && $this->type === 'colorfulCover'; } /** * @return ColorfulCover */ - public function asColorfulCover(): ColorfulCover { - if (!($this->value instanceof ColorfulCover&& $this->type === 'colorfulCover')){ + public function asColorfulCover(): ColorfulCover + { + if (!($this->value instanceof ColorfulCover && $this->type === 'colorfulCover')) { throw new Exception( "Expected colorfulCover; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isDisloyalValue(): bool { - return $this->value instanceof DisloyalValue&& $this->type === 'disloyalValue'; + public function isDisloyalValue(): bool + { + return $this->value instanceof DisloyalValue && $this->type === 'disloyalValue'; } /** * @return DisloyalValue */ - public function asDisloyalValue(): DisloyalValue { - if (!($this->value instanceof DisloyalValue&& $this->type === 'disloyalValue')){ + public function asDisloyalValue(): DisloyalValue + { + if (!($this->value instanceof DisloyalValue && $this->type === 'disloyalValue')) { throw new Exception( "Expected disloyalValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isGruesomeCoach(): bool { - return $this->value instanceof GruesomeCoach&& $this->type === 'gruesomeCoach'; + public function isGruesomeCoach(): bool + { + return $this->value instanceof GruesomeCoach && $this->type === 'gruesomeCoach'; } /** * @return GruesomeCoach */ - public function asGruesomeCoach(): GruesomeCoach { - if (!($this->value instanceof GruesomeCoach&& $this->type === 'gruesomeCoach')){ + public function asGruesomeCoach(): GruesomeCoach + { + if (!($this->value instanceof GruesomeCoach && $this->type === 'gruesomeCoach')) { throw new Exception( "Expected gruesomeCoach; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isTotalWork(): bool { - return $this->value instanceof TotalWork&& $this->type === 'totalWork'; + public function isTotalWork(): bool + { + return $this->value instanceof TotalWork && $this->type === 'totalWork'; } /** * @return TotalWork */ - public function asTotalWork(): TotalWork { - if (!($this->value instanceof TotalWork&& $this->type === 'totalWork')){ + public function asTotalWork(): TotalWork + { + if (!($this->value instanceof TotalWork && $this->type === 'totalWork')) { throw new Exception( "Expected totalWork; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isHarmoniousPlay(): bool { - return $this->value instanceof HarmoniousPlay&& $this->type === 'harmoniousPlay'; + public function isHarmoniousPlay(): bool + { + return $this->value instanceof HarmoniousPlay && $this->type === 'harmoniousPlay'; } /** * @return HarmoniousPlay */ - public function asHarmoniousPlay(): HarmoniousPlay { - if (!($this->value instanceof HarmoniousPlay&& $this->type === 'harmoniousPlay')){ + public function asHarmoniousPlay(): HarmoniousPlay + { + if (!($this->value instanceof HarmoniousPlay && $this->type === 'harmoniousPlay')) { throw new Exception( "Expected harmoniousPlay; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isUniqueStress(): bool { - return $this->value instanceof UniqueStress&& $this->type === 'uniqueStress'; + public function isUniqueStress(): bool + { + return $this->value instanceof UniqueStress && $this->type === 'uniqueStress'; } /** * @return UniqueStress */ - public function asUniqueStress(): UniqueStress { - if (!($this->value instanceof UniqueStress&& $this->type === 'uniqueStress')){ + public function asUniqueStress(): UniqueStress + { + if (!($this->value instanceof UniqueStress && $this->type === 'uniqueStress')) { throw new Exception( "Expected uniqueStress; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isUnwillingSmoke(): bool { - return $this->value instanceof UnwillingSmoke&& $this->type === 'unwillingSmoke'; + public function isUnwillingSmoke(): bool + { + return $this->value instanceof UnwillingSmoke && $this->type === 'unwillingSmoke'; } /** * @return UnwillingSmoke */ - public function asUnwillingSmoke(): UnwillingSmoke { - if (!($this->value instanceof UnwillingSmoke&& $this->type === 'unwillingSmoke')){ + public function asUnwillingSmoke(): UnwillingSmoke + { + if (!($this->value instanceof UnwillingSmoke && $this->type === 'unwillingSmoke')) { throw new Exception( "Expected unwillingSmoke; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isFrozenSleep(): bool { - return $this->value instanceof FrozenSleep&& $this->type === 'frozenSleep'; + public function isFrozenSleep(): bool + { + return $this->value instanceof FrozenSleep && $this->type === 'frozenSleep'; } /** * @return FrozenSleep */ - public function asFrozenSleep(): FrozenSleep { - if (!($this->value instanceof FrozenSleep&& $this->type === 'frozenSleep')){ + public function asFrozenSleep(): FrozenSleep + { + if (!($this->value instanceof FrozenSleep && $this->type === 'frozenSleep')) { throw new Exception( "Expected frozenSleep; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isDiligentDeal(): bool { - return $this->value instanceof DiligentDeal&& $this->type === 'diligentDeal'; + public function isDiligentDeal(): bool + { + return $this->value instanceof DiligentDeal && $this->type === 'diligentDeal'; } /** * @return DiligentDeal */ - public function asDiligentDeal(): DiligentDeal { - if (!($this->value instanceof DiligentDeal&& $this->type === 'diligentDeal')){ + public function asDiligentDeal(): DiligentDeal + { + if (!($this->value instanceof DiligentDeal && $this->type === 'diligentDeal')) { throw new Exception( "Expected diligentDeal; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isAttractiveScript(): bool { - return $this->value instanceof AttractiveScript&& $this->type === 'attractiveScript'; + public function isAttractiveScript(): bool + { + return $this->value instanceof AttractiveScript && $this->type === 'attractiveScript'; } /** * @return AttractiveScript */ - public function asAttractiveScript(): AttractiveScript { - if (!($this->value instanceof AttractiveScript&& $this->type === 'attractiveScript')){ + public function asAttractiveScript(): AttractiveScript + { + if (!($this->value instanceof AttractiveScript && $this->type === 'attractiveScript')) { throw new Exception( "Expected attractiveScript; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isHoarseMouse(): bool { - return $this->value instanceof HoarseMouse&& $this->type === 'hoarseMouse'; + public function isHoarseMouse(): bool + { + return $this->value instanceof HoarseMouse && $this->type === 'hoarseMouse'; } /** * @return HoarseMouse */ - public function asHoarseMouse(): HoarseMouse { - if (!($this->value instanceof HoarseMouse&& $this->type === 'hoarseMouse')){ + public function asHoarseMouse(): HoarseMouse + { + if (!($this->value instanceof HoarseMouse && $this->type === 'hoarseMouse')) { throw new Exception( "Expected hoarseMouse; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isCircularCard(): bool { - return $this->value instanceof CircularCard&& $this->type === 'circularCard'; + public function isCircularCard(): bool + { + return $this->value instanceof CircularCard && $this->type === 'circularCard'; } /** * @return CircularCard */ - public function asCircularCard(): CircularCard { - if (!($this->value instanceof CircularCard&& $this->type === 'circularCard')){ + public function asCircularCard(): CircularCard + { + if (!($this->value instanceof CircularCard && $this->type === 'circularCard')) { throw new Exception( "Expected circularCard; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isPotableBad(): bool { - return $this->value instanceof PotableBad&& $this->type === 'potableBad'; + public function isPotableBad(): bool + { + return $this->value instanceof PotableBad && $this->type === 'potableBad'; } /** * @return PotableBad */ - public function asPotableBad(): PotableBad { - if (!($this->value instanceof PotableBad&& $this->type === 'potableBad')){ + public function asPotableBad(): PotableBad + { + if (!($this->value instanceof PotableBad && $this->type === 'potableBad')) { throw new Exception( "Expected potableBad; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isTriangularRepair(): bool { - return $this->value instanceof TriangularRepair&& $this->type === 'triangularRepair'; + public function isTriangularRepair(): bool + { + return $this->value instanceof TriangularRepair && $this->type === 'triangularRepair'; } /** * @return TriangularRepair */ - public function asTriangularRepair(): TriangularRepair { - if (!($this->value instanceof TriangularRepair&& $this->type === 'triangularRepair')){ + public function asTriangularRepair(): TriangularRepair + { + if (!($this->value instanceof TriangularRepair && $this->type === 'triangularRepair')) { throw new Exception( "Expected triangularRepair; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isGaseousRoad(): bool { - return $this->value instanceof GaseousRoad&& $this->type === 'gaseousRoad'; + public function isGaseousRoad(): bool + { + return $this->value instanceof GaseousRoad && $this->type === 'gaseousRoad'; } /** * @return GaseousRoad */ - public function asGaseousRoad(): GaseousRoad { - if (!($this->value instanceof GaseousRoad&& $this->type === 'gaseousRoad')){ + public function asGaseousRoad(): GaseousRoad + { + if (!($this->value instanceof GaseousRoad && $this->type === 'gaseousRoad')) { throw new Exception( "Expected gaseousRoad; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'normalSweet': $value = $this->asNormalSweet()->jsonSerialize(); $result = array_merge($value, $result); @@ -1388,26 +1480,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -1416,58 +1509,59 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('id', $data)){ + if (!array_key_exists('id', $data)) { throw new Exception( "JSON data is missing property 'id'", ); } - if (!(is_string($data['id']))){ + if (!(is_string($data['id']))) { throw new Exception( "Expected property 'id' in JSON data to be string, instead received " . get_debug_type($data['id']), ); } $args['id'] = $data['id']; - - if (!array_key_exists('created-at', $data)){ + + if (!array_key_exists('created-at', $data)) { throw new Exception( "JSON data is missing property 'created-at'", ); } - if (!($data['created-at'] instanceof DateTime)){ + if (!($data['created-at'] instanceof DateTime)) { throw new Exception( "Expected property 'createdAt' in JSON data to be dateTime, instead received " . get_debug_type($data['created-at']), ); } $args['createdAt'] = $data['created-at']; - - if (!array_key_exists('archived-at', $data)){ + + if (!array_key_exists('archived-at', $data)) { throw new Exception( "JSON data is missing property 'archived-at'", ); } - if (!((is_null($data['archived-at']) || $data['archived-at'] instanceof DateTime))){ + if (!((is_null($data['archived-at']) || $data['archived-at'] instanceof DateTime))) { throw new Exception( "Expected property 'archivedAt' in JSON data to be optional, instead received " . get_debug_type($data['archived-at']), ); } $args['archivedAt'] = $data['archived-at']; - - if (!array_key_exists('type', $data)){ + + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'normalSweet': $args['value'] = NormalSweet::jsonDeserialize($data); break; @@ -1560,7 +1654,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/unions-with-local-date/src/Bigunion/CircularCard.php b/seed/php-model/unions-with-local-date/src/Bigunion/CircularCard.php index aa2d8a5f9c32..05419e03ee73 100644 --- a/seed/php-model/unions-with-local-date/src/Bigunion/CircularCard.php +++ b/seed/php-model/unions-with-local-date/src/Bigunion/CircularCard.php @@ -20,15 +20,15 @@ class CircularCard extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions-with-local-date/src/Bigunion/ColorfulCover.php b/seed/php-model/unions-with-local-date/src/Bigunion/ColorfulCover.php index a70136a77e37..81cecaa1dcc1 100644 --- a/seed/php-model/unions-with-local-date/src/Bigunion/ColorfulCover.php +++ b/seed/php-model/unions-with-local-date/src/Bigunion/ColorfulCover.php @@ -20,15 +20,15 @@ class ColorfulCover extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions-with-local-date/src/Bigunion/DiligentDeal.php b/seed/php-model/unions-with-local-date/src/Bigunion/DiligentDeal.php index a5cc5c3ca749..00dc65e89aef 100644 --- a/seed/php-model/unions-with-local-date/src/Bigunion/DiligentDeal.php +++ b/seed/php-model/unions-with-local-date/src/Bigunion/DiligentDeal.php @@ -20,15 +20,15 @@ class DiligentDeal extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions-with-local-date/src/Bigunion/DisloyalValue.php b/seed/php-model/unions-with-local-date/src/Bigunion/DisloyalValue.php index a762ff9a03d3..41115687f356 100644 --- a/seed/php-model/unions-with-local-date/src/Bigunion/DisloyalValue.php +++ b/seed/php-model/unions-with-local-date/src/Bigunion/DisloyalValue.php @@ -20,15 +20,15 @@ class DisloyalValue extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions-with-local-date/src/Bigunion/DistinctFailure.php b/seed/php-model/unions-with-local-date/src/Bigunion/DistinctFailure.php index c9951791e1fd..73121cfbfc56 100644 --- a/seed/php-model/unions-with-local-date/src/Bigunion/DistinctFailure.php +++ b/seed/php-model/unions-with-local-date/src/Bigunion/DistinctFailure.php @@ -20,15 +20,15 @@ class DistinctFailure extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions-with-local-date/src/Bigunion/FalseMirror.php b/seed/php-model/unions-with-local-date/src/Bigunion/FalseMirror.php index 300b44753bc3..8d9adc2f7ae0 100644 --- a/seed/php-model/unions-with-local-date/src/Bigunion/FalseMirror.php +++ b/seed/php-model/unions-with-local-date/src/Bigunion/FalseMirror.php @@ -20,15 +20,15 @@ class FalseMirror extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions-with-local-date/src/Bigunion/FrozenSleep.php b/seed/php-model/unions-with-local-date/src/Bigunion/FrozenSleep.php index e7baf168f074..63358e39e74e 100644 --- a/seed/php-model/unions-with-local-date/src/Bigunion/FrozenSleep.php +++ b/seed/php-model/unions-with-local-date/src/Bigunion/FrozenSleep.php @@ -20,15 +20,15 @@ class FrozenSleep extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions-with-local-date/src/Bigunion/GaseousRoad.php b/seed/php-model/unions-with-local-date/src/Bigunion/GaseousRoad.php index e39942463a52..65494e20a7b4 100644 --- a/seed/php-model/unions-with-local-date/src/Bigunion/GaseousRoad.php +++ b/seed/php-model/unions-with-local-date/src/Bigunion/GaseousRoad.php @@ -20,15 +20,15 @@ class GaseousRoad extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions-with-local-date/src/Bigunion/GruesomeCoach.php b/seed/php-model/unions-with-local-date/src/Bigunion/GruesomeCoach.php index 1b9c77a76680..380ceef20774 100644 --- a/seed/php-model/unions-with-local-date/src/Bigunion/GruesomeCoach.php +++ b/seed/php-model/unions-with-local-date/src/Bigunion/GruesomeCoach.php @@ -20,15 +20,15 @@ class GruesomeCoach extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions-with-local-date/src/Bigunion/HarmoniousPlay.php b/seed/php-model/unions-with-local-date/src/Bigunion/HarmoniousPlay.php index 20645361043b..717249a8bf7d 100644 --- a/seed/php-model/unions-with-local-date/src/Bigunion/HarmoniousPlay.php +++ b/seed/php-model/unions-with-local-date/src/Bigunion/HarmoniousPlay.php @@ -20,15 +20,15 @@ class HarmoniousPlay extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions-with-local-date/src/Bigunion/HastyPain.php b/seed/php-model/unions-with-local-date/src/Bigunion/HastyPain.php index 655a809fc519..c36c50c2adc7 100644 --- a/seed/php-model/unions-with-local-date/src/Bigunion/HastyPain.php +++ b/seed/php-model/unions-with-local-date/src/Bigunion/HastyPain.php @@ -20,15 +20,15 @@ class HastyPain extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions-with-local-date/src/Bigunion/HoarseMouse.php b/seed/php-model/unions-with-local-date/src/Bigunion/HoarseMouse.php index 18a78ee072c1..6f1437628757 100644 --- a/seed/php-model/unions-with-local-date/src/Bigunion/HoarseMouse.php +++ b/seed/php-model/unions-with-local-date/src/Bigunion/HoarseMouse.php @@ -20,15 +20,15 @@ class HoarseMouse extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions-with-local-date/src/Bigunion/JumboEnd.php b/seed/php-model/unions-with-local-date/src/Bigunion/JumboEnd.php index c46acf1fd124..97d2c3ea6f6d 100644 --- a/seed/php-model/unions-with-local-date/src/Bigunion/JumboEnd.php +++ b/seed/php-model/unions-with-local-date/src/Bigunion/JumboEnd.php @@ -20,15 +20,15 @@ class JumboEnd extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions-with-local-date/src/Bigunion/LimpingStep.php b/seed/php-model/unions-with-local-date/src/Bigunion/LimpingStep.php index a18506df5631..2216809108d3 100644 --- a/seed/php-model/unions-with-local-date/src/Bigunion/LimpingStep.php +++ b/seed/php-model/unions-with-local-date/src/Bigunion/LimpingStep.php @@ -20,15 +20,15 @@ class LimpingStep extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions-with-local-date/src/Bigunion/MistySnow.php b/seed/php-model/unions-with-local-date/src/Bigunion/MistySnow.php index 819bc1c66ffa..c2054320774d 100644 --- a/seed/php-model/unions-with-local-date/src/Bigunion/MistySnow.php +++ b/seed/php-model/unions-with-local-date/src/Bigunion/MistySnow.php @@ -20,15 +20,15 @@ class MistySnow extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions-with-local-date/src/Bigunion/NormalSweet.php b/seed/php-model/unions-with-local-date/src/Bigunion/NormalSweet.php index 01103ea30aab..f75bc941042c 100644 --- a/seed/php-model/unions-with-local-date/src/Bigunion/NormalSweet.php +++ b/seed/php-model/unions-with-local-date/src/Bigunion/NormalSweet.php @@ -20,15 +20,15 @@ class NormalSweet extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions-with-local-date/src/Bigunion/PopularLimit.php b/seed/php-model/unions-with-local-date/src/Bigunion/PopularLimit.php index 407fcce985d2..291106ea2974 100644 --- a/seed/php-model/unions-with-local-date/src/Bigunion/PopularLimit.php +++ b/seed/php-model/unions-with-local-date/src/Bigunion/PopularLimit.php @@ -20,15 +20,15 @@ class PopularLimit extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions-with-local-date/src/Bigunion/PotableBad.php b/seed/php-model/unions-with-local-date/src/Bigunion/PotableBad.php index 3c9cfb79e28a..2f3e11c76129 100644 --- a/seed/php-model/unions-with-local-date/src/Bigunion/PotableBad.php +++ b/seed/php-model/unions-with-local-date/src/Bigunion/PotableBad.php @@ -20,15 +20,15 @@ class PotableBad extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions-with-local-date/src/Bigunion/PracticalPrinciple.php b/seed/php-model/unions-with-local-date/src/Bigunion/PracticalPrinciple.php index 68986330908a..7a77f2a2b39d 100644 --- a/seed/php-model/unions-with-local-date/src/Bigunion/PracticalPrinciple.php +++ b/seed/php-model/unions-with-local-date/src/Bigunion/PracticalPrinciple.php @@ -20,15 +20,15 @@ class PracticalPrinciple extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions-with-local-date/src/Bigunion/PrimaryBlock.php b/seed/php-model/unions-with-local-date/src/Bigunion/PrimaryBlock.php index dda5e4cb2c08..5fbbac1b9857 100644 --- a/seed/php-model/unions-with-local-date/src/Bigunion/PrimaryBlock.php +++ b/seed/php-model/unions-with-local-date/src/Bigunion/PrimaryBlock.php @@ -20,15 +20,15 @@ class PrimaryBlock extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions-with-local-date/src/Bigunion/RotatingRatio.php b/seed/php-model/unions-with-local-date/src/Bigunion/RotatingRatio.php index 4faa41ffac34..cdada5a83a7f 100644 --- a/seed/php-model/unions-with-local-date/src/Bigunion/RotatingRatio.php +++ b/seed/php-model/unions-with-local-date/src/Bigunion/RotatingRatio.php @@ -20,15 +20,15 @@ class RotatingRatio extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions-with-local-date/src/Bigunion/ThankfulFactor.php b/seed/php-model/unions-with-local-date/src/Bigunion/ThankfulFactor.php index 031b30ab1b29..3d02a7d46dfb 100644 --- a/seed/php-model/unions-with-local-date/src/Bigunion/ThankfulFactor.php +++ b/seed/php-model/unions-with-local-date/src/Bigunion/ThankfulFactor.php @@ -20,15 +20,15 @@ class ThankfulFactor extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions-with-local-date/src/Bigunion/TotalWork.php b/seed/php-model/unions-with-local-date/src/Bigunion/TotalWork.php index 8f45e3af61e2..3708e211d0dd 100644 --- a/seed/php-model/unions-with-local-date/src/Bigunion/TotalWork.php +++ b/seed/php-model/unions-with-local-date/src/Bigunion/TotalWork.php @@ -20,15 +20,15 @@ class TotalWork extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions-with-local-date/src/Bigunion/TriangularRepair.php b/seed/php-model/unions-with-local-date/src/Bigunion/TriangularRepair.php index 724993cbbfc7..3405c1fdfe9d 100644 --- a/seed/php-model/unions-with-local-date/src/Bigunion/TriangularRepair.php +++ b/seed/php-model/unions-with-local-date/src/Bigunion/TriangularRepair.php @@ -20,15 +20,15 @@ class TriangularRepair extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions-with-local-date/src/Bigunion/UniqueStress.php b/seed/php-model/unions-with-local-date/src/Bigunion/UniqueStress.php index ad4fd69806ed..4a320ba473ca 100644 --- a/seed/php-model/unions-with-local-date/src/Bigunion/UniqueStress.php +++ b/seed/php-model/unions-with-local-date/src/Bigunion/UniqueStress.php @@ -20,15 +20,15 @@ class UniqueStress extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions-with-local-date/src/Bigunion/UnwillingSmoke.php b/seed/php-model/unions-with-local-date/src/Bigunion/UnwillingSmoke.php index 7f7549b7a04e..bb3e6ba1dc3a 100644 --- a/seed/php-model/unions-with-local-date/src/Bigunion/UnwillingSmoke.php +++ b/seed/php-model/unions-with-local-date/src/Bigunion/UnwillingSmoke.php @@ -20,15 +20,15 @@ class UnwillingSmoke extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions-with-local-date/src/Bigunion/VibrantExcitement.php b/seed/php-model/unions-with-local-date/src/Bigunion/VibrantExcitement.php index 373dadf5b85c..177cf8717704 100644 --- a/seed/php-model/unions-with-local-date/src/Bigunion/VibrantExcitement.php +++ b/seed/php-model/unions-with-local-date/src/Bigunion/VibrantExcitement.php @@ -20,15 +20,15 @@ class VibrantExcitement extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions-with-local-date/src/Core/Json/JsonEncoder.php b/seed/php-model/unions-with-local-date/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/unions-with-local-date/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/unions-with-local-date/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/unions-with-local-date/src/Core/Json/JsonProperty.php b/seed/php-model/unions-with-local-date/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/unions-with-local-date/src/Core/Json/JsonProperty.php +++ b/seed/php-model/unions-with-local-date/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/unions-with-local-date/src/Core/Types/ArrayType.php b/seed/php-model/unions-with-local-date/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/unions-with-local-date/src/Core/Types/ArrayType.php +++ b/seed/php-model/unions-with-local-date/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/unions-with-local-date/src/Core/Types/Constant.php b/seed/php-model/unions-with-local-date/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/unions-with-local-date/src/Core/Types/Constant.php +++ b/seed/php-model/unions-with-local-date/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/unions-with-local-date/src/Core/Types/Union.php b/seed/php-model/unions-with-local-date/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/unions-with-local-date/src/Core/Types/Union.php +++ b/seed/php-model/unions-with-local-date/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/unions-with-local-date/src/Types/Bar.php b/seed/php-model/unions-with-local-date/src/Types/Bar.php index 9be1a872a923..735bd90eee29 100644 --- a/seed/php-model/unions-with-local-date/src/Types/Bar.php +++ b/seed/php-model/unions-with-local-date/src/Types/Bar.php @@ -20,15 +20,15 @@ class Bar extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions-with-local-date/src/Types/Foo.php b/seed/php-model/unions-with-local-date/src/Types/Foo.php index 24d2085e66c4..7933294edd39 100644 --- a/seed/php-model/unions-with-local-date/src/Types/Foo.php +++ b/seed/php-model/unions-with-local-date/src/Types/Foo.php @@ -20,15 +20,15 @@ class Foo extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions-with-local-date/src/Types/FooExtended.php b/seed/php-model/unions-with-local-date/src/Types/FooExtended.php index 74cfb9266d33..7ae0fffcf0e7 100644 --- a/seed/php-model/unions-with-local-date/src/Types/FooExtended.php +++ b/seed/php-model/unions-with-local-date/src/Types/FooExtended.php @@ -24,15 +24,16 @@ class FooExtended extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->age = $values['age']; + ) { + $this->name = $values['name']; + $this->age = $values['age']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions-with-local-date/src/Types/Traits/Foo.php b/seed/php-model/unions-with-local-date/src/Types/Traits/Foo.php index 901f6d1cc180..7b400d71eacd 100644 --- a/seed/php-model/unions-with-local-date/src/Types/Traits/Foo.php +++ b/seed/php-model/unions-with-local-date/src/Types/Traits/Foo.php @@ -7,7 +7,7 @@ /** * @property string $name */ -trait Foo +trait Foo { /** * @var string $name diff --git a/seed/php-model/unions-with-local-date/src/Types/Union.php b/seed/php-model/unions-with-local-date/src/Types/Union.php index c45f30b8de39..189c346bf405 100644 --- a/seed/php-model/unions-with-local-date/src/Types/Union.php +++ b/seed/php-model/unions-with-local-date/src/Types/Union.php @@ -45,16 +45,17 @@ class Union extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param Foo $foo * @return Union */ - public static function foo(Foo $foo): Union { + public static function foo(Foo $foo): Union + { return new Union([ 'type' => 'foo', 'value' => $foo, @@ -65,7 +66,8 @@ public static function foo(Foo $foo): Union { * @param Bar $bar * @return Union */ - public static function bar(Bar $bar): Union { + public static function bar(Bar $bar): Union + { return new Union([ 'type' => 'bar', 'value' => $bar, @@ -75,61 +77,67 @@ public static function bar(Bar $bar): Union { /** * @return bool */ - public function isFoo(): bool { - return $this->value instanceof Foo&& $this->type === 'foo'; + public function isFoo(): bool + { + return $this->value instanceof Foo && $this->type === 'foo'; } /** * @return Foo */ - public function asFoo(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo')){ + public function asFoo(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo')) { throw new Exception( "Expected foo; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isBar(): bool { - return $this->value instanceof Bar&& $this->type === 'bar'; + public function isBar(): bool + { + return $this->value instanceof Bar && $this->type === 'bar'; } /** * @return Bar */ - public function asBar(): Bar { - if (!($this->value instanceof Bar&& $this->type === 'bar')){ + public function asBar(): Bar + { + if (!($this->value instanceof Bar && $this->type === 'bar')) { throw new Exception( "Expected bar; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'foo': $value = $this->asFoo()->jsonSerialize(); $result['foo'] = $value; @@ -140,26 +148,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -168,30 +177,31 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'foo': - if (!array_key_exists('foo', $data)){ + if (!array_key_exists('foo', $data)) { throw new Exception( "JSON data is missing property 'foo'", ); } - - if (!(is_array($data['foo']))){ + + if (!(is_array($data['foo']))) { throw new Exception( "Expected property 'foo' in JSON data to be array, instead received " . get_debug_type($data['foo']), ); @@ -199,13 +209,13 @@ public static function jsonDeserialize(array $data): static { $args['value'] = Foo::jsonDeserialize($data['foo']); break; case 'bar': - if (!array_key_exists('bar', $data)){ + if (!array_key_exists('bar', $data)) { throw new Exception( "JSON data is missing property 'bar'", ); } - - if (!(is_array($data['bar']))){ + + if (!(is_array($data['bar']))) { throw new Exception( "Expected property 'bar' in JSON data to be array, instead received " . get_debug_type($data['bar']), ); @@ -217,7 +227,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/unions-with-local-date/src/Types/UnionWithBaseProperties.php b/seed/php-model/unions-with-local-date/src/Types/UnionWithBaseProperties.php index 2cd29a393056..d153f3734293 100644 --- a/seed/php-model/unions-with-local-date/src/Types/UnionWithBaseProperties.php +++ b/seed/php-model/unions-with-local-date/src/Types/UnionWithBaseProperties.php @@ -54,9 +54,10 @@ class UnionWithBaseProperties extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->id = $values['id'];$this->type = $values['type'];$this->value = $values['value']; + ) { + $this->id = $values['id']; + $this->type = $values['type']; + $this->value = $values['value']; } /** @@ -64,7 +65,8 @@ private function __construct( * @param int $integer * @return UnionWithBaseProperties */ - public static function integer(string $id, int $integer): UnionWithBaseProperties { + public static function integer(string $id, int $integer): UnionWithBaseProperties + { return new UnionWithBaseProperties([ 'id' => $id, 'type' => 'integer', @@ -77,7 +79,8 @@ public static function integer(string $id, int $integer): UnionWithBasePropertie * @param string $string * @return UnionWithBaseProperties */ - public static function string(string $id, string $string): UnionWithBaseProperties { + public static function string(string $id, string $string): UnionWithBaseProperties + { return new UnionWithBaseProperties([ 'id' => $id, 'type' => 'string', @@ -90,7 +93,8 @@ public static function string(string $id, string $string): UnionWithBaseProperti * @param Foo $foo * @return UnionWithBaseProperties */ - public static function foo(string $id, Foo $foo): UnionWithBaseProperties { + public static function foo(string $id, Foo $foo): UnionWithBaseProperties + { return new UnionWithBaseProperties([ 'id' => $id, 'type' => 'foo', @@ -101,81 +105,89 @@ public static function foo(string $id, Foo $foo): UnionWithBaseProperties { /** * @return bool */ - public function isInteger(): bool { - return is_int($this->value)&& $this->type === 'integer'; + public function isInteger(): bool + { + return is_int($this->value) && $this->type === 'integer'; } /** * @return int */ - public function asInteger(): int { - if (!(is_int($this->value)&& $this->type === 'integer')){ + public function asInteger(): int + { + if (!(is_int($this->value) && $this->type === 'integer')) { throw new Exception( "Expected integer; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isString(): bool { - return is_string($this->value)&& $this->type === 'string'; + public function isString(): bool + { + return is_string($this->value) && $this->type === 'string'; } /** * @return string */ - public function asString(): string { - if (!(is_string($this->value)&& $this->type === 'string')){ + public function asString(): string + { + if (!(is_string($this->value) && $this->type === 'string')) { throw new Exception( "Expected string; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isFoo(): bool { - return $this->value instanceof Foo&& $this->type === 'foo'; + public function isFoo(): bool + { + return $this->value instanceof Foo && $this->type === 'foo'; } /** * @return Foo */ - public function asFoo(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo')){ + public function asFoo(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo')) { throw new Exception( "Expected foo; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'integer': $value = $this->value; $result['integer'] = $value; @@ -190,26 +202,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -218,50 +231,51 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('id', $data)){ + if (!array_key_exists('id', $data)) { throw new Exception( "JSON data is missing property 'id'", ); } - if (!(is_string($data['id']))){ + if (!(is_string($data['id']))) { throw new Exception( "Expected property 'id' in JSON data to be string, instead received " . get_debug_type($data['id']), ); } $args['id'] = $data['id']; - - if (!array_key_exists('type', $data)){ + + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'integer': - if (!array_key_exists('integer', $data)){ + if (!array_key_exists('integer', $data)) { throw new Exception( "JSON data is missing property 'integer'", ); } - + $args['value'] = $data['integer']; break; case 'string': - if (!array_key_exists('string', $data)){ + if (!array_key_exists('string', $data)) { throw new Exception( "JSON data is missing property 'string'", ); } - + $args['value'] = $data['string']; break; case 'foo': @@ -272,7 +286,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/unions-with-local-date/src/Types/UnionWithDiscriminant.php b/seed/php-model/unions-with-local-date/src/Types/UnionWithDiscriminant.php index 9df4071fb159..c1c8317343c7 100644 --- a/seed/php-model/unions-with-local-date/src/Types/UnionWithDiscriminant.php +++ b/seed/php-model/unions-with-local-date/src/Types/UnionWithDiscriminant.php @@ -42,16 +42,17 @@ class UnionWithDiscriminant extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param Foo $foo * @return UnionWithDiscriminant */ - public static function foo(Foo $foo): UnionWithDiscriminant { + public static function foo(Foo $foo): UnionWithDiscriminant + { return new UnionWithDiscriminant([ 'type' => 'foo', 'value' => $foo, @@ -62,7 +63,8 @@ public static function foo(Foo $foo): UnionWithDiscriminant { * @param Bar $bar * @return UnionWithDiscriminant */ - public static function bar(Bar $bar): UnionWithDiscriminant { + public static function bar(Bar $bar): UnionWithDiscriminant + { return new UnionWithDiscriminant([ 'type' => 'bar', 'value' => $bar, @@ -72,61 +74,67 @@ public static function bar(Bar $bar): UnionWithDiscriminant { /** * @return bool */ - public function isFoo(): bool { - return $this->value instanceof Foo&& $this->type === 'foo'; + public function isFoo(): bool + { + return $this->value instanceof Foo && $this->type === 'foo'; } /** * @return Foo */ - public function asFoo(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo')){ + public function asFoo(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo')) { throw new Exception( "Expected foo; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isBar(): bool { - return $this->value instanceof Bar&& $this->type === 'bar'; + public function isBar(): bool + { + return $this->value instanceof Bar && $this->type === 'bar'; } /** * @return Bar */ - public function asBar(): Bar { - if (!($this->value instanceof Bar&& $this->type === 'bar')){ + public function asBar(): Bar + { + if (!($this->value instanceof Bar && $this->type === 'bar')) { throw new Exception( "Expected bar; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['_type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'foo': $value = $this->asFoo()->jsonSerialize(); $result['foo'] = $value; @@ -137,26 +145,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -165,30 +174,31 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('_type', $data)){ + if (!array_key_exists('_type', $data)) { throw new Exception( "JSON data is missing property '_type'", ); } $type = $data['_type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['_type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'foo': - if (!array_key_exists('foo', $data)){ + if (!array_key_exists('foo', $data)) { throw new Exception( "JSON data is missing property 'foo'", ); } - - if (!(is_array($data['foo']))){ + + if (!(is_array($data['foo']))) { throw new Exception( "Expected property 'foo' in JSON data to be array, instead received " . get_debug_type($data['foo']), ); @@ -196,13 +206,13 @@ public static function jsonDeserialize(array $data): static { $args['value'] = Foo::jsonDeserialize($data['foo']); break; case 'bar': - if (!array_key_exists('bar', $data)){ + if (!array_key_exists('bar', $data)) { throw new Exception( "JSON data is missing property 'bar'", ); } - - if (!(is_array($data['bar']))){ + + if (!(is_array($data['bar']))) { throw new Exception( "Expected property 'bar' in JSON data to be array, instead received " . get_debug_type($data['bar']), ); @@ -214,7 +224,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/unions-with-local-date/src/Types/UnionWithDuplicatePrimitive.php b/seed/php-model/unions-with-local-date/src/Types/UnionWithDuplicatePrimitive.php index 9fd6e32c0fdc..1ef734b3bc47 100644 --- a/seed/php-model/unions-with-local-date/src/Types/UnionWithDuplicatePrimitive.php +++ b/seed/php-model/unions-with-local-date/src/Types/UnionWithDuplicatePrimitive.php @@ -46,16 +46,17 @@ class UnionWithDuplicatePrimitive extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param int $integer1 * @return UnionWithDuplicatePrimitive */ - public static function integer1(int $integer1): UnionWithDuplicatePrimitive { + public static function integer1(int $integer1): UnionWithDuplicatePrimitive + { return new UnionWithDuplicatePrimitive([ 'type' => 'integer1', 'value' => $integer1, @@ -66,7 +67,8 @@ public static function integer1(int $integer1): UnionWithDuplicatePrimitive { * @param int $integer2 * @return UnionWithDuplicatePrimitive */ - public static function integer2(int $integer2): UnionWithDuplicatePrimitive { + public static function integer2(int $integer2): UnionWithDuplicatePrimitive + { return new UnionWithDuplicatePrimitive([ 'type' => 'integer2', 'value' => $integer2, @@ -77,7 +79,8 @@ public static function integer2(int $integer2): UnionWithDuplicatePrimitive { * @param string $string1 * @return UnionWithDuplicatePrimitive */ - public static function string1(string $string1): UnionWithDuplicatePrimitive { + public static function string1(string $string1): UnionWithDuplicatePrimitive + { return new UnionWithDuplicatePrimitive([ 'type' => 'string1', 'value' => $string1, @@ -88,7 +91,8 @@ public static function string1(string $string1): UnionWithDuplicatePrimitive { * @param string $string2 * @return UnionWithDuplicatePrimitive */ - public static function string2(string $string2): UnionWithDuplicatePrimitive { + public static function string2(string $string2): UnionWithDuplicatePrimitive + { return new UnionWithDuplicatePrimitive([ 'type' => 'string2', 'value' => $string2, @@ -98,101 +102,111 @@ public static function string2(string $string2): UnionWithDuplicatePrimitive { /** * @return bool */ - public function isInteger1(): bool { - return is_int($this->value)&& $this->type === 'integer1'; + public function isInteger1(): bool + { + return is_int($this->value) && $this->type === 'integer1'; } /** * @return int */ - public function asInteger1(): int { - if (!(is_int($this->value)&& $this->type === 'integer1')){ + public function asInteger1(): int + { + if (!(is_int($this->value) && $this->type === 'integer1')) { throw new Exception( "Expected integer1; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isInteger2(): bool { - return is_int($this->value)&& $this->type === 'integer2'; + public function isInteger2(): bool + { + return is_int($this->value) && $this->type === 'integer2'; } /** * @return int */ - public function asInteger2(): int { - if (!(is_int($this->value)&& $this->type === 'integer2')){ + public function asInteger2(): int + { + if (!(is_int($this->value) && $this->type === 'integer2')) { throw new Exception( "Expected integer2; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isString1(): bool { - return is_string($this->value)&& $this->type === 'string1'; + public function isString1(): bool + { + return is_string($this->value) && $this->type === 'string1'; } /** * @return string */ - public function asString1(): string { - if (!(is_string($this->value)&& $this->type === 'string1')){ + public function asString1(): string + { + if (!(is_string($this->value) && $this->type === 'string1')) { throw new Exception( "Expected string1; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isString2(): bool { - return is_string($this->value)&& $this->type === 'string2'; + public function isString2(): bool + { + return is_string($this->value) && $this->type === 'string2'; } /** * @return string */ - public function asString2(): string { - if (!(is_string($this->value)&& $this->type === 'string2')){ + public function asString2(): string + { + if (!(is_string($this->value) && $this->type === 'string2')) { throw new Exception( "Expected string2; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'integer1': $value = $this->value; $result['integer1'] = $value; @@ -211,26 +225,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -239,56 +254,57 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'integer1': - if (!array_key_exists('integer1', $data)){ + if (!array_key_exists('integer1', $data)) { throw new Exception( "JSON data is missing property 'integer1'", ); } - + $args['value'] = $data['integer1']; break; case 'integer2': - if (!array_key_exists('integer2', $data)){ + if (!array_key_exists('integer2', $data)) { throw new Exception( "JSON data is missing property 'integer2'", ); } - + $args['value'] = $data['integer2']; break; case 'string1': - if (!array_key_exists('string1', $data)){ + if (!array_key_exists('string1', $data)) { throw new Exception( "JSON data is missing property 'string1'", ); } - + $args['value'] = $data['string1']; break; case 'string2': - if (!array_key_exists('string2', $data)){ + if (!array_key_exists('string2', $data)) { throw new Exception( "JSON data is missing property 'string2'", ); } - + $args['value'] = $data['string2']; break; case '_unknown': @@ -296,7 +312,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/unions-with-local-date/src/Types/UnionWithDuplicateTypes.php b/seed/php-model/unions-with-local-date/src/Types/UnionWithDuplicateTypes.php index 316ae9373aab..e99748222593 100644 --- a/seed/php-model/unions-with-local-date/src/Types/UnionWithDuplicateTypes.php +++ b/seed/php-model/unions-with-local-date/src/Types/UnionWithDuplicateTypes.php @@ -40,16 +40,17 @@ class UnionWithDuplicateTypes extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param Foo $foo1 * @return UnionWithDuplicateTypes */ - public static function foo1(Foo $foo1): UnionWithDuplicateTypes { + public static function foo1(Foo $foo1): UnionWithDuplicateTypes + { return new UnionWithDuplicateTypes([ 'type' => 'foo1', 'value' => $foo1, @@ -60,7 +61,8 @@ public static function foo1(Foo $foo1): UnionWithDuplicateTypes { * @param Foo $foo2 * @return UnionWithDuplicateTypes */ - public static function foo2(Foo $foo2): UnionWithDuplicateTypes { + public static function foo2(Foo $foo2): UnionWithDuplicateTypes + { return new UnionWithDuplicateTypes([ 'type' => 'foo2', 'value' => $foo2, @@ -70,61 +72,67 @@ public static function foo2(Foo $foo2): UnionWithDuplicateTypes { /** * @return bool */ - public function isFoo1(): bool { - return $this->value instanceof Foo&& $this->type === 'foo1'; + public function isFoo1(): bool + { + return $this->value instanceof Foo && $this->type === 'foo1'; } /** * @return Foo */ - public function asFoo1(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo1')){ + public function asFoo1(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo1')) { throw new Exception( "Expected foo1; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isFoo2(): bool { - return $this->value instanceof Foo&& $this->type === 'foo2'; + public function isFoo2(): bool + { + return $this->value instanceof Foo && $this->type === 'foo2'; } /** * @return Foo */ - public function asFoo2(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo2')){ + public function asFoo2(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo2')) { throw new Exception( "Expected foo2; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'foo1': $value = $this->asFoo1()->jsonSerialize(); $result = array_merge($value, $result); @@ -135,26 +143,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -163,22 +172,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'foo1': $args['value'] = Foo::jsonDeserialize($data); break; @@ -190,7 +200,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/unions-with-local-date/src/Types/UnionWithLiteral.php b/seed/php-model/unions-with-local-date/src/Types/UnionWithLiteral.php index 9d0afffb3a00..c10f4f528918 100644 --- a/seed/php-model/unions-with-local-date/src/Types/UnionWithLiteral.php +++ b/seed/php-model/unions-with-local-date/src/Types/UnionWithLiteral.php @@ -46,9 +46,10 @@ class UnionWithLiteral extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->base = $values['base'];$this->type = $values['type'];$this->value = $values['value']; + ) { + $this->base = $values['base']; + $this->type = $values['type']; + $this->value = $values['value']; } /** @@ -56,7 +57,8 @@ private function __construct( * @param 'fern' $fern * @return UnionWithLiteral */ - public static function fern(string $base, string $fern): UnionWithLiteral { + public static function fern(string $base, string $fern): UnionWithLiteral + { return new UnionWithLiteral([ 'base' => $base, 'type' => 'fern', @@ -67,67 +69,72 @@ public static function fern(string $base, string $fern): UnionWithLiteral { /** * @return bool */ - public function isFern(): bool { - return $this->value === 'fern'&& $this->type === 'fern'; + public function isFern(): bool + { + return $this->value === 'fern' && $this->type === 'fern'; } /** * @return 'fern' */ - public function asFern(): string { - if (!($this->value === 'fern'&& $this->type === 'fern')){ + public function asFern(): string + { + if (!($this->value === 'fern' && $this->type === 'fern')) { throw new Exception( "Expected fern; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'fern': $value = $this->value; $result['fern'] = $value; break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -136,41 +143,42 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('base', $data)){ + if (!array_key_exists('base', $data)) { throw new Exception( "JSON data is missing property 'base'", ); } - if (!($data['base'] === 'base')){ + if (!($data['base'] === 'base')) { throw new Exception( "Expected property 'base' in JSON data to be 'base', instead received " . get_debug_type($data['base']), ); } $args['base'] = $data['base']; - - if (!array_key_exists('type', $data)){ + + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'fern': - if (!array_key_exists('fern', $data)){ + if (!array_key_exists('fern', $data)) { throw new Exception( "JSON data is missing property 'fern'", ); } - + $args['value'] = $data['fern']; break; case '_unknown': @@ -178,7 +186,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/unions-with-local-date/src/Types/UnionWithMultipleNoProperties.php b/seed/php-model/unions-with-local-date/src/Types/UnionWithMultipleNoProperties.php index 5bd1fedbdbe2..7d1e43b8031c 100644 --- a/seed/php-model/unions-with-local-date/src/Types/UnionWithMultipleNoProperties.php +++ b/seed/php-model/unions-with-local-date/src/Types/UnionWithMultipleNoProperties.php @@ -44,16 +44,17 @@ class UnionWithMultipleNoProperties extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param Foo $foo * @return UnionWithMultipleNoProperties */ - public static function foo(Foo $foo): UnionWithMultipleNoProperties { + public static function foo(Foo $foo): UnionWithMultipleNoProperties + { return new UnionWithMultipleNoProperties([ 'type' => 'foo', 'value' => $foo, @@ -63,7 +64,8 @@ public static function foo(Foo $foo): UnionWithMultipleNoProperties { /** * @return UnionWithMultipleNoProperties */ - public static function empty1(): UnionWithMultipleNoProperties { + public static function empty1(): UnionWithMultipleNoProperties + { return new UnionWithMultipleNoProperties([ 'type' => 'empty1', 'value' => null, @@ -73,7 +75,8 @@ public static function empty1(): UnionWithMultipleNoProperties { /** * @return UnionWithMultipleNoProperties */ - public static function empty2(): UnionWithMultipleNoProperties { + public static function empty2(): UnionWithMultipleNoProperties + { return new UnionWithMultipleNoProperties([ 'type' => 'empty2', 'value' => null, @@ -83,55 +86,61 @@ public static function empty2(): UnionWithMultipleNoProperties { /** * @return bool */ - public function isFoo(): bool { - return $this->value instanceof Foo&& $this->type === 'foo'; + public function isFoo(): bool + { + return $this->value instanceof Foo && $this->type === 'foo'; } /** * @return Foo */ - public function asFoo(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo')){ + public function asFoo(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo')) { throw new Exception( "Expected foo; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isEmpty1(): bool { - return is_null($this->value)&& $this->type === 'empty1'; + public function isEmpty1(): bool + { + return is_null($this->value) && $this->type === 'empty1'; } /** * @return bool */ - public function isEmpty2(): bool { - return is_null($this->value)&& $this->type === 'empty2'; + public function isEmpty2(): bool + { + return is_null($this->value) && $this->type === 'empty2'; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'foo': $value = $this->asFoo()->jsonSerialize(); $result = array_merge($value, $result); @@ -144,26 +153,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -172,22 +182,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'foo': $args['value'] = Foo::jsonDeserialize($data); break; @@ -202,7 +213,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/unions-with-local-date/src/Types/UnionWithNoProperties.php b/seed/php-model/unions-with-local-date/src/Types/UnionWithNoProperties.php index a4afa158a378..aa4fd389b09d 100644 --- a/seed/php-model/unions-with-local-date/src/Types/UnionWithNoProperties.php +++ b/seed/php-model/unions-with-local-date/src/Types/UnionWithNoProperties.php @@ -42,16 +42,17 @@ class UnionWithNoProperties extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param Foo $foo * @return UnionWithNoProperties */ - public static function foo(Foo $foo): UnionWithNoProperties { + public static function foo(Foo $foo): UnionWithNoProperties + { return new UnionWithNoProperties([ 'type' => 'foo', 'value' => $foo, @@ -61,7 +62,8 @@ public static function foo(Foo $foo): UnionWithNoProperties { /** * @return UnionWithNoProperties */ - public static function empty(): UnionWithNoProperties { + public static function empty(): UnionWithNoProperties + { return new UnionWithNoProperties([ 'type' => 'empty', 'value' => null, @@ -71,48 +73,53 @@ public static function empty(): UnionWithNoProperties { /** * @return bool */ - public function isFoo(): bool { - return $this->value instanceof Foo&& $this->type === 'foo'; + public function isFoo(): bool + { + return $this->value instanceof Foo && $this->type === 'foo'; } /** * @return Foo */ - public function asFoo(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo')){ + public function asFoo(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo')) { throw new Exception( "Expected foo; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isEmpty(): bool { - return is_null($this->value)&& $this->type === 'empty'; + public function isEmpty(): bool + { + return is_null($this->value) && $this->type === 'empty'; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'foo': $value = $this->asFoo()->jsonSerialize(); $result = array_merge($value, $result); @@ -122,26 +129,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -150,22 +158,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'foo': $args['value'] = Foo::jsonDeserialize($data); break; @@ -177,7 +186,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/unions-with-local-date/src/Types/UnionWithOptionalTime.php b/seed/php-model/unions-with-local-date/src/Types/UnionWithOptionalTime.php index c56866ef3e99..afcbbf0dfa69 100644 --- a/seed/php-model/unions-with-local-date/src/Types/UnionWithOptionalTime.php +++ b/seed/php-model/unions-with-local-date/src/Types/UnionWithOptionalTime.php @@ -44,16 +44,17 @@ class UnionWithOptionalTime extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param ?DateTime $date * @return UnionWithOptionalTime */ - public static function date(?DateTime $date = null): UnionWithOptionalTime { + public static function date(?DateTime $date = null): UnionWithOptionalTime + { return new UnionWithOptionalTime([ 'type' => 'date', 'value' => $date, @@ -64,7 +65,8 @@ public static function date(?DateTime $date = null): UnionWithOptionalTime { * @param ?DateTime $datetime * @return UnionWithOptionalTime */ - public static function datetime(?DateTime $datetime = null): UnionWithOptionalTime { + public static function datetime(?DateTime $datetime = null): UnionWithOptionalTime + { return new UnionWithOptionalTime([ 'type' => 'datetime', 'value' => $datetime, @@ -74,97 +76,104 @@ public static function datetime(?DateTime $datetime = null): UnionWithOptionalTi /** * @return bool */ - public function isDate(): bool { - return (is_null($this->value) || $this->value instanceof DateTime)&& $this->type === 'date'; + public function isDate(): bool + { + return (is_null($this->value) || $this->value instanceof DateTime) && $this->type === 'date'; } /** * @return ?DateTime */ - public function asDate(): ?DateTime { - if (!((is_null($this->value) || $this->value instanceof DateTime)&& $this->type === 'date')){ + public function asDate(): ?DateTime + { + if (!((is_null($this->value) || $this->value instanceof DateTime) && $this->type === 'date')) { throw new Exception( "Expected date; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isDatetime(): bool { - return (is_null($this->value) || $this->value instanceof DateTime)&& $this->type === 'datetime'; + public function isDatetime(): bool + { + return (is_null($this->value) || $this->value instanceof DateTime) && $this->type === 'datetime'; } /** * @return ?DateTime */ - public function asDatetime(): ?DateTime { - if (!((is_null($this->value) || $this->value instanceof DateTime)&& $this->type === 'datetime')){ + public function asDatetime(): ?DateTime + { + if (!((is_null($this->value) || $this->value instanceof DateTime) && $this->type === 'datetime')) { throw new Exception( "Expected datetime; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'date': $value = $this->asDate(); - if (!is_null($value)){ + if (!is_null($value)) { $value = JsonSerializer::serializeDate($value); } $result['date'] = $value; break; case 'datetime': $value = $this->asDatetime(); - if (!is_null($value)){ + if (!is_null($value)) { $value = JsonSerializer::serializeDateTime($value); } $result['datetime'] = $value; break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -173,38 +182,39 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'date': - if (!array_key_exists('date', $data)){ + if (!array_key_exists('date', $data)) { throw new Exception( "JSON data is missing property 'date'", ); } - + $args['value'] = $data['date']; break; case 'datetime': - if (!array_key_exists('datetime', $data)){ + if (!array_key_exists('datetime', $data)) { throw new Exception( "JSON data is missing property 'datetime'", ); } - + $args['value'] = $data['datetime']; break; case '_unknown': @@ -212,7 +222,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/unions-with-local-date/src/Types/UnionWithPrimitive.php b/seed/php-model/unions-with-local-date/src/Types/UnionWithPrimitive.php index d67cef8f83e8..6c7edcd62712 100644 --- a/seed/php-model/unions-with-local-date/src/Types/UnionWithPrimitive.php +++ b/seed/php-model/unions-with-local-date/src/Types/UnionWithPrimitive.php @@ -42,16 +42,17 @@ class UnionWithPrimitive extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param int $integer * @return UnionWithPrimitive */ - public static function integer(int $integer): UnionWithPrimitive { + public static function integer(int $integer): UnionWithPrimitive + { return new UnionWithPrimitive([ 'type' => 'integer', 'value' => $integer, @@ -62,7 +63,8 @@ public static function integer(int $integer): UnionWithPrimitive { * @param string $string * @return UnionWithPrimitive */ - public static function string(string $string): UnionWithPrimitive { + public static function string(string $string): UnionWithPrimitive + { return new UnionWithPrimitive([ 'type' => 'string', 'value' => $string, @@ -72,61 +74,67 @@ public static function string(string $string): UnionWithPrimitive { /** * @return bool */ - public function isInteger(): bool { - return is_int($this->value)&& $this->type === 'integer'; + public function isInteger(): bool + { + return is_int($this->value) && $this->type === 'integer'; } /** * @return int */ - public function asInteger(): int { - if (!(is_int($this->value)&& $this->type === 'integer')){ + public function asInteger(): int + { + if (!(is_int($this->value) && $this->type === 'integer')) { throw new Exception( "Expected integer; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isString(): bool { - return is_string($this->value)&& $this->type === 'string'; + public function isString(): bool + { + return is_string($this->value) && $this->type === 'string'; } /** * @return string */ - public function asString(): string { - if (!(is_string($this->value)&& $this->type === 'string')){ + public function asString(): string + { + if (!(is_string($this->value) && $this->type === 'string')) { throw new Exception( "Expected string; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'integer': $value = $this->value; $result['integer'] = $value; @@ -137,26 +145,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -165,38 +174,39 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'integer': - if (!array_key_exists('integer', $data)){ + if (!array_key_exists('integer', $data)) { throw new Exception( "JSON data is missing property 'integer'", ); } - + $args['value'] = $data['integer']; break; case 'string': - if (!array_key_exists('string', $data)){ + if (!array_key_exists('string', $data)) { throw new Exception( "JSON data is missing property 'string'", ); } - + $args['value'] = $data['string']; break; case '_unknown': @@ -204,7 +214,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/unions-with-local-date/src/Types/UnionWithSameNumberTypes.php b/seed/php-model/unions-with-local-date/src/Types/UnionWithSameNumberTypes.php index fd1c25243e83..24f90d39e8bd 100644 --- a/seed/php-model/unions-with-local-date/src/Types/UnionWithSameNumberTypes.php +++ b/seed/php-model/unions-with-local-date/src/Types/UnionWithSameNumberTypes.php @@ -44,16 +44,17 @@ class UnionWithSameNumberTypes extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param int $positiveInt * @return UnionWithSameNumberTypes */ - public static function positiveInt(int $positiveInt): UnionWithSameNumberTypes { + public static function positiveInt(int $positiveInt): UnionWithSameNumberTypes + { return new UnionWithSameNumberTypes([ 'type' => 'positiveInt', 'value' => $positiveInt, @@ -64,7 +65,8 @@ public static function positiveInt(int $positiveInt): UnionWithSameNumberTypes { * @param int $negativeInt * @return UnionWithSameNumberTypes */ - public static function negativeInt(int $negativeInt): UnionWithSameNumberTypes { + public static function negativeInt(int $negativeInt): UnionWithSameNumberTypes + { return new UnionWithSameNumberTypes([ 'type' => 'negativeInt', 'value' => $negativeInt, @@ -75,7 +77,8 @@ public static function negativeInt(int $negativeInt): UnionWithSameNumberTypes { * @param float $anyNumber * @return UnionWithSameNumberTypes */ - public static function anyNumber(float $anyNumber): UnionWithSameNumberTypes { + public static function anyNumber(float $anyNumber): UnionWithSameNumberTypes + { return new UnionWithSameNumberTypes([ 'type' => 'anyNumber', 'value' => $anyNumber, @@ -85,81 +88,89 @@ public static function anyNumber(float $anyNumber): UnionWithSameNumberTypes { /** * @return bool */ - public function isPositiveInt(): bool { - return is_int($this->value)&& $this->type === 'positiveInt'; + public function isPositiveInt(): bool + { + return is_int($this->value) && $this->type === 'positiveInt'; } /** * @return int */ - public function asPositiveInt(): int { - if (!(is_int($this->value)&& $this->type === 'positiveInt')){ + public function asPositiveInt(): int + { + if (!(is_int($this->value) && $this->type === 'positiveInt')) { throw new Exception( "Expected positiveInt; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isNegativeInt(): bool { - return is_int($this->value)&& $this->type === 'negativeInt'; + public function isNegativeInt(): bool + { + return is_int($this->value) && $this->type === 'negativeInt'; } /** * @return int */ - public function asNegativeInt(): int { - if (!(is_int($this->value)&& $this->type === 'negativeInt')){ + public function asNegativeInt(): int + { + if (!(is_int($this->value) && $this->type === 'negativeInt')) { throw new Exception( "Expected negativeInt; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isAnyNumber(): bool { - return is_float($this->value)&& $this->type === 'anyNumber'; + public function isAnyNumber(): bool + { + return is_float($this->value) && $this->type === 'anyNumber'; } /** * @return float */ - public function asAnyNumber(): float { - if (!(is_float($this->value)&& $this->type === 'anyNumber')){ + public function asAnyNumber(): float + { + if (!(is_float($this->value) && $this->type === 'anyNumber')) { throw new Exception( "Expected anyNumber; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'positiveInt': $value = $this->value; $result['positiveInt'] = $value; @@ -174,26 +185,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -202,47 +214,48 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'positiveInt': - if (!array_key_exists('positiveInt', $data)){ + if (!array_key_exists('positiveInt', $data)) { throw new Exception( "JSON data is missing property 'positiveInt'", ); } - + $args['value'] = $data['positiveInt']; break; case 'negativeInt': - if (!array_key_exists('negativeInt', $data)){ + if (!array_key_exists('negativeInt', $data)) { throw new Exception( "JSON data is missing property 'negativeInt'", ); } - + $args['value'] = $data['negativeInt']; break; case 'anyNumber': - if (!array_key_exists('anyNumber', $data)){ + if (!array_key_exists('anyNumber', $data)) { throw new Exception( "JSON data is missing property 'anyNumber'", ); } - + $args['value'] = $data['anyNumber']; break; case '_unknown': @@ -250,7 +263,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/unions-with-local-date/src/Types/UnionWithSameStringTypes.php b/seed/php-model/unions-with-local-date/src/Types/UnionWithSameStringTypes.php index 7f16554362b0..9f8c047439e3 100644 --- a/seed/php-model/unions-with-local-date/src/Types/UnionWithSameStringTypes.php +++ b/seed/php-model/unions-with-local-date/src/Types/UnionWithSameStringTypes.php @@ -42,16 +42,17 @@ class UnionWithSameStringTypes extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param string $customFormat * @return UnionWithSameStringTypes */ - public static function customFormat(string $customFormat): UnionWithSameStringTypes { + public static function customFormat(string $customFormat): UnionWithSameStringTypes + { return new UnionWithSameStringTypes([ 'type' => 'customFormat', 'value' => $customFormat, @@ -62,7 +63,8 @@ public static function customFormat(string $customFormat): UnionWithSameStringTy * @param string $regularString * @return UnionWithSameStringTypes */ - public static function regularString(string $regularString): UnionWithSameStringTypes { + public static function regularString(string $regularString): UnionWithSameStringTypes + { return new UnionWithSameStringTypes([ 'type' => 'regularString', 'value' => $regularString, @@ -73,7 +75,8 @@ public static function regularString(string $regularString): UnionWithSameString * @param string $patternString * @return UnionWithSameStringTypes */ - public static function patternString(string $patternString): UnionWithSameStringTypes { + public static function patternString(string $patternString): UnionWithSameStringTypes + { return new UnionWithSameStringTypes([ 'type' => 'patternString', 'value' => $patternString, @@ -83,81 +86,89 @@ public static function patternString(string $patternString): UnionWithSameString /** * @return bool */ - public function isCustomFormat(): bool { - return is_string($this->value)&& $this->type === 'customFormat'; + public function isCustomFormat(): bool + { + return is_string($this->value) && $this->type === 'customFormat'; } /** * @return string */ - public function asCustomFormat(): string { - if (!(is_string($this->value)&& $this->type === 'customFormat')){ + public function asCustomFormat(): string + { + if (!(is_string($this->value) && $this->type === 'customFormat')) { throw new Exception( "Expected customFormat; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isRegularString(): bool { - return is_string($this->value)&& $this->type === 'regularString'; + public function isRegularString(): bool + { + return is_string($this->value) && $this->type === 'regularString'; } /** * @return string */ - public function asRegularString(): string { - if (!(is_string($this->value)&& $this->type === 'regularString')){ + public function asRegularString(): string + { + if (!(is_string($this->value) && $this->type === 'regularString')) { throw new Exception( "Expected regularString; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isPatternString(): bool { - return is_string($this->value)&& $this->type === 'patternString'; + public function isPatternString(): bool + { + return is_string($this->value) && $this->type === 'patternString'; } /** * @return string */ - public function asPatternString(): string { - if (!(is_string($this->value)&& $this->type === 'patternString')){ + public function asPatternString(): string + { + if (!(is_string($this->value) && $this->type === 'patternString')) { throw new Exception( "Expected patternString; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'customFormat': $value = $this->value; $result['customFormat'] = $value; @@ -172,26 +183,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -200,47 +212,48 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'customFormat': - if (!array_key_exists('customFormat', $data)){ + if (!array_key_exists('customFormat', $data)) { throw new Exception( "JSON data is missing property 'customFormat'", ); } - + $args['value'] = $data['customFormat']; break; case 'regularString': - if (!array_key_exists('regularString', $data)){ + if (!array_key_exists('regularString', $data)) { throw new Exception( "JSON data is missing property 'regularString'", ); } - + $args['value'] = $data['regularString']; break; case 'patternString': - if (!array_key_exists('patternString', $data)){ + if (!array_key_exists('patternString', $data)) { throw new Exception( "JSON data is missing property 'patternString'", ); } - + $args['value'] = $data['patternString']; break; case '_unknown': @@ -248,7 +261,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/unions-with-local-date/src/Types/UnionWithSingleElement.php b/seed/php-model/unions-with-local-date/src/Types/UnionWithSingleElement.php index 8000de5cf6d5..5d17d877b09f 100644 --- a/seed/php-model/unions-with-local-date/src/Types/UnionWithSingleElement.php +++ b/seed/php-model/unions-with-local-date/src/Types/UnionWithSingleElement.php @@ -38,16 +38,17 @@ class UnionWithSingleElement extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param Foo $foo * @return UnionWithSingleElement */ - public static function foo(Foo $foo): UnionWithSingleElement { + public static function foo(Foo $foo): UnionWithSingleElement + { return new UnionWithSingleElement([ 'type' => 'foo', 'value' => $foo, @@ -57,67 +58,72 @@ public static function foo(Foo $foo): UnionWithSingleElement { /** * @return bool */ - public function isFoo(): bool { - return $this->value instanceof Foo&& $this->type === 'foo'; + public function isFoo(): bool + { + return $this->value instanceof Foo && $this->type === 'foo'; } /** * @return Foo */ - public function asFoo(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo')){ + public function asFoo(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo')) { throw new Exception( "Expected foo; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'foo': $value = $this->asFoo()->jsonSerialize(); $result = array_merge($value, $result); break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -126,22 +132,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'foo': $args['value'] = Foo::jsonDeserialize($data); break; @@ -150,7 +157,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/unions-with-local-date/src/Types/UnionWithSubTypes.php b/seed/php-model/unions-with-local-date/src/Types/UnionWithSubTypes.php index 9740344f1885..7157cd2a6d5b 100644 --- a/seed/php-model/unions-with-local-date/src/Types/UnionWithSubTypes.php +++ b/seed/php-model/unions-with-local-date/src/Types/UnionWithSubTypes.php @@ -42,16 +42,17 @@ class UnionWithSubTypes extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param Foo $foo * @return UnionWithSubTypes */ - public static function foo(Foo $foo): UnionWithSubTypes { + public static function foo(Foo $foo): UnionWithSubTypes + { return new UnionWithSubTypes([ 'type' => 'foo', 'value' => $foo, @@ -62,7 +63,8 @@ public static function foo(Foo $foo): UnionWithSubTypes { * @param FooExtended $fooExtended * @return UnionWithSubTypes */ - public static function fooExtended(FooExtended $fooExtended): UnionWithSubTypes { + public static function fooExtended(FooExtended $fooExtended): UnionWithSubTypes + { return new UnionWithSubTypes([ 'type' => 'fooExtended', 'value' => $fooExtended, @@ -72,61 +74,67 @@ public static function fooExtended(FooExtended $fooExtended): UnionWithSubTypes /** * @return bool */ - public function isFoo(): bool { - return $this->value instanceof Foo&& $this->type === 'foo'; + public function isFoo(): bool + { + return $this->value instanceof Foo && $this->type === 'foo'; } /** * @return Foo */ - public function asFoo(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo')){ + public function asFoo(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo')) { throw new Exception( "Expected foo; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isFooExtended(): bool { - return $this->value instanceof FooExtended&& $this->type === 'fooExtended'; + public function isFooExtended(): bool + { + return $this->value instanceof FooExtended && $this->type === 'fooExtended'; } /** * @return FooExtended */ - public function asFooExtended(): FooExtended { - if (!($this->value instanceof FooExtended&& $this->type === 'fooExtended')){ + public function asFooExtended(): FooExtended + { + if (!($this->value instanceof FooExtended && $this->type === 'fooExtended')) { throw new Exception( "Expected fooExtended; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'foo': $value = $this->asFoo()->jsonSerialize(); $result = array_merge($value, $result); @@ -137,26 +145,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -165,22 +174,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'foo': $args['value'] = Foo::jsonDeserialize($data); break; @@ -192,7 +202,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/unions-with-local-date/src/Types/UnionWithTime.php b/seed/php-model/unions-with-local-date/src/Types/UnionWithTime.php index 76df1c11c57b..5dedd81336fd 100644 --- a/seed/php-model/unions-with-local-date/src/Types/UnionWithTime.php +++ b/seed/php-model/unions-with-local-date/src/Types/UnionWithTime.php @@ -46,16 +46,17 @@ class UnionWithTime extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param int $value * @return UnionWithTime */ - public static function value(int $value): UnionWithTime { + public static function value(int $value): UnionWithTime + { return new UnionWithTime([ 'type' => 'value', 'value' => $value, @@ -66,7 +67,8 @@ public static function value(int $value): UnionWithTime { * @param DateTime $date * @return UnionWithTime */ - public static function date(DateTime $date): UnionWithTime { + public static function date(DateTime $date): UnionWithTime + { return new UnionWithTime([ 'type' => 'date', 'value' => $date, @@ -77,7 +79,8 @@ public static function date(DateTime $date): UnionWithTime { * @param DateTime $datetime * @return UnionWithTime */ - public static function datetime(DateTime $datetime): UnionWithTime { + public static function datetime(DateTime $datetime): UnionWithTime + { return new UnionWithTime([ 'type' => 'datetime', 'value' => $datetime, @@ -87,81 +90,89 @@ public static function datetime(DateTime $datetime): UnionWithTime { /** * @return bool */ - public function isValue(): bool { - return is_int($this->value)&& $this->type === 'value'; + public function isValue(): bool + { + return is_int($this->value) && $this->type === 'value'; } /** * @return int */ - public function asValue(): int { - if (!(is_int($this->value)&& $this->type === 'value')){ + public function asValue(): int + { + if (!(is_int($this->value) && $this->type === 'value')) { throw new Exception( "Expected value; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isDate(): bool { - return $this->value instanceof DateTime&& $this->type === 'date'; + public function isDate(): bool + { + return $this->value instanceof DateTime && $this->type === 'date'; } /** * @return DateTime */ - public function asDate(): DateTime { - if (!($this->value instanceof DateTime&& $this->type === 'date')){ + public function asDate(): DateTime + { + if (!($this->value instanceof DateTime && $this->type === 'date')) { throw new Exception( "Expected date; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isDatetime(): bool { - return $this->value instanceof DateTime&& $this->type === 'datetime'; + public function isDatetime(): bool + { + return $this->value instanceof DateTime && $this->type === 'datetime'; } /** * @return DateTime */ - public function asDatetime(): DateTime { - if (!($this->value instanceof DateTime&& $this->type === 'datetime')){ + public function asDatetime(): DateTime + { + if (!($this->value instanceof DateTime && $this->type === 'datetime')) { throw new Exception( "Expected datetime; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'value': $value = $this->value; $result['value'] = $value; @@ -176,26 +187,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -204,47 +216,48 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'value': - if (!array_key_exists('value', $data)){ + if (!array_key_exists('value', $data)) { throw new Exception( "JSON data is missing property 'value'", ); } - + $args['value'] = $data['value']; break; case 'date': - if (!array_key_exists('date', $data)){ + if (!array_key_exists('date', $data)) { throw new Exception( "JSON data is missing property 'date'", ); } - + $args['value'] = $data['date']; break; case 'datetime': - if (!array_key_exists('datetime', $data)){ + if (!array_key_exists('datetime', $data)) { throw new Exception( "JSON data is missing property 'datetime'", ); } - + $args['value'] = $data['datetime']; break; case '_unknown': @@ -252,7 +265,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/unions-with-local-date/src/Types/UnionWithoutKey.php b/seed/php-model/unions-with-local-date/src/Types/UnionWithoutKey.php index 798e78133766..c67ed338d10f 100644 --- a/seed/php-model/unions-with-local-date/src/Types/UnionWithoutKey.php +++ b/seed/php-model/unions-with-local-date/src/Types/UnionWithoutKey.php @@ -42,16 +42,17 @@ class UnionWithoutKey extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param Foo $foo * @return UnionWithoutKey */ - public static function foo(Foo $foo): UnionWithoutKey { + public static function foo(Foo $foo): UnionWithoutKey + { return new UnionWithoutKey([ 'type' => 'foo', 'value' => $foo, @@ -62,7 +63,8 @@ public static function foo(Foo $foo): UnionWithoutKey { * @param Bar $bar * @return UnionWithoutKey */ - public static function bar(Bar $bar): UnionWithoutKey { + public static function bar(Bar $bar): UnionWithoutKey + { return new UnionWithoutKey([ 'type' => 'bar', 'value' => $bar, @@ -72,61 +74,67 @@ public static function bar(Bar $bar): UnionWithoutKey { /** * @return bool */ - public function isFoo(): bool { - return $this->value instanceof Foo&& $this->type === 'foo'; + public function isFoo(): bool + { + return $this->value instanceof Foo && $this->type === 'foo'; } /** * @return Foo */ - public function asFoo(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo')){ + public function asFoo(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo')) { throw new Exception( "Expected foo; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isBar(): bool { - return $this->value instanceof Bar&& $this->type === 'bar'; + public function isBar(): bool + { + return $this->value instanceof Bar && $this->type === 'bar'; } /** * @return Bar */ - public function asBar(): Bar { - if (!($this->value instanceof Bar&& $this->type === 'bar')){ + public function asBar(): Bar + { + if (!($this->value instanceof Bar && $this->type === 'bar')) { throw new Exception( "Expected bar; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'foo': $value = $this->asFoo()->jsonSerialize(); $result = array_merge($value, $result); @@ -137,26 +145,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -165,22 +174,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'foo': $args['value'] = Foo::jsonDeserialize($data); break; @@ -192,7 +202,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/unions-with-local-date/src/Union/Circle.php b/seed/php-model/unions-with-local-date/src/Union/Circle.php index 257c64f20b7d..a361ed87c9b0 100644 --- a/seed/php-model/unions-with-local-date/src/Union/Circle.php +++ b/seed/php-model/unions-with-local-date/src/Union/Circle.php @@ -20,15 +20,15 @@ class Circle extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->radius = $values['radius']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions-with-local-date/src/Union/GetShapeRequest.php b/seed/php-model/unions-with-local-date/src/Union/GetShapeRequest.php index a84b148bd023..5da803399a3c 100644 --- a/seed/php-model/unions-with-local-date/src/Union/GetShapeRequest.php +++ b/seed/php-model/unions-with-local-date/src/Union/GetShapeRequest.php @@ -20,15 +20,15 @@ class GetShapeRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->id = $values['id']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions-with-local-date/src/Union/Shape.php b/seed/php-model/unions-with-local-date/src/Union/Shape.php index c369a6513c6e..2da9f99896d7 100644 --- a/seed/php-model/unions-with-local-date/src/Union/Shape.php +++ b/seed/php-model/unions-with-local-date/src/Union/Shape.php @@ -50,9 +50,10 @@ class Shape extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->id = $values['id'];$this->type = $values['type'];$this->value = $values['value']; + ) { + $this->id = $values['id']; + $this->type = $values['type']; + $this->value = $values['value']; } /** @@ -60,7 +61,8 @@ private function __construct( * @param Circle $circle * @return Shape */ - public static function circle(string $id, Circle $circle): Shape { + public static function circle(string $id, Circle $circle): Shape + { return new Shape([ 'id' => $id, 'type' => 'circle', @@ -73,7 +75,8 @@ public static function circle(string $id, Circle $circle): Shape { * @param Square $square * @return Shape */ - public static function square(string $id, Square $square): Shape { + public static function square(string $id, Square $square): Shape + { return new Shape([ 'id' => $id, 'type' => 'square', @@ -84,61 +87,67 @@ public static function square(string $id, Square $square): Shape { /** * @return bool */ - public function isCircle(): bool { - return $this->value instanceof Circle&& $this->type === 'circle'; + public function isCircle(): bool + { + return $this->value instanceof Circle && $this->type === 'circle'; } /** * @return Circle */ - public function asCircle(): Circle { - if (!($this->value instanceof Circle&& $this->type === 'circle')){ + public function asCircle(): Circle + { + if (!($this->value instanceof Circle && $this->type === 'circle')) { throw new Exception( "Expected circle; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isSquare(): bool { - return $this->value instanceof Square&& $this->type === 'square'; + public function isSquare(): bool + { + return $this->value instanceof Square && $this->type === 'square'; } /** * @return Square */ - public function asSquare(): Square { - if (!($this->value instanceof Square&& $this->type === 'square')){ + public function asSquare(): Square + { + if (!($this->value instanceof Square && $this->type === 'square')) { throw new Exception( "Expected square; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'circle': $value = $this->asCircle()->jsonSerialize(); $result = array_merge($value, $result); @@ -149,26 +158,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -177,34 +187,35 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('id', $data)){ + if (!array_key_exists('id', $data)) { throw new Exception( "JSON data is missing property 'id'", ); } - if (!(is_string($data['id']))){ + if (!(is_string($data['id']))) { throw new Exception( "Expected property 'id' in JSON data to be string, instead received " . get_debug_type($data['id']), ); } $args['id'] = $data['id']; - - if (!array_key_exists('type', $data)){ + + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'circle': $args['value'] = Circle::jsonDeserialize($data); break; @@ -216,7 +227,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/unions-with-local-date/src/Union/Square.php b/seed/php-model/unions-with-local-date/src/Union/Square.php index 5f4a6548abf5..c702fc27a457 100644 --- a/seed/php-model/unions-with-local-date/src/Union/Square.php +++ b/seed/php-model/unions-with-local-date/src/Union/Square.php @@ -20,15 +20,15 @@ class Square extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->length = $values['length']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions-with-local-date/src/Union/WithName.php b/seed/php-model/unions-with-local-date/src/Union/WithName.php index 8994d59ab9b1..533c12569c84 100644 --- a/seed/php-model/unions-with-local-date/src/Union/WithName.php +++ b/seed/php-model/unions-with-local-date/src/Union/WithName.php @@ -20,15 +20,15 @@ class WithName extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions-with-local-date/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/unions-with-local-date/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/unions-with-local-date/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/unions-with-local-date/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/unions-with-local-date/tests/Core/Json/EnumTest.php b/seed/php-model/unions-with-local-date/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/unions-with-local-date/tests/Core/Json/EnumTest.php +++ b/seed/php-model/unions-with-local-date/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/unions-with-local-date/tests/Core/Json/TraitTest.php b/seed/php-model/unions-with-local-date/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/unions-with-local-date/tests/Core/Json/TraitTest.php +++ b/seed/php-model/unions-with-local-date/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/unions-with-local-date/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/unions-with-local-date/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/unions-with-local-date/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/unions-with-local-date/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/unions/src/Bigunion/ActiveDiamond.php b/seed/php-model/unions/src/Bigunion/ActiveDiamond.php index 849ff840ba36..ec928b7c2df1 100644 --- a/seed/php-model/unions/src/Bigunion/ActiveDiamond.php +++ b/seed/php-model/unions/src/Bigunion/ActiveDiamond.php @@ -20,15 +20,15 @@ class ActiveDiamond extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions/src/Bigunion/AttractiveScript.php b/seed/php-model/unions/src/Bigunion/AttractiveScript.php index d478c56c6270..124ebbdd36b9 100644 --- a/seed/php-model/unions/src/Bigunion/AttractiveScript.php +++ b/seed/php-model/unions/src/Bigunion/AttractiveScript.php @@ -20,15 +20,15 @@ class AttractiveScript extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions/src/Bigunion/BigUnion.php b/seed/php-model/unions/src/Bigunion/BigUnion.php index 1d99cc4eb464..6feb1900942c 100644 --- a/seed/php-model/unions/src/Bigunion/BigUnion.php +++ b/seed/php-model/unions/src/Bigunion/BigUnion.php @@ -174,9 +174,12 @@ class BigUnion extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->id = $values['id'];$this->createdAt = $values['createdAt'];$this->archivedAt = $values['archivedAt'] ?? null;$this->type = $values['type'];$this->value = $values['value']; + ) { + $this->id = $values['id']; + $this->createdAt = $values['createdAt']; + $this->archivedAt = $values['archivedAt'] ?? null; + $this->type = $values['type']; + $this->value = $values['value']; } /** @@ -186,7 +189,8 @@ private function __construct( * @param NormalSweet $normalSweet * @return BigUnion */ - public static function normalSweet(string $id, DateTime $createdAt, NormalSweet $normalSweet, ?DateTime $archivedAt = null): BigUnion { + public static function normalSweet(string $id, DateTime $createdAt, NormalSweet $normalSweet, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -203,7 +207,8 @@ public static function normalSweet(string $id, DateTime $createdAt, NormalSweet * @param ThankfulFactor $thankfulFactor * @return BigUnion */ - public static function thankfulFactor(string $id, DateTime $createdAt, ThankfulFactor $thankfulFactor, ?DateTime $archivedAt = null): BigUnion { + public static function thankfulFactor(string $id, DateTime $createdAt, ThankfulFactor $thankfulFactor, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -220,7 +225,8 @@ public static function thankfulFactor(string $id, DateTime $createdAt, ThankfulF * @param JumboEnd $jumboEnd * @return BigUnion */ - public static function jumboEnd(string $id, DateTime $createdAt, JumboEnd $jumboEnd, ?DateTime $archivedAt = null): BigUnion { + public static function jumboEnd(string $id, DateTime $createdAt, JumboEnd $jumboEnd, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -237,7 +243,8 @@ public static function jumboEnd(string $id, DateTime $createdAt, JumboEnd $jumbo * @param HastyPain $hastyPain * @return BigUnion */ - public static function hastyPain(string $id, DateTime $createdAt, HastyPain $hastyPain, ?DateTime $archivedAt = null): BigUnion { + public static function hastyPain(string $id, DateTime $createdAt, HastyPain $hastyPain, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -254,7 +261,8 @@ public static function hastyPain(string $id, DateTime $createdAt, HastyPain $has * @param MistySnow $mistySnow * @return BigUnion */ - public static function mistySnow(string $id, DateTime $createdAt, MistySnow $mistySnow, ?DateTime $archivedAt = null): BigUnion { + public static function mistySnow(string $id, DateTime $createdAt, MistySnow $mistySnow, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -271,7 +279,8 @@ public static function mistySnow(string $id, DateTime $createdAt, MistySnow $mis * @param DistinctFailure $distinctFailure * @return BigUnion */ - public static function distinctFailure(string $id, DateTime $createdAt, DistinctFailure $distinctFailure, ?DateTime $archivedAt = null): BigUnion { + public static function distinctFailure(string $id, DateTime $createdAt, DistinctFailure $distinctFailure, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -288,7 +297,8 @@ public static function distinctFailure(string $id, DateTime $createdAt, Distinct * @param PracticalPrinciple $practicalPrinciple * @return BigUnion */ - public static function practicalPrinciple(string $id, DateTime $createdAt, PracticalPrinciple $practicalPrinciple, ?DateTime $archivedAt = null): BigUnion { + public static function practicalPrinciple(string $id, DateTime $createdAt, PracticalPrinciple $practicalPrinciple, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -305,7 +315,8 @@ public static function practicalPrinciple(string $id, DateTime $createdAt, Pract * @param LimpingStep $limpingStep * @return BigUnion */ - public static function limpingStep(string $id, DateTime $createdAt, LimpingStep $limpingStep, ?DateTime $archivedAt = null): BigUnion { + public static function limpingStep(string $id, DateTime $createdAt, LimpingStep $limpingStep, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -322,7 +333,8 @@ public static function limpingStep(string $id, DateTime $createdAt, LimpingStep * @param VibrantExcitement $vibrantExcitement * @return BigUnion */ - public static function vibrantExcitement(string $id, DateTime $createdAt, VibrantExcitement $vibrantExcitement, ?DateTime $archivedAt = null): BigUnion { + public static function vibrantExcitement(string $id, DateTime $createdAt, VibrantExcitement $vibrantExcitement, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -339,7 +351,8 @@ public static function vibrantExcitement(string $id, DateTime $createdAt, Vibran * @param ActiveDiamond $activeDiamond * @return BigUnion */ - public static function activeDiamond(string $id, DateTime $createdAt, ActiveDiamond $activeDiamond, ?DateTime $archivedAt = null): BigUnion { + public static function activeDiamond(string $id, DateTime $createdAt, ActiveDiamond $activeDiamond, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -356,7 +369,8 @@ public static function activeDiamond(string $id, DateTime $createdAt, ActiveDiam * @param PopularLimit $popularLimit * @return BigUnion */ - public static function popularLimit(string $id, DateTime $createdAt, PopularLimit $popularLimit, ?DateTime $archivedAt = null): BigUnion { + public static function popularLimit(string $id, DateTime $createdAt, PopularLimit $popularLimit, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -373,7 +387,8 @@ public static function popularLimit(string $id, DateTime $createdAt, PopularLimi * @param FalseMirror $falseMirror * @return BigUnion */ - public static function falseMirror(string $id, DateTime $createdAt, FalseMirror $falseMirror, ?DateTime $archivedAt = null): BigUnion { + public static function falseMirror(string $id, DateTime $createdAt, FalseMirror $falseMirror, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -390,7 +405,8 @@ public static function falseMirror(string $id, DateTime $createdAt, FalseMirror * @param PrimaryBlock $primaryBlock * @return BigUnion */ - public static function primaryBlock(string $id, DateTime $createdAt, PrimaryBlock $primaryBlock, ?DateTime $archivedAt = null): BigUnion { + public static function primaryBlock(string $id, DateTime $createdAt, PrimaryBlock $primaryBlock, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -407,7 +423,8 @@ public static function primaryBlock(string $id, DateTime $createdAt, PrimaryBloc * @param RotatingRatio $rotatingRatio * @return BigUnion */ - public static function rotatingRatio(string $id, DateTime $createdAt, RotatingRatio $rotatingRatio, ?DateTime $archivedAt = null): BigUnion { + public static function rotatingRatio(string $id, DateTime $createdAt, RotatingRatio $rotatingRatio, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -424,7 +441,8 @@ public static function rotatingRatio(string $id, DateTime $createdAt, RotatingRa * @param ColorfulCover $colorfulCover * @return BigUnion */ - public static function colorfulCover(string $id, DateTime $createdAt, ColorfulCover $colorfulCover, ?DateTime $archivedAt = null): BigUnion { + public static function colorfulCover(string $id, DateTime $createdAt, ColorfulCover $colorfulCover, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -441,7 +459,8 @@ public static function colorfulCover(string $id, DateTime $createdAt, ColorfulCo * @param DisloyalValue $disloyalValue * @return BigUnion */ - public static function disloyalValue(string $id, DateTime $createdAt, DisloyalValue $disloyalValue, ?DateTime $archivedAt = null): BigUnion { + public static function disloyalValue(string $id, DateTime $createdAt, DisloyalValue $disloyalValue, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -458,7 +477,8 @@ public static function disloyalValue(string $id, DateTime $createdAt, DisloyalVa * @param GruesomeCoach $gruesomeCoach * @return BigUnion */ - public static function gruesomeCoach(string $id, DateTime $createdAt, GruesomeCoach $gruesomeCoach, ?DateTime $archivedAt = null): BigUnion { + public static function gruesomeCoach(string $id, DateTime $createdAt, GruesomeCoach $gruesomeCoach, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -475,7 +495,8 @@ public static function gruesomeCoach(string $id, DateTime $createdAt, GruesomeCo * @param TotalWork $totalWork * @return BigUnion */ - public static function totalWork(string $id, DateTime $createdAt, TotalWork $totalWork, ?DateTime $archivedAt = null): BigUnion { + public static function totalWork(string $id, DateTime $createdAt, TotalWork $totalWork, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -492,7 +513,8 @@ public static function totalWork(string $id, DateTime $createdAt, TotalWork $tot * @param HarmoniousPlay $harmoniousPlay * @return BigUnion */ - public static function harmoniousPlay(string $id, DateTime $createdAt, HarmoniousPlay $harmoniousPlay, ?DateTime $archivedAt = null): BigUnion { + public static function harmoniousPlay(string $id, DateTime $createdAt, HarmoniousPlay $harmoniousPlay, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -509,7 +531,8 @@ public static function harmoniousPlay(string $id, DateTime $createdAt, Harmoniou * @param UniqueStress $uniqueStress * @return BigUnion */ - public static function uniqueStress(string $id, DateTime $createdAt, UniqueStress $uniqueStress, ?DateTime $archivedAt = null): BigUnion { + public static function uniqueStress(string $id, DateTime $createdAt, UniqueStress $uniqueStress, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -526,7 +549,8 @@ public static function uniqueStress(string $id, DateTime $createdAt, UniqueStres * @param UnwillingSmoke $unwillingSmoke * @return BigUnion */ - public static function unwillingSmoke(string $id, DateTime $createdAt, UnwillingSmoke $unwillingSmoke, ?DateTime $archivedAt = null): BigUnion { + public static function unwillingSmoke(string $id, DateTime $createdAt, UnwillingSmoke $unwillingSmoke, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -543,7 +567,8 @@ public static function unwillingSmoke(string $id, DateTime $createdAt, Unwilling * @param FrozenSleep $frozenSleep * @return BigUnion */ - public static function frozenSleep(string $id, DateTime $createdAt, FrozenSleep $frozenSleep, ?DateTime $archivedAt = null): BigUnion { + public static function frozenSleep(string $id, DateTime $createdAt, FrozenSleep $frozenSleep, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -560,7 +585,8 @@ public static function frozenSleep(string $id, DateTime $createdAt, FrozenSleep * @param DiligentDeal $diligentDeal * @return BigUnion */ - public static function diligentDeal(string $id, DateTime $createdAt, DiligentDeal $diligentDeal, ?DateTime $archivedAt = null): BigUnion { + public static function diligentDeal(string $id, DateTime $createdAt, DiligentDeal $diligentDeal, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -577,7 +603,8 @@ public static function diligentDeal(string $id, DateTime $createdAt, DiligentDea * @param AttractiveScript $attractiveScript * @return BigUnion */ - public static function attractiveScript(string $id, DateTime $createdAt, AttractiveScript $attractiveScript, ?DateTime $archivedAt = null): BigUnion { + public static function attractiveScript(string $id, DateTime $createdAt, AttractiveScript $attractiveScript, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -594,7 +621,8 @@ public static function attractiveScript(string $id, DateTime $createdAt, Attract * @param HoarseMouse $hoarseMouse * @return BigUnion */ - public static function hoarseMouse(string $id, DateTime $createdAt, HoarseMouse $hoarseMouse, ?DateTime $archivedAt = null): BigUnion { + public static function hoarseMouse(string $id, DateTime $createdAt, HoarseMouse $hoarseMouse, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -611,7 +639,8 @@ public static function hoarseMouse(string $id, DateTime $createdAt, HoarseMouse * @param CircularCard $circularCard * @return BigUnion */ - public static function circularCard(string $id, DateTime $createdAt, CircularCard $circularCard, ?DateTime $archivedAt = null): BigUnion { + public static function circularCard(string $id, DateTime $createdAt, CircularCard $circularCard, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -628,7 +657,8 @@ public static function circularCard(string $id, DateTime $createdAt, CircularCar * @param PotableBad $potableBad * @return BigUnion */ - public static function potableBad(string $id, DateTime $createdAt, PotableBad $potableBad, ?DateTime $archivedAt = null): BigUnion { + public static function potableBad(string $id, DateTime $createdAt, PotableBad $potableBad, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -645,7 +675,8 @@ public static function potableBad(string $id, DateTime $createdAt, PotableBad $p * @param TriangularRepair $triangularRepair * @return BigUnion */ - public static function triangularRepair(string $id, DateTime $createdAt, TriangularRepair $triangularRepair, ?DateTime $archivedAt = null): BigUnion { + public static function triangularRepair(string $id, DateTime $createdAt, TriangularRepair $triangularRepair, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -662,7 +693,8 @@ public static function triangularRepair(string $id, DateTime $createdAt, Triangu * @param GaseousRoad $gaseousRoad * @return BigUnion */ - public static function gaseousRoad(string $id, DateTime $createdAt, GaseousRoad $gaseousRoad, ?DateTime $archivedAt = null): BigUnion { + public static function gaseousRoad(string $id, DateTime $createdAt, GaseousRoad $gaseousRoad, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -675,601 +707,661 @@ public static function gaseousRoad(string $id, DateTime $createdAt, GaseousRoad /** * @return bool */ - public function isNormalSweet(): bool { - return $this->value instanceof NormalSweet&& $this->type === 'normalSweet'; + public function isNormalSweet(): bool + { + return $this->value instanceof NormalSweet && $this->type === 'normalSweet'; } /** * @return NormalSweet */ - public function asNormalSweet(): NormalSweet { - if (!($this->value instanceof NormalSweet&& $this->type === 'normalSweet')){ + public function asNormalSweet(): NormalSweet + { + if (!($this->value instanceof NormalSweet && $this->type === 'normalSweet')) { throw new Exception( "Expected normalSweet; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isThankfulFactor(): bool { - return $this->value instanceof ThankfulFactor&& $this->type === 'thankfulFactor'; + public function isThankfulFactor(): bool + { + return $this->value instanceof ThankfulFactor && $this->type === 'thankfulFactor'; } /** * @return ThankfulFactor */ - public function asThankfulFactor(): ThankfulFactor { - if (!($this->value instanceof ThankfulFactor&& $this->type === 'thankfulFactor')){ + public function asThankfulFactor(): ThankfulFactor + { + if (!($this->value instanceof ThankfulFactor && $this->type === 'thankfulFactor')) { throw new Exception( "Expected thankfulFactor; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isJumboEnd(): bool { - return $this->value instanceof JumboEnd&& $this->type === 'jumboEnd'; + public function isJumboEnd(): bool + { + return $this->value instanceof JumboEnd && $this->type === 'jumboEnd'; } /** * @return JumboEnd */ - public function asJumboEnd(): JumboEnd { - if (!($this->value instanceof JumboEnd&& $this->type === 'jumboEnd')){ + public function asJumboEnd(): JumboEnd + { + if (!($this->value instanceof JumboEnd && $this->type === 'jumboEnd')) { throw new Exception( "Expected jumboEnd; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isHastyPain(): bool { - return $this->value instanceof HastyPain&& $this->type === 'hastyPain'; + public function isHastyPain(): bool + { + return $this->value instanceof HastyPain && $this->type === 'hastyPain'; } /** * @return HastyPain */ - public function asHastyPain(): HastyPain { - if (!($this->value instanceof HastyPain&& $this->type === 'hastyPain')){ + public function asHastyPain(): HastyPain + { + if (!($this->value instanceof HastyPain && $this->type === 'hastyPain')) { throw new Exception( "Expected hastyPain; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isMistySnow(): bool { - return $this->value instanceof MistySnow&& $this->type === 'mistySnow'; + public function isMistySnow(): bool + { + return $this->value instanceof MistySnow && $this->type === 'mistySnow'; } /** * @return MistySnow */ - public function asMistySnow(): MistySnow { - if (!($this->value instanceof MistySnow&& $this->type === 'mistySnow')){ + public function asMistySnow(): MistySnow + { + if (!($this->value instanceof MistySnow && $this->type === 'mistySnow')) { throw new Exception( "Expected mistySnow; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isDistinctFailure(): bool { - return $this->value instanceof DistinctFailure&& $this->type === 'distinctFailure'; + public function isDistinctFailure(): bool + { + return $this->value instanceof DistinctFailure && $this->type === 'distinctFailure'; } /** * @return DistinctFailure */ - public function asDistinctFailure(): DistinctFailure { - if (!($this->value instanceof DistinctFailure&& $this->type === 'distinctFailure')){ + public function asDistinctFailure(): DistinctFailure + { + if (!($this->value instanceof DistinctFailure && $this->type === 'distinctFailure')) { throw new Exception( "Expected distinctFailure; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isPracticalPrinciple(): bool { - return $this->value instanceof PracticalPrinciple&& $this->type === 'practicalPrinciple'; + public function isPracticalPrinciple(): bool + { + return $this->value instanceof PracticalPrinciple && $this->type === 'practicalPrinciple'; } /** * @return PracticalPrinciple */ - public function asPracticalPrinciple(): PracticalPrinciple { - if (!($this->value instanceof PracticalPrinciple&& $this->type === 'practicalPrinciple')){ + public function asPracticalPrinciple(): PracticalPrinciple + { + if (!($this->value instanceof PracticalPrinciple && $this->type === 'practicalPrinciple')) { throw new Exception( "Expected practicalPrinciple; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isLimpingStep(): bool { - return $this->value instanceof LimpingStep&& $this->type === 'limpingStep'; + public function isLimpingStep(): bool + { + return $this->value instanceof LimpingStep && $this->type === 'limpingStep'; } /** * @return LimpingStep */ - public function asLimpingStep(): LimpingStep { - if (!($this->value instanceof LimpingStep&& $this->type === 'limpingStep')){ + public function asLimpingStep(): LimpingStep + { + if (!($this->value instanceof LimpingStep && $this->type === 'limpingStep')) { throw new Exception( "Expected limpingStep; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isVibrantExcitement(): bool { - return $this->value instanceof VibrantExcitement&& $this->type === 'vibrantExcitement'; + public function isVibrantExcitement(): bool + { + return $this->value instanceof VibrantExcitement && $this->type === 'vibrantExcitement'; } /** * @return VibrantExcitement */ - public function asVibrantExcitement(): VibrantExcitement { - if (!($this->value instanceof VibrantExcitement&& $this->type === 'vibrantExcitement')){ + public function asVibrantExcitement(): VibrantExcitement + { + if (!($this->value instanceof VibrantExcitement && $this->type === 'vibrantExcitement')) { throw new Exception( "Expected vibrantExcitement; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isActiveDiamond(): bool { - return $this->value instanceof ActiveDiamond&& $this->type === 'activeDiamond'; + public function isActiveDiamond(): bool + { + return $this->value instanceof ActiveDiamond && $this->type === 'activeDiamond'; } /** * @return ActiveDiamond */ - public function asActiveDiamond(): ActiveDiamond { - if (!($this->value instanceof ActiveDiamond&& $this->type === 'activeDiamond')){ + public function asActiveDiamond(): ActiveDiamond + { + if (!($this->value instanceof ActiveDiamond && $this->type === 'activeDiamond')) { throw new Exception( "Expected activeDiamond; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isPopularLimit(): bool { - return $this->value instanceof PopularLimit&& $this->type === 'popularLimit'; + public function isPopularLimit(): bool + { + return $this->value instanceof PopularLimit && $this->type === 'popularLimit'; } /** * @return PopularLimit */ - public function asPopularLimit(): PopularLimit { - if (!($this->value instanceof PopularLimit&& $this->type === 'popularLimit')){ + public function asPopularLimit(): PopularLimit + { + if (!($this->value instanceof PopularLimit && $this->type === 'popularLimit')) { throw new Exception( "Expected popularLimit; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isFalseMirror(): bool { - return $this->value instanceof FalseMirror&& $this->type === 'falseMirror'; + public function isFalseMirror(): bool + { + return $this->value instanceof FalseMirror && $this->type === 'falseMirror'; } /** * @return FalseMirror */ - public function asFalseMirror(): FalseMirror { - if (!($this->value instanceof FalseMirror&& $this->type === 'falseMirror')){ + public function asFalseMirror(): FalseMirror + { + if (!($this->value instanceof FalseMirror && $this->type === 'falseMirror')) { throw new Exception( "Expected falseMirror; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isPrimaryBlock(): bool { - return $this->value instanceof PrimaryBlock&& $this->type === 'primaryBlock'; + public function isPrimaryBlock(): bool + { + return $this->value instanceof PrimaryBlock && $this->type === 'primaryBlock'; } /** * @return PrimaryBlock */ - public function asPrimaryBlock(): PrimaryBlock { - if (!($this->value instanceof PrimaryBlock&& $this->type === 'primaryBlock')){ + public function asPrimaryBlock(): PrimaryBlock + { + if (!($this->value instanceof PrimaryBlock && $this->type === 'primaryBlock')) { throw new Exception( "Expected primaryBlock; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isRotatingRatio(): bool { - return $this->value instanceof RotatingRatio&& $this->type === 'rotatingRatio'; + public function isRotatingRatio(): bool + { + return $this->value instanceof RotatingRatio && $this->type === 'rotatingRatio'; } /** * @return RotatingRatio */ - public function asRotatingRatio(): RotatingRatio { - if (!($this->value instanceof RotatingRatio&& $this->type === 'rotatingRatio')){ + public function asRotatingRatio(): RotatingRatio + { + if (!($this->value instanceof RotatingRatio && $this->type === 'rotatingRatio')) { throw new Exception( "Expected rotatingRatio; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isColorfulCover(): bool { - return $this->value instanceof ColorfulCover&& $this->type === 'colorfulCover'; + public function isColorfulCover(): bool + { + return $this->value instanceof ColorfulCover && $this->type === 'colorfulCover'; } /** * @return ColorfulCover */ - public function asColorfulCover(): ColorfulCover { - if (!($this->value instanceof ColorfulCover&& $this->type === 'colorfulCover')){ + public function asColorfulCover(): ColorfulCover + { + if (!($this->value instanceof ColorfulCover && $this->type === 'colorfulCover')) { throw new Exception( "Expected colorfulCover; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isDisloyalValue(): bool { - return $this->value instanceof DisloyalValue&& $this->type === 'disloyalValue'; + public function isDisloyalValue(): bool + { + return $this->value instanceof DisloyalValue && $this->type === 'disloyalValue'; } /** * @return DisloyalValue */ - public function asDisloyalValue(): DisloyalValue { - if (!($this->value instanceof DisloyalValue&& $this->type === 'disloyalValue')){ + public function asDisloyalValue(): DisloyalValue + { + if (!($this->value instanceof DisloyalValue && $this->type === 'disloyalValue')) { throw new Exception( "Expected disloyalValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isGruesomeCoach(): bool { - return $this->value instanceof GruesomeCoach&& $this->type === 'gruesomeCoach'; + public function isGruesomeCoach(): bool + { + return $this->value instanceof GruesomeCoach && $this->type === 'gruesomeCoach'; } /** * @return GruesomeCoach */ - public function asGruesomeCoach(): GruesomeCoach { - if (!($this->value instanceof GruesomeCoach&& $this->type === 'gruesomeCoach')){ + public function asGruesomeCoach(): GruesomeCoach + { + if (!($this->value instanceof GruesomeCoach && $this->type === 'gruesomeCoach')) { throw new Exception( "Expected gruesomeCoach; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isTotalWork(): bool { - return $this->value instanceof TotalWork&& $this->type === 'totalWork'; + public function isTotalWork(): bool + { + return $this->value instanceof TotalWork && $this->type === 'totalWork'; } /** * @return TotalWork */ - public function asTotalWork(): TotalWork { - if (!($this->value instanceof TotalWork&& $this->type === 'totalWork')){ + public function asTotalWork(): TotalWork + { + if (!($this->value instanceof TotalWork && $this->type === 'totalWork')) { throw new Exception( "Expected totalWork; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isHarmoniousPlay(): bool { - return $this->value instanceof HarmoniousPlay&& $this->type === 'harmoniousPlay'; + public function isHarmoniousPlay(): bool + { + return $this->value instanceof HarmoniousPlay && $this->type === 'harmoniousPlay'; } /** * @return HarmoniousPlay */ - public function asHarmoniousPlay(): HarmoniousPlay { - if (!($this->value instanceof HarmoniousPlay&& $this->type === 'harmoniousPlay')){ + public function asHarmoniousPlay(): HarmoniousPlay + { + if (!($this->value instanceof HarmoniousPlay && $this->type === 'harmoniousPlay')) { throw new Exception( "Expected harmoniousPlay; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isUniqueStress(): bool { - return $this->value instanceof UniqueStress&& $this->type === 'uniqueStress'; + public function isUniqueStress(): bool + { + return $this->value instanceof UniqueStress && $this->type === 'uniqueStress'; } /** * @return UniqueStress */ - public function asUniqueStress(): UniqueStress { - if (!($this->value instanceof UniqueStress&& $this->type === 'uniqueStress')){ + public function asUniqueStress(): UniqueStress + { + if (!($this->value instanceof UniqueStress && $this->type === 'uniqueStress')) { throw new Exception( "Expected uniqueStress; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isUnwillingSmoke(): bool { - return $this->value instanceof UnwillingSmoke&& $this->type === 'unwillingSmoke'; + public function isUnwillingSmoke(): bool + { + return $this->value instanceof UnwillingSmoke && $this->type === 'unwillingSmoke'; } /** * @return UnwillingSmoke */ - public function asUnwillingSmoke(): UnwillingSmoke { - if (!($this->value instanceof UnwillingSmoke&& $this->type === 'unwillingSmoke')){ + public function asUnwillingSmoke(): UnwillingSmoke + { + if (!($this->value instanceof UnwillingSmoke && $this->type === 'unwillingSmoke')) { throw new Exception( "Expected unwillingSmoke; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isFrozenSleep(): bool { - return $this->value instanceof FrozenSleep&& $this->type === 'frozenSleep'; + public function isFrozenSleep(): bool + { + return $this->value instanceof FrozenSleep && $this->type === 'frozenSleep'; } /** * @return FrozenSleep */ - public function asFrozenSleep(): FrozenSleep { - if (!($this->value instanceof FrozenSleep&& $this->type === 'frozenSleep')){ + public function asFrozenSleep(): FrozenSleep + { + if (!($this->value instanceof FrozenSleep && $this->type === 'frozenSleep')) { throw new Exception( "Expected frozenSleep; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isDiligentDeal(): bool { - return $this->value instanceof DiligentDeal&& $this->type === 'diligentDeal'; + public function isDiligentDeal(): bool + { + return $this->value instanceof DiligentDeal && $this->type === 'diligentDeal'; } /** * @return DiligentDeal */ - public function asDiligentDeal(): DiligentDeal { - if (!($this->value instanceof DiligentDeal&& $this->type === 'diligentDeal')){ + public function asDiligentDeal(): DiligentDeal + { + if (!($this->value instanceof DiligentDeal && $this->type === 'diligentDeal')) { throw new Exception( "Expected diligentDeal; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isAttractiveScript(): bool { - return $this->value instanceof AttractiveScript&& $this->type === 'attractiveScript'; + public function isAttractiveScript(): bool + { + return $this->value instanceof AttractiveScript && $this->type === 'attractiveScript'; } /** * @return AttractiveScript */ - public function asAttractiveScript(): AttractiveScript { - if (!($this->value instanceof AttractiveScript&& $this->type === 'attractiveScript')){ + public function asAttractiveScript(): AttractiveScript + { + if (!($this->value instanceof AttractiveScript && $this->type === 'attractiveScript')) { throw new Exception( "Expected attractiveScript; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isHoarseMouse(): bool { - return $this->value instanceof HoarseMouse&& $this->type === 'hoarseMouse'; + public function isHoarseMouse(): bool + { + return $this->value instanceof HoarseMouse && $this->type === 'hoarseMouse'; } /** * @return HoarseMouse */ - public function asHoarseMouse(): HoarseMouse { - if (!($this->value instanceof HoarseMouse&& $this->type === 'hoarseMouse')){ + public function asHoarseMouse(): HoarseMouse + { + if (!($this->value instanceof HoarseMouse && $this->type === 'hoarseMouse')) { throw new Exception( "Expected hoarseMouse; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isCircularCard(): bool { - return $this->value instanceof CircularCard&& $this->type === 'circularCard'; + public function isCircularCard(): bool + { + return $this->value instanceof CircularCard && $this->type === 'circularCard'; } /** * @return CircularCard */ - public function asCircularCard(): CircularCard { - if (!($this->value instanceof CircularCard&& $this->type === 'circularCard')){ + public function asCircularCard(): CircularCard + { + if (!($this->value instanceof CircularCard && $this->type === 'circularCard')) { throw new Exception( "Expected circularCard; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isPotableBad(): bool { - return $this->value instanceof PotableBad&& $this->type === 'potableBad'; + public function isPotableBad(): bool + { + return $this->value instanceof PotableBad && $this->type === 'potableBad'; } /** * @return PotableBad */ - public function asPotableBad(): PotableBad { - if (!($this->value instanceof PotableBad&& $this->type === 'potableBad')){ + public function asPotableBad(): PotableBad + { + if (!($this->value instanceof PotableBad && $this->type === 'potableBad')) { throw new Exception( "Expected potableBad; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isTriangularRepair(): bool { - return $this->value instanceof TriangularRepair&& $this->type === 'triangularRepair'; + public function isTriangularRepair(): bool + { + return $this->value instanceof TriangularRepair && $this->type === 'triangularRepair'; } /** * @return TriangularRepair */ - public function asTriangularRepair(): TriangularRepair { - if (!($this->value instanceof TriangularRepair&& $this->type === 'triangularRepair')){ + public function asTriangularRepair(): TriangularRepair + { + if (!($this->value instanceof TriangularRepair && $this->type === 'triangularRepair')) { throw new Exception( "Expected triangularRepair; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isGaseousRoad(): bool { - return $this->value instanceof GaseousRoad&& $this->type === 'gaseousRoad'; + public function isGaseousRoad(): bool + { + return $this->value instanceof GaseousRoad && $this->type === 'gaseousRoad'; } /** * @return GaseousRoad */ - public function asGaseousRoad(): GaseousRoad { - if (!($this->value instanceof GaseousRoad&& $this->type === 'gaseousRoad')){ + public function asGaseousRoad(): GaseousRoad + { + if (!($this->value instanceof GaseousRoad && $this->type === 'gaseousRoad')) { throw new Exception( "Expected gaseousRoad; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'normalSweet': $value = $this->asNormalSweet()->jsonSerialize(); $result = array_merge($value, $result); @@ -1388,26 +1480,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -1416,58 +1509,59 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('id', $data)){ + if (!array_key_exists('id', $data)) { throw new Exception( "JSON data is missing property 'id'", ); } - if (!(is_string($data['id']))){ + if (!(is_string($data['id']))) { throw new Exception( "Expected property 'id' in JSON data to be string, instead received " . get_debug_type($data['id']), ); } $args['id'] = $data['id']; - - if (!array_key_exists('created-at', $data)){ + + if (!array_key_exists('created-at', $data)) { throw new Exception( "JSON data is missing property 'created-at'", ); } - if (!($data['created-at'] instanceof DateTime)){ + if (!($data['created-at'] instanceof DateTime)) { throw new Exception( "Expected property 'createdAt' in JSON data to be dateTime, instead received " . get_debug_type($data['created-at']), ); } $args['createdAt'] = $data['created-at']; - - if (!array_key_exists('archived-at', $data)){ + + if (!array_key_exists('archived-at', $data)) { throw new Exception( "JSON data is missing property 'archived-at'", ); } - if (!((is_null($data['archived-at']) || $data['archived-at'] instanceof DateTime))){ + if (!((is_null($data['archived-at']) || $data['archived-at'] instanceof DateTime))) { throw new Exception( "Expected property 'archivedAt' in JSON data to be optional, instead received " . get_debug_type($data['archived-at']), ); } $args['archivedAt'] = $data['archived-at']; - - if (!array_key_exists('type', $data)){ + + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'normalSweet': $args['value'] = NormalSweet::jsonDeserialize($data); break; @@ -1560,7 +1654,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/unions/src/Bigunion/CircularCard.php b/seed/php-model/unions/src/Bigunion/CircularCard.php index aa2d8a5f9c32..05419e03ee73 100644 --- a/seed/php-model/unions/src/Bigunion/CircularCard.php +++ b/seed/php-model/unions/src/Bigunion/CircularCard.php @@ -20,15 +20,15 @@ class CircularCard extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions/src/Bigunion/ColorfulCover.php b/seed/php-model/unions/src/Bigunion/ColorfulCover.php index a70136a77e37..81cecaa1dcc1 100644 --- a/seed/php-model/unions/src/Bigunion/ColorfulCover.php +++ b/seed/php-model/unions/src/Bigunion/ColorfulCover.php @@ -20,15 +20,15 @@ class ColorfulCover extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions/src/Bigunion/DiligentDeal.php b/seed/php-model/unions/src/Bigunion/DiligentDeal.php index a5cc5c3ca749..00dc65e89aef 100644 --- a/seed/php-model/unions/src/Bigunion/DiligentDeal.php +++ b/seed/php-model/unions/src/Bigunion/DiligentDeal.php @@ -20,15 +20,15 @@ class DiligentDeal extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions/src/Bigunion/DisloyalValue.php b/seed/php-model/unions/src/Bigunion/DisloyalValue.php index a762ff9a03d3..41115687f356 100644 --- a/seed/php-model/unions/src/Bigunion/DisloyalValue.php +++ b/seed/php-model/unions/src/Bigunion/DisloyalValue.php @@ -20,15 +20,15 @@ class DisloyalValue extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions/src/Bigunion/DistinctFailure.php b/seed/php-model/unions/src/Bigunion/DistinctFailure.php index c9951791e1fd..73121cfbfc56 100644 --- a/seed/php-model/unions/src/Bigunion/DistinctFailure.php +++ b/seed/php-model/unions/src/Bigunion/DistinctFailure.php @@ -20,15 +20,15 @@ class DistinctFailure extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions/src/Bigunion/FalseMirror.php b/seed/php-model/unions/src/Bigunion/FalseMirror.php index 300b44753bc3..8d9adc2f7ae0 100644 --- a/seed/php-model/unions/src/Bigunion/FalseMirror.php +++ b/seed/php-model/unions/src/Bigunion/FalseMirror.php @@ -20,15 +20,15 @@ class FalseMirror extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions/src/Bigunion/FrozenSleep.php b/seed/php-model/unions/src/Bigunion/FrozenSleep.php index e7baf168f074..63358e39e74e 100644 --- a/seed/php-model/unions/src/Bigunion/FrozenSleep.php +++ b/seed/php-model/unions/src/Bigunion/FrozenSleep.php @@ -20,15 +20,15 @@ class FrozenSleep extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions/src/Bigunion/GaseousRoad.php b/seed/php-model/unions/src/Bigunion/GaseousRoad.php index e39942463a52..65494e20a7b4 100644 --- a/seed/php-model/unions/src/Bigunion/GaseousRoad.php +++ b/seed/php-model/unions/src/Bigunion/GaseousRoad.php @@ -20,15 +20,15 @@ class GaseousRoad extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions/src/Bigunion/GruesomeCoach.php b/seed/php-model/unions/src/Bigunion/GruesomeCoach.php index 1b9c77a76680..380ceef20774 100644 --- a/seed/php-model/unions/src/Bigunion/GruesomeCoach.php +++ b/seed/php-model/unions/src/Bigunion/GruesomeCoach.php @@ -20,15 +20,15 @@ class GruesomeCoach extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions/src/Bigunion/HarmoniousPlay.php b/seed/php-model/unions/src/Bigunion/HarmoniousPlay.php index 20645361043b..717249a8bf7d 100644 --- a/seed/php-model/unions/src/Bigunion/HarmoniousPlay.php +++ b/seed/php-model/unions/src/Bigunion/HarmoniousPlay.php @@ -20,15 +20,15 @@ class HarmoniousPlay extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions/src/Bigunion/HastyPain.php b/seed/php-model/unions/src/Bigunion/HastyPain.php index 655a809fc519..c36c50c2adc7 100644 --- a/seed/php-model/unions/src/Bigunion/HastyPain.php +++ b/seed/php-model/unions/src/Bigunion/HastyPain.php @@ -20,15 +20,15 @@ class HastyPain extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions/src/Bigunion/HoarseMouse.php b/seed/php-model/unions/src/Bigunion/HoarseMouse.php index 18a78ee072c1..6f1437628757 100644 --- a/seed/php-model/unions/src/Bigunion/HoarseMouse.php +++ b/seed/php-model/unions/src/Bigunion/HoarseMouse.php @@ -20,15 +20,15 @@ class HoarseMouse extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions/src/Bigunion/JumboEnd.php b/seed/php-model/unions/src/Bigunion/JumboEnd.php index c46acf1fd124..97d2c3ea6f6d 100644 --- a/seed/php-model/unions/src/Bigunion/JumboEnd.php +++ b/seed/php-model/unions/src/Bigunion/JumboEnd.php @@ -20,15 +20,15 @@ class JumboEnd extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions/src/Bigunion/LimpingStep.php b/seed/php-model/unions/src/Bigunion/LimpingStep.php index a18506df5631..2216809108d3 100644 --- a/seed/php-model/unions/src/Bigunion/LimpingStep.php +++ b/seed/php-model/unions/src/Bigunion/LimpingStep.php @@ -20,15 +20,15 @@ class LimpingStep extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions/src/Bigunion/MistySnow.php b/seed/php-model/unions/src/Bigunion/MistySnow.php index 819bc1c66ffa..c2054320774d 100644 --- a/seed/php-model/unions/src/Bigunion/MistySnow.php +++ b/seed/php-model/unions/src/Bigunion/MistySnow.php @@ -20,15 +20,15 @@ class MistySnow extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions/src/Bigunion/NormalSweet.php b/seed/php-model/unions/src/Bigunion/NormalSweet.php index 01103ea30aab..f75bc941042c 100644 --- a/seed/php-model/unions/src/Bigunion/NormalSweet.php +++ b/seed/php-model/unions/src/Bigunion/NormalSweet.php @@ -20,15 +20,15 @@ class NormalSweet extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions/src/Bigunion/PopularLimit.php b/seed/php-model/unions/src/Bigunion/PopularLimit.php index 407fcce985d2..291106ea2974 100644 --- a/seed/php-model/unions/src/Bigunion/PopularLimit.php +++ b/seed/php-model/unions/src/Bigunion/PopularLimit.php @@ -20,15 +20,15 @@ class PopularLimit extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions/src/Bigunion/PotableBad.php b/seed/php-model/unions/src/Bigunion/PotableBad.php index 3c9cfb79e28a..2f3e11c76129 100644 --- a/seed/php-model/unions/src/Bigunion/PotableBad.php +++ b/seed/php-model/unions/src/Bigunion/PotableBad.php @@ -20,15 +20,15 @@ class PotableBad extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions/src/Bigunion/PracticalPrinciple.php b/seed/php-model/unions/src/Bigunion/PracticalPrinciple.php index 68986330908a..7a77f2a2b39d 100644 --- a/seed/php-model/unions/src/Bigunion/PracticalPrinciple.php +++ b/seed/php-model/unions/src/Bigunion/PracticalPrinciple.php @@ -20,15 +20,15 @@ class PracticalPrinciple extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions/src/Bigunion/PrimaryBlock.php b/seed/php-model/unions/src/Bigunion/PrimaryBlock.php index dda5e4cb2c08..5fbbac1b9857 100644 --- a/seed/php-model/unions/src/Bigunion/PrimaryBlock.php +++ b/seed/php-model/unions/src/Bigunion/PrimaryBlock.php @@ -20,15 +20,15 @@ class PrimaryBlock extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions/src/Bigunion/RotatingRatio.php b/seed/php-model/unions/src/Bigunion/RotatingRatio.php index 4faa41ffac34..cdada5a83a7f 100644 --- a/seed/php-model/unions/src/Bigunion/RotatingRatio.php +++ b/seed/php-model/unions/src/Bigunion/RotatingRatio.php @@ -20,15 +20,15 @@ class RotatingRatio extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions/src/Bigunion/ThankfulFactor.php b/seed/php-model/unions/src/Bigunion/ThankfulFactor.php index 031b30ab1b29..3d02a7d46dfb 100644 --- a/seed/php-model/unions/src/Bigunion/ThankfulFactor.php +++ b/seed/php-model/unions/src/Bigunion/ThankfulFactor.php @@ -20,15 +20,15 @@ class ThankfulFactor extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions/src/Bigunion/TotalWork.php b/seed/php-model/unions/src/Bigunion/TotalWork.php index 8f45e3af61e2..3708e211d0dd 100644 --- a/seed/php-model/unions/src/Bigunion/TotalWork.php +++ b/seed/php-model/unions/src/Bigunion/TotalWork.php @@ -20,15 +20,15 @@ class TotalWork extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions/src/Bigunion/TriangularRepair.php b/seed/php-model/unions/src/Bigunion/TriangularRepair.php index 724993cbbfc7..3405c1fdfe9d 100644 --- a/seed/php-model/unions/src/Bigunion/TriangularRepair.php +++ b/seed/php-model/unions/src/Bigunion/TriangularRepair.php @@ -20,15 +20,15 @@ class TriangularRepair extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions/src/Bigunion/UniqueStress.php b/seed/php-model/unions/src/Bigunion/UniqueStress.php index ad4fd69806ed..4a320ba473ca 100644 --- a/seed/php-model/unions/src/Bigunion/UniqueStress.php +++ b/seed/php-model/unions/src/Bigunion/UniqueStress.php @@ -20,15 +20,15 @@ class UniqueStress extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions/src/Bigunion/UnwillingSmoke.php b/seed/php-model/unions/src/Bigunion/UnwillingSmoke.php index 7f7549b7a04e..bb3e6ba1dc3a 100644 --- a/seed/php-model/unions/src/Bigunion/UnwillingSmoke.php +++ b/seed/php-model/unions/src/Bigunion/UnwillingSmoke.php @@ -20,15 +20,15 @@ class UnwillingSmoke extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions/src/Bigunion/VibrantExcitement.php b/seed/php-model/unions/src/Bigunion/VibrantExcitement.php index 373dadf5b85c..177cf8717704 100644 --- a/seed/php-model/unions/src/Bigunion/VibrantExcitement.php +++ b/seed/php-model/unions/src/Bigunion/VibrantExcitement.php @@ -20,15 +20,15 @@ class VibrantExcitement extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions/src/Core/Json/JsonEncoder.php b/seed/php-model/unions/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/unions/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/unions/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/unions/src/Core/Json/JsonProperty.php b/seed/php-model/unions/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/unions/src/Core/Json/JsonProperty.php +++ b/seed/php-model/unions/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/unions/src/Core/Types/ArrayType.php b/seed/php-model/unions/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/unions/src/Core/Types/ArrayType.php +++ b/seed/php-model/unions/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/unions/src/Core/Types/Constant.php b/seed/php-model/unions/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/unions/src/Core/Types/Constant.php +++ b/seed/php-model/unions/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/unions/src/Core/Types/Union.php b/seed/php-model/unions/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/unions/src/Core/Types/Union.php +++ b/seed/php-model/unions/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/unions/src/Types/Bar.php b/seed/php-model/unions/src/Types/Bar.php index 9be1a872a923..735bd90eee29 100644 --- a/seed/php-model/unions/src/Types/Bar.php +++ b/seed/php-model/unions/src/Types/Bar.php @@ -20,15 +20,15 @@ class Bar extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions/src/Types/FirstItemType.php b/seed/php-model/unions/src/Types/FirstItemType.php index 872d82073530..8404bf6f4ce0 100644 --- a/seed/php-model/unions/src/Types/FirstItemType.php +++ b/seed/php-model/unions/src/Types/FirstItemType.php @@ -27,15 +27,16 @@ class FirstItemType extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->type = $values['type'] ?? null;$this->name = $values['name']; + ) { + $this->type = $values['type'] ?? null; + $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions/src/Types/Foo.php b/seed/php-model/unions/src/Types/Foo.php index 24d2085e66c4..7933294edd39 100644 --- a/seed/php-model/unions/src/Types/Foo.php +++ b/seed/php-model/unions/src/Types/Foo.php @@ -20,15 +20,15 @@ class Foo extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions/src/Types/FooExtended.php b/seed/php-model/unions/src/Types/FooExtended.php index 74cfb9266d33..7ae0fffcf0e7 100644 --- a/seed/php-model/unions/src/Types/FooExtended.php +++ b/seed/php-model/unions/src/Types/FooExtended.php @@ -24,15 +24,16 @@ class FooExtended extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->age = $values['age']; + ) { + $this->name = $values['name']; + $this->age = $values['age']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions/src/Types/SecondItemType.php b/seed/php-model/unions/src/Types/SecondItemType.php index a385d79a3b0c..ea73a0d95338 100644 --- a/seed/php-model/unions/src/Types/SecondItemType.php +++ b/seed/php-model/unions/src/Types/SecondItemType.php @@ -27,15 +27,16 @@ class SecondItemType extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->type = $values['type'] ?? null;$this->title = $values['title']; + ) { + $this->type = $values['type'] ?? null; + $this->title = $values['title']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions/src/Types/Traits/Foo.php b/seed/php-model/unions/src/Types/Traits/Foo.php index 901f6d1cc180..7b400d71eacd 100644 --- a/seed/php-model/unions/src/Types/Traits/Foo.php +++ b/seed/php-model/unions/src/Types/Traits/Foo.php @@ -7,7 +7,7 @@ /** * @property string $name */ -trait Foo +trait Foo { /** * @var string $name diff --git a/seed/php-model/unions/src/Types/Union.php b/seed/php-model/unions/src/Types/Union.php index c45f30b8de39..189c346bf405 100644 --- a/seed/php-model/unions/src/Types/Union.php +++ b/seed/php-model/unions/src/Types/Union.php @@ -45,16 +45,17 @@ class Union extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param Foo $foo * @return Union */ - public static function foo(Foo $foo): Union { + public static function foo(Foo $foo): Union + { return new Union([ 'type' => 'foo', 'value' => $foo, @@ -65,7 +66,8 @@ public static function foo(Foo $foo): Union { * @param Bar $bar * @return Union */ - public static function bar(Bar $bar): Union { + public static function bar(Bar $bar): Union + { return new Union([ 'type' => 'bar', 'value' => $bar, @@ -75,61 +77,67 @@ public static function bar(Bar $bar): Union { /** * @return bool */ - public function isFoo(): bool { - return $this->value instanceof Foo&& $this->type === 'foo'; + public function isFoo(): bool + { + return $this->value instanceof Foo && $this->type === 'foo'; } /** * @return Foo */ - public function asFoo(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo')){ + public function asFoo(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo')) { throw new Exception( "Expected foo; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isBar(): bool { - return $this->value instanceof Bar&& $this->type === 'bar'; + public function isBar(): bool + { + return $this->value instanceof Bar && $this->type === 'bar'; } /** * @return Bar */ - public function asBar(): Bar { - if (!($this->value instanceof Bar&& $this->type === 'bar')){ + public function asBar(): Bar + { + if (!($this->value instanceof Bar && $this->type === 'bar')) { throw new Exception( "Expected bar; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'foo': $value = $this->asFoo()->jsonSerialize(); $result['foo'] = $value; @@ -140,26 +148,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -168,30 +177,31 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'foo': - if (!array_key_exists('foo', $data)){ + if (!array_key_exists('foo', $data)) { throw new Exception( "JSON data is missing property 'foo'", ); } - - if (!(is_array($data['foo']))){ + + if (!(is_array($data['foo']))) { throw new Exception( "Expected property 'foo' in JSON data to be array, instead received " . get_debug_type($data['foo']), ); @@ -199,13 +209,13 @@ public static function jsonDeserialize(array $data): static { $args['value'] = Foo::jsonDeserialize($data['foo']); break; case 'bar': - if (!array_key_exists('bar', $data)){ + if (!array_key_exists('bar', $data)) { throw new Exception( "JSON data is missing property 'bar'", ); } - - if (!(is_array($data['bar']))){ + + if (!(is_array($data['bar']))) { throw new Exception( "Expected property 'bar' in JSON data to be array, instead received " . get_debug_type($data['bar']), ); @@ -217,7 +227,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/unions/src/Types/UnionWithBaseProperties.php b/seed/php-model/unions/src/Types/UnionWithBaseProperties.php index 2cd29a393056..d153f3734293 100644 --- a/seed/php-model/unions/src/Types/UnionWithBaseProperties.php +++ b/seed/php-model/unions/src/Types/UnionWithBaseProperties.php @@ -54,9 +54,10 @@ class UnionWithBaseProperties extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->id = $values['id'];$this->type = $values['type'];$this->value = $values['value']; + ) { + $this->id = $values['id']; + $this->type = $values['type']; + $this->value = $values['value']; } /** @@ -64,7 +65,8 @@ private function __construct( * @param int $integer * @return UnionWithBaseProperties */ - public static function integer(string $id, int $integer): UnionWithBaseProperties { + public static function integer(string $id, int $integer): UnionWithBaseProperties + { return new UnionWithBaseProperties([ 'id' => $id, 'type' => 'integer', @@ -77,7 +79,8 @@ public static function integer(string $id, int $integer): UnionWithBasePropertie * @param string $string * @return UnionWithBaseProperties */ - public static function string(string $id, string $string): UnionWithBaseProperties { + public static function string(string $id, string $string): UnionWithBaseProperties + { return new UnionWithBaseProperties([ 'id' => $id, 'type' => 'string', @@ -90,7 +93,8 @@ public static function string(string $id, string $string): UnionWithBaseProperti * @param Foo $foo * @return UnionWithBaseProperties */ - public static function foo(string $id, Foo $foo): UnionWithBaseProperties { + public static function foo(string $id, Foo $foo): UnionWithBaseProperties + { return new UnionWithBaseProperties([ 'id' => $id, 'type' => 'foo', @@ -101,81 +105,89 @@ public static function foo(string $id, Foo $foo): UnionWithBaseProperties { /** * @return bool */ - public function isInteger(): bool { - return is_int($this->value)&& $this->type === 'integer'; + public function isInteger(): bool + { + return is_int($this->value) && $this->type === 'integer'; } /** * @return int */ - public function asInteger(): int { - if (!(is_int($this->value)&& $this->type === 'integer')){ + public function asInteger(): int + { + if (!(is_int($this->value) && $this->type === 'integer')) { throw new Exception( "Expected integer; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isString(): bool { - return is_string($this->value)&& $this->type === 'string'; + public function isString(): bool + { + return is_string($this->value) && $this->type === 'string'; } /** * @return string */ - public function asString(): string { - if (!(is_string($this->value)&& $this->type === 'string')){ + public function asString(): string + { + if (!(is_string($this->value) && $this->type === 'string')) { throw new Exception( "Expected string; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isFoo(): bool { - return $this->value instanceof Foo&& $this->type === 'foo'; + public function isFoo(): bool + { + return $this->value instanceof Foo && $this->type === 'foo'; } /** * @return Foo */ - public function asFoo(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo')){ + public function asFoo(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo')) { throw new Exception( "Expected foo; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'integer': $value = $this->value; $result['integer'] = $value; @@ -190,26 +202,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -218,50 +231,51 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('id', $data)){ + if (!array_key_exists('id', $data)) { throw new Exception( "JSON data is missing property 'id'", ); } - if (!(is_string($data['id']))){ + if (!(is_string($data['id']))) { throw new Exception( "Expected property 'id' in JSON data to be string, instead received " . get_debug_type($data['id']), ); } $args['id'] = $data['id']; - - if (!array_key_exists('type', $data)){ + + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'integer': - if (!array_key_exists('integer', $data)){ + if (!array_key_exists('integer', $data)) { throw new Exception( "JSON data is missing property 'integer'", ); } - + $args['value'] = $data['integer']; break; case 'string': - if (!array_key_exists('string', $data)){ + if (!array_key_exists('string', $data)) { throw new Exception( "JSON data is missing property 'string'", ); } - + $args['value'] = $data['string']; break; case 'foo': @@ -272,7 +286,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/unions/src/Types/UnionWithDiscriminant.php b/seed/php-model/unions/src/Types/UnionWithDiscriminant.php index 9df4071fb159..c1c8317343c7 100644 --- a/seed/php-model/unions/src/Types/UnionWithDiscriminant.php +++ b/seed/php-model/unions/src/Types/UnionWithDiscriminant.php @@ -42,16 +42,17 @@ class UnionWithDiscriminant extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param Foo $foo * @return UnionWithDiscriminant */ - public static function foo(Foo $foo): UnionWithDiscriminant { + public static function foo(Foo $foo): UnionWithDiscriminant + { return new UnionWithDiscriminant([ 'type' => 'foo', 'value' => $foo, @@ -62,7 +63,8 @@ public static function foo(Foo $foo): UnionWithDiscriminant { * @param Bar $bar * @return UnionWithDiscriminant */ - public static function bar(Bar $bar): UnionWithDiscriminant { + public static function bar(Bar $bar): UnionWithDiscriminant + { return new UnionWithDiscriminant([ 'type' => 'bar', 'value' => $bar, @@ -72,61 +74,67 @@ public static function bar(Bar $bar): UnionWithDiscriminant { /** * @return bool */ - public function isFoo(): bool { - return $this->value instanceof Foo&& $this->type === 'foo'; + public function isFoo(): bool + { + return $this->value instanceof Foo && $this->type === 'foo'; } /** * @return Foo */ - public function asFoo(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo')){ + public function asFoo(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo')) { throw new Exception( "Expected foo; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isBar(): bool { - return $this->value instanceof Bar&& $this->type === 'bar'; + public function isBar(): bool + { + return $this->value instanceof Bar && $this->type === 'bar'; } /** * @return Bar */ - public function asBar(): Bar { - if (!($this->value instanceof Bar&& $this->type === 'bar')){ + public function asBar(): Bar + { + if (!($this->value instanceof Bar && $this->type === 'bar')) { throw new Exception( "Expected bar; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['_type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'foo': $value = $this->asFoo()->jsonSerialize(); $result['foo'] = $value; @@ -137,26 +145,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -165,30 +174,31 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('_type', $data)){ + if (!array_key_exists('_type', $data)) { throw new Exception( "JSON data is missing property '_type'", ); } $type = $data['_type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['_type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'foo': - if (!array_key_exists('foo', $data)){ + if (!array_key_exists('foo', $data)) { throw new Exception( "JSON data is missing property 'foo'", ); } - - if (!(is_array($data['foo']))){ + + if (!(is_array($data['foo']))) { throw new Exception( "Expected property 'foo' in JSON data to be array, instead received " . get_debug_type($data['foo']), ); @@ -196,13 +206,13 @@ public static function jsonDeserialize(array $data): static { $args['value'] = Foo::jsonDeserialize($data['foo']); break; case 'bar': - if (!array_key_exists('bar', $data)){ + if (!array_key_exists('bar', $data)) { throw new Exception( "JSON data is missing property 'bar'", ); } - - if (!(is_array($data['bar']))){ + + if (!(is_array($data['bar']))) { throw new Exception( "Expected property 'bar' in JSON data to be array, instead received " . get_debug_type($data['bar']), ); @@ -214,7 +224,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/unions/src/Types/UnionWithDuplicatePrimitive.php b/seed/php-model/unions/src/Types/UnionWithDuplicatePrimitive.php index 9fd6e32c0fdc..1ef734b3bc47 100644 --- a/seed/php-model/unions/src/Types/UnionWithDuplicatePrimitive.php +++ b/seed/php-model/unions/src/Types/UnionWithDuplicatePrimitive.php @@ -46,16 +46,17 @@ class UnionWithDuplicatePrimitive extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param int $integer1 * @return UnionWithDuplicatePrimitive */ - public static function integer1(int $integer1): UnionWithDuplicatePrimitive { + public static function integer1(int $integer1): UnionWithDuplicatePrimitive + { return new UnionWithDuplicatePrimitive([ 'type' => 'integer1', 'value' => $integer1, @@ -66,7 +67,8 @@ public static function integer1(int $integer1): UnionWithDuplicatePrimitive { * @param int $integer2 * @return UnionWithDuplicatePrimitive */ - public static function integer2(int $integer2): UnionWithDuplicatePrimitive { + public static function integer2(int $integer2): UnionWithDuplicatePrimitive + { return new UnionWithDuplicatePrimitive([ 'type' => 'integer2', 'value' => $integer2, @@ -77,7 +79,8 @@ public static function integer2(int $integer2): UnionWithDuplicatePrimitive { * @param string $string1 * @return UnionWithDuplicatePrimitive */ - public static function string1(string $string1): UnionWithDuplicatePrimitive { + public static function string1(string $string1): UnionWithDuplicatePrimitive + { return new UnionWithDuplicatePrimitive([ 'type' => 'string1', 'value' => $string1, @@ -88,7 +91,8 @@ public static function string1(string $string1): UnionWithDuplicatePrimitive { * @param string $string2 * @return UnionWithDuplicatePrimitive */ - public static function string2(string $string2): UnionWithDuplicatePrimitive { + public static function string2(string $string2): UnionWithDuplicatePrimitive + { return new UnionWithDuplicatePrimitive([ 'type' => 'string2', 'value' => $string2, @@ -98,101 +102,111 @@ public static function string2(string $string2): UnionWithDuplicatePrimitive { /** * @return bool */ - public function isInteger1(): bool { - return is_int($this->value)&& $this->type === 'integer1'; + public function isInteger1(): bool + { + return is_int($this->value) && $this->type === 'integer1'; } /** * @return int */ - public function asInteger1(): int { - if (!(is_int($this->value)&& $this->type === 'integer1')){ + public function asInteger1(): int + { + if (!(is_int($this->value) && $this->type === 'integer1')) { throw new Exception( "Expected integer1; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isInteger2(): bool { - return is_int($this->value)&& $this->type === 'integer2'; + public function isInteger2(): bool + { + return is_int($this->value) && $this->type === 'integer2'; } /** * @return int */ - public function asInteger2(): int { - if (!(is_int($this->value)&& $this->type === 'integer2')){ + public function asInteger2(): int + { + if (!(is_int($this->value) && $this->type === 'integer2')) { throw new Exception( "Expected integer2; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isString1(): bool { - return is_string($this->value)&& $this->type === 'string1'; + public function isString1(): bool + { + return is_string($this->value) && $this->type === 'string1'; } /** * @return string */ - public function asString1(): string { - if (!(is_string($this->value)&& $this->type === 'string1')){ + public function asString1(): string + { + if (!(is_string($this->value) && $this->type === 'string1')) { throw new Exception( "Expected string1; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isString2(): bool { - return is_string($this->value)&& $this->type === 'string2'; + public function isString2(): bool + { + return is_string($this->value) && $this->type === 'string2'; } /** * @return string */ - public function asString2(): string { - if (!(is_string($this->value)&& $this->type === 'string2')){ + public function asString2(): string + { + if (!(is_string($this->value) && $this->type === 'string2')) { throw new Exception( "Expected string2; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'integer1': $value = $this->value; $result['integer1'] = $value; @@ -211,26 +225,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -239,56 +254,57 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'integer1': - if (!array_key_exists('integer1', $data)){ + if (!array_key_exists('integer1', $data)) { throw new Exception( "JSON data is missing property 'integer1'", ); } - + $args['value'] = $data['integer1']; break; case 'integer2': - if (!array_key_exists('integer2', $data)){ + if (!array_key_exists('integer2', $data)) { throw new Exception( "JSON data is missing property 'integer2'", ); } - + $args['value'] = $data['integer2']; break; case 'string1': - if (!array_key_exists('string1', $data)){ + if (!array_key_exists('string1', $data)) { throw new Exception( "JSON data is missing property 'string1'", ); } - + $args['value'] = $data['string1']; break; case 'string2': - if (!array_key_exists('string2', $data)){ + if (!array_key_exists('string2', $data)) { throw new Exception( "JSON data is missing property 'string2'", ); } - + $args['value'] = $data['string2']; break; case '_unknown': @@ -296,7 +312,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/unions/src/Types/UnionWithDuplicateTypes.php b/seed/php-model/unions/src/Types/UnionWithDuplicateTypes.php index 316ae9373aab..e99748222593 100644 --- a/seed/php-model/unions/src/Types/UnionWithDuplicateTypes.php +++ b/seed/php-model/unions/src/Types/UnionWithDuplicateTypes.php @@ -40,16 +40,17 @@ class UnionWithDuplicateTypes extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param Foo $foo1 * @return UnionWithDuplicateTypes */ - public static function foo1(Foo $foo1): UnionWithDuplicateTypes { + public static function foo1(Foo $foo1): UnionWithDuplicateTypes + { return new UnionWithDuplicateTypes([ 'type' => 'foo1', 'value' => $foo1, @@ -60,7 +61,8 @@ public static function foo1(Foo $foo1): UnionWithDuplicateTypes { * @param Foo $foo2 * @return UnionWithDuplicateTypes */ - public static function foo2(Foo $foo2): UnionWithDuplicateTypes { + public static function foo2(Foo $foo2): UnionWithDuplicateTypes + { return new UnionWithDuplicateTypes([ 'type' => 'foo2', 'value' => $foo2, @@ -70,61 +72,67 @@ public static function foo2(Foo $foo2): UnionWithDuplicateTypes { /** * @return bool */ - public function isFoo1(): bool { - return $this->value instanceof Foo&& $this->type === 'foo1'; + public function isFoo1(): bool + { + return $this->value instanceof Foo && $this->type === 'foo1'; } /** * @return Foo */ - public function asFoo1(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo1')){ + public function asFoo1(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo1')) { throw new Exception( "Expected foo1; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isFoo2(): bool { - return $this->value instanceof Foo&& $this->type === 'foo2'; + public function isFoo2(): bool + { + return $this->value instanceof Foo && $this->type === 'foo2'; } /** * @return Foo */ - public function asFoo2(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo2')){ + public function asFoo2(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo2')) { throw new Exception( "Expected foo2; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'foo1': $value = $this->asFoo1()->jsonSerialize(); $result = array_merge($value, $result); @@ -135,26 +143,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -163,22 +172,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'foo1': $args['value'] = Foo::jsonDeserialize($data); break; @@ -190,7 +200,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/unions/src/Types/UnionWithDuplicativeDiscriminants.php b/seed/php-model/unions/src/Types/UnionWithDuplicativeDiscriminants.php index ec6277553de6..354d6a65b618 100644 --- a/seed/php-model/unions/src/Types/UnionWithDuplicativeDiscriminants.php +++ b/seed/php-model/unions/src/Types/UnionWithDuplicativeDiscriminants.php @@ -42,16 +42,17 @@ class UnionWithDuplicativeDiscriminants extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param FirstItemType $firstItemType * @return UnionWithDuplicativeDiscriminants */ - public static function firstItemType(FirstItemType $firstItemType): UnionWithDuplicativeDiscriminants { + public static function firstItemType(FirstItemType $firstItemType): UnionWithDuplicativeDiscriminants + { return new UnionWithDuplicativeDiscriminants([ 'type' => 'firstItemType', 'value' => $firstItemType, @@ -62,7 +63,8 @@ public static function firstItemType(FirstItemType $firstItemType): UnionWithDup * @param SecondItemType $secondItemType * @return UnionWithDuplicativeDiscriminants */ - public static function secondItemType(SecondItemType $secondItemType): UnionWithDuplicativeDiscriminants { + public static function secondItemType(SecondItemType $secondItemType): UnionWithDuplicativeDiscriminants + { return new UnionWithDuplicativeDiscriminants([ 'type' => 'secondItemType', 'value' => $secondItemType, @@ -72,61 +74,67 @@ public static function secondItemType(SecondItemType $secondItemType): UnionWith /** * @return bool */ - public function isFirstItemType(): bool { - return $this->value instanceof FirstItemType&& $this->type === 'firstItemType'; + public function isFirstItemType(): bool + { + return $this->value instanceof FirstItemType && $this->type === 'firstItemType'; } /** * @return FirstItemType */ - public function asFirstItemType(): FirstItemType { - if (!($this->value instanceof FirstItemType&& $this->type === 'firstItemType')){ + public function asFirstItemType(): FirstItemType + { + if (!($this->value instanceof FirstItemType && $this->type === 'firstItemType')) { throw new Exception( "Expected firstItemType; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isSecondItemType(): bool { - return $this->value instanceof SecondItemType&& $this->type === 'secondItemType'; + public function isSecondItemType(): bool + { + return $this->value instanceof SecondItemType && $this->type === 'secondItemType'; } /** * @return SecondItemType */ - public function asSecondItemType(): SecondItemType { - if (!($this->value instanceof SecondItemType&& $this->type === 'secondItemType')){ + public function asSecondItemType(): SecondItemType + { + if (!($this->value instanceof SecondItemType && $this->type === 'secondItemType')) { throw new Exception( "Expected secondItemType; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'firstItemType': $value = $this->asFirstItemType()->jsonSerialize(); $result = array_merge($value, $result); @@ -137,26 +145,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -165,22 +174,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'firstItemType': $args['value'] = FirstItemType::jsonDeserialize($data); break; @@ -192,7 +202,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/unions/src/Types/UnionWithLiteral.php b/seed/php-model/unions/src/Types/UnionWithLiteral.php index 9d0afffb3a00..c10f4f528918 100644 --- a/seed/php-model/unions/src/Types/UnionWithLiteral.php +++ b/seed/php-model/unions/src/Types/UnionWithLiteral.php @@ -46,9 +46,10 @@ class UnionWithLiteral extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->base = $values['base'];$this->type = $values['type'];$this->value = $values['value']; + ) { + $this->base = $values['base']; + $this->type = $values['type']; + $this->value = $values['value']; } /** @@ -56,7 +57,8 @@ private function __construct( * @param 'fern' $fern * @return UnionWithLiteral */ - public static function fern(string $base, string $fern): UnionWithLiteral { + public static function fern(string $base, string $fern): UnionWithLiteral + { return new UnionWithLiteral([ 'base' => $base, 'type' => 'fern', @@ -67,67 +69,72 @@ public static function fern(string $base, string $fern): UnionWithLiteral { /** * @return bool */ - public function isFern(): bool { - return $this->value === 'fern'&& $this->type === 'fern'; + public function isFern(): bool + { + return $this->value === 'fern' && $this->type === 'fern'; } /** * @return 'fern' */ - public function asFern(): string { - if (!($this->value === 'fern'&& $this->type === 'fern')){ + public function asFern(): string + { + if (!($this->value === 'fern' && $this->type === 'fern')) { throw new Exception( "Expected fern; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'fern': $value = $this->value; $result['fern'] = $value; break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -136,41 +143,42 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('base', $data)){ + if (!array_key_exists('base', $data)) { throw new Exception( "JSON data is missing property 'base'", ); } - if (!($data['base'] === 'base')){ + if (!($data['base'] === 'base')) { throw new Exception( "Expected property 'base' in JSON data to be 'base', instead received " . get_debug_type($data['base']), ); } $args['base'] = $data['base']; - - if (!array_key_exists('type', $data)){ + + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'fern': - if (!array_key_exists('fern', $data)){ + if (!array_key_exists('fern', $data)) { throw new Exception( "JSON data is missing property 'fern'", ); } - + $args['value'] = $data['fern']; break; case '_unknown': @@ -178,7 +186,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/unions/src/Types/UnionWithMultipleNoProperties.php b/seed/php-model/unions/src/Types/UnionWithMultipleNoProperties.php index 5bd1fedbdbe2..7d1e43b8031c 100644 --- a/seed/php-model/unions/src/Types/UnionWithMultipleNoProperties.php +++ b/seed/php-model/unions/src/Types/UnionWithMultipleNoProperties.php @@ -44,16 +44,17 @@ class UnionWithMultipleNoProperties extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param Foo $foo * @return UnionWithMultipleNoProperties */ - public static function foo(Foo $foo): UnionWithMultipleNoProperties { + public static function foo(Foo $foo): UnionWithMultipleNoProperties + { return new UnionWithMultipleNoProperties([ 'type' => 'foo', 'value' => $foo, @@ -63,7 +64,8 @@ public static function foo(Foo $foo): UnionWithMultipleNoProperties { /** * @return UnionWithMultipleNoProperties */ - public static function empty1(): UnionWithMultipleNoProperties { + public static function empty1(): UnionWithMultipleNoProperties + { return new UnionWithMultipleNoProperties([ 'type' => 'empty1', 'value' => null, @@ -73,7 +75,8 @@ public static function empty1(): UnionWithMultipleNoProperties { /** * @return UnionWithMultipleNoProperties */ - public static function empty2(): UnionWithMultipleNoProperties { + public static function empty2(): UnionWithMultipleNoProperties + { return new UnionWithMultipleNoProperties([ 'type' => 'empty2', 'value' => null, @@ -83,55 +86,61 @@ public static function empty2(): UnionWithMultipleNoProperties { /** * @return bool */ - public function isFoo(): bool { - return $this->value instanceof Foo&& $this->type === 'foo'; + public function isFoo(): bool + { + return $this->value instanceof Foo && $this->type === 'foo'; } /** * @return Foo */ - public function asFoo(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo')){ + public function asFoo(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo')) { throw new Exception( "Expected foo; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isEmpty1(): bool { - return is_null($this->value)&& $this->type === 'empty1'; + public function isEmpty1(): bool + { + return is_null($this->value) && $this->type === 'empty1'; } /** * @return bool */ - public function isEmpty2(): bool { - return is_null($this->value)&& $this->type === 'empty2'; + public function isEmpty2(): bool + { + return is_null($this->value) && $this->type === 'empty2'; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'foo': $value = $this->asFoo()->jsonSerialize(); $result = array_merge($value, $result); @@ -144,26 +153,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -172,22 +182,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'foo': $args['value'] = Foo::jsonDeserialize($data); break; @@ -202,7 +213,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/unions/src/Types/UnionWithNoProperties.php b/seed/php-model/unions/src/Types/UnionWithNoProperties.php index a4afa158a378..aa4fd389b09d 100644 --- a/seed/php-model/unions/src/Types/UnionWithNoProperties.php +++ b/seed/php-model/unions/src/Types/UnionWithNoProperties.php @@ -42,16 +42,17 @@ class UnionWithNoProperties extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param Foo $foo * @return UnionWithNoProperties */ - public static function foo(Foo $foo): UnionWithNoProperties { + public static function foo(Foo $foo): UnionWithNoProperties + { return new UnionWithNoProperties([ 'type' => 'foo', 'value' => $foo, @@ -61,7 +62,8 @@ public static function foo(Foo $foo): UnionWithNoProperties { /** * @return UnionWithNoProperties */ - public static function empty(): UnionWithNoProperties { + public static function empty(): UnionWithNoProperties + { return new UnionWithNoProperties([ 'type' => 'empty', 'value' => null, @@ -71,48 +73,53 @@ public static function empty(): UnionWithNoProperties { /** * @return bool */ - public function isFoo(): bool { - return $this->value instanceof Foo&& $this->type === 'foo'; + public function isFoo(): bool + { + return $this->value instanceof Foo && $this->type === 'foo'; } /** * @return Foo */ - public function asFoo(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo')){ + public function asFoo(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo')) { throw new Exception( "Expected foo; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isEmpty(): bool { - return is_null($this->value)&& $this->type === 'empty'; + public function isEmpty(): bool + { + return is_null($this->value) && $this->type === 'empty'; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'foo': $value = $this->asFoo()->jsonSerialize(); $result = array_merge($value, $result); @@ -122,26 +129,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -150,22 +158,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'foo': $args['value'] = Foo::jsonDeserialize($data); break; @@ -177,7 +186,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/unions/src/Types/UnionWithOptionalTime.php b/seed/php-model/unions/src/Types/UnionWithOptionalTime.php index c56866ef3e99..afcbbf0dfa69 100644 --- a/seed/php-model/unions/src/Types/UnionWithOptionalTime.php +++ b/seed/php-model/unions/src/Types/UnionWithOptionalTime.php @@ -44,16 +44,17 @@ class UnionWithOptionalTime extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param ?DateTime $date * @return UnionWithOptionalTime */ - public static function date(?DateTime $date = null): UnionWithOptionalTime { + public static function date(?DateTime $date = null): UnionWithOptionalTime + { return new UnionWithOptionalTime([ 'type' => 'date', 'value' => $date, @@ -64,7 +65,8 @@ public static function date(?DateTime $date = null): UnionWithOptionalTime { * @param ?DateTime $datetime * @return UnionWithOptionalTime */ - public static function datetime(?DateTime $datetime = null): UnionWithOptionalTime { + public static function datetime(?DateTime $datetime = null): UnionWithOptionalTime + { return new UnionWithOptionalTime([ 'type' => 'datetime', 'value' => $datetime, @@ -74,97 +76,104 @@ public static function datetime(?DateTime $datetime = null): UnionWithOptionalTi /** * @return bool */ - public function isDate(): bool { - return (is_null($this->value) || $this->value instanceof DateTime)&& $this->type === 'date'; + public function isDate(): bool + { + return (is_null($this->value) || $this->value instanceof DateTime) && $this->type === 'date'; } /** * @return ?DateTime */ - public function asDate(): ?DateTime { - if (!((is_null($this->value) || $this->value instanceof DateTime)&& $this->type === 'date')){ + public function asDate(): ?DateTime + { + if (!((is_null($this->value) || $this->value instanceof DateTime) && $this->type === 'date')) { throw new Exception( "Expected date; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isDatetime(): bool { - return (is_null($this->value) || $this->value instanceof DateTime)&& $this->type === 'datetime'; + public function isDatetime(): bool + { + return (is_null($this->value) || $this->value instanceof DateTime) && $this->type === 'datetime'; } /** * @return ?DateTime */ - public function asDatetime(): ?DateTime { - if (!((is_null($this->value) || $this->value instanceof DateTime)&& $this->type === 'datetime')){ + public function asDatetime(): ?DateTime + { + if (!((is_null($this->value) || $this->value instanceof DateTime) && $this->type === 'datetime')) { throw new Exception( "Expected datetime; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'date': $value = $this->asDate(); - if (!is_null($value)){ + if (!is_null($value)) { $value = JsonSerializer::serializeDate($value); } $result['date'] = $value; break; case 'datetime': $value = $this->asDatetime(); - if (!is_null($value)){ + if (!is_null($value)) { $value = JsonSerializer::serializeDateTime($value); } $result['datetime'] = $value; break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -173,38 +182,39 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'date': - if (!array_key_exists('date', $data)){ + if (!array_key_exists('date', $data)) { throw new Exception( "JSON data is missing property 'date'", ); } - + $args['value'] = $data['date']; break; case 'datetime': - if (!array_key_exists('datetime', $data)){ + if (!array_key_exists('datetime', $data)) { throw new Exception( "JSON data is missing property 'datetime'", ); } - + $args['value'] = $data['datetime']; break; case '_unknown': @@ -212,7 +222,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/unions/src/Types/UnionWithPrimitive.php b/seed/php-model/unions/src/Types/UnionWithPrimitive.php index d67cef8f83e8..6c7edcd62712 100644 --- a/seed/php-model/unions/src/Types/UnionWithPrimitive.php +++ b/seed/php-model/unions/src/Types/UnionWithPrimitive.php @@ -42,16 +42,17 @@ class UnionWithPrimitive extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param int $integer * @return UnionWithPrimitive */ - public static function integer(int $integer): UnionWithPrimitive { + public static function integer(int $integer): UnionWithPrimitive + { return new UnionWithPrimitive([ 'type' => 'integer', 'value' => $integer, @@ -62,7 +63,8 @@ public static function integer(int $integer): UnionWithPrimitive { * @param string $string * @return UnionWithPrimitive */ - public static function string(string $string): UnionWithPrimitive { + public static function string(string $string): UnionWithPrimitive + { return new UnionWithPrimitive([ 'type' => 'string', 'value' => $string, @@ -72,61 +74,67 @@ public static function string(string $string): UnionWithPrimitive { /** * @return bool */ - public function isInteger(): bool { - return is_int($this->value)&& $this->type === 'integer'; + public function isInteger(): bool + { + return is_int($this->value) && $this->type === 'integer'; } /** * @return int */ - public function asInteger(): int { - if (!(is_int($this->value)&& $this->type === 'integer')){ + public function asInteger(): int + { + if (!(is_int($this->value) && $this->type === 'integer')) { throw new Exception( "Expected integer; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isString(): bool { - return is_string($this->value)&& $this->type === 'string'; + public function isString(): bool + { + return is_string($this->value) && $this->type === 'string'; } /** * @return string */ - public function asString(): string { - if (!(is_string($this->value)&& $this->type === 'string')){ + public function asString(): string + { + if (!(is_string($this->value) && $this->type === 'string')) { throw new Exception( "Expected string; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'integer': $value = $this->value; $result['integer'] = $value; @@ -137,26 +145,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -165,38 +174,39 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'integer': - if (!array_key_exists('integer', $data)){ + if (!array_key_exists('integer', $data)) { throw new Exception( "JSON data is missing property 'integer'", ); } - + $args['value'] = $data['integer']; break; case 'string': - if (!array_key_exists('string', $data)){ + if (!array_key_exists('string', $data)) { throw new Exception( "JSON data is missing property 'string'", ); } - + $args['value'] = $data['string']; break; case '_unknown': @@ -204,7 +214,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/unions/src/Types/UnionWithSameNumberTypes.php b/seed/php-model/unions/src/Types/UnionWithSameNumberTypes.php index fd1c25243e83..24f90d39e8bd 100644 --- a/seed/php-model/unions/src/Types/UnionWithSameNumberTypes.php +++ b/seed/php-model/unions/src/Types/UnionWithSameNumberTypes.php @@ -44,16 +44,17 @@ class UnionWithSameNumberTypes extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param int $positiveInt * @return UnionWithSameNumberTypes */ - public static function positiveInt(int $positiveInt): UnionWithSameNumberTypes { + public static function positiveInt(int $positiveInt): UnionWithSameNumberTypes + { return new UnionWithSameNumberTypes([ 'type' => 'positiveInt', 'value' => $positiveInt, @@ -64,7 +65,8 @@ public static function positiveInt(int $positiveInt): UnionWithSameNumberTypes { * @param int $negativeInt * @return UnionWithSameNumberTypes */ - public static function negativeInt(int $negativeInt): UnionWithSameNumberTypes { + public static function negativeInt(int $negativeInt): UnionWithSameNumberTypes + { return new UnionWithSameNumberTypes([ 'type' => 'negativeInt', 'value' => $negativeInt, @@ -75,7 +77,8 @@ public static function negativeInt(int $negativeInt): UnionWithSameNumberTypes { * @param float $anyNumber * @return UnionWithSameNumberTypes */ - public static function anyNumber(float $anyNumber): UnionWithSameNumberTypes { + public static function anyNumber(float $anyNumber): UnionWithSameNumberTypes + { return new UnionWithSameNumberTypes([ 'type' => 'anyNumber', 'value' => $anyNumber, @@ -85,81 +88,89 @@ public static function anyNumber(float $anyNumber): UnionWithSameNumberTypes { /** * @return bool */ - public function isPositiveInt(): bool { - return is_int($this->value)&& $this->type === 'positiveInt'; + public function isPositiveInt(): bool + { + return is_int($this->value) && $this->type === 'positiveInt'; } /** * @return int */ - public function asPositiveInt(): int { - if (!(is_int($this->value)&& $this->type === 'positiveInt')){ + public function asPositiveInt(): int + { + if (!(is_int($this->value) && $this->type === 'positiveInt')) { throw new Exception( "Expected positiveInt; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isNegativeInt(): bool { - return is_int($this->value)&& $this->type === 'negativeInt'; + public function isNegativeInt(): bool + { + return is_int($this->value) && $this->type === 'negativeInt'; } /** * @return int */ - public function asNegativeInt(): int { - if (!(is_int($this->value)&& $this->type === 'negativeInt')){ + public function asNegativeInt(): int + { + if (!(is_int($this->value) && $this->type === 'negativeInt')) { throw new Exception( "Expected negativeInt; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isAnyNumber(): bool { - return is_float($this->value)&& $this->type === 'anyNumber'; + public function isAnyNumber(): bool + { + return is_float($this->value) && $this->type === 'anyNumber'; } /** * @return float */ - public function asAnyNumber(): float { - if (!(is_float($this->value)&& $this->type === 'anyNumber')){ + public function asAnyNumber(): float + { + if (!(is_float($this->value) && $this->type === 'anyNumber')) { throw new Exception( "Expected anyNumber; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'positiveInt': $value = $this->value; $result['positiveInt'] = $value; @@ -174,26 +185,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -202,47 +214,48 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'positiveInt': - if (!array_key_exists('positiveInt', $data)){ + if (!array_key_exists('positiveInt', $data)) { throw new Exception( "JSON data is missing property 'positiveInt'", ); } - + $args['value'] = $data['positiveInt']; break; case 'negativeInt': - if (!array_key_exists('negativeInt', $data)){ + if (!array_key_exists('negativeInt', $data)) { throw new Exception( "JSON data is missing property 'negativeInt'", ); } - + $args['value'] = $data['negativeInt']; break; case 'anyNumber': - if (!array_key_exists('anyNumber', $data)){ + if (!array_key_exists('anyNumber', $data)) { throw new Exception( "JSON data is missing property 'anyNumber'", ); } - + $args['value'] = $data['anyNumber']; break; case '_unknown': @@ -250,7 +263,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/unions/src/Types/UnionWithSameStringTypes.php b/seed/php-model/unions/src/Types/UnionWithSameStringTypes.php index 7f16554362b0..9f8c047439e3 100644 --- a/seed/php-model/unions/src/Types/UnionWithSameStringTypes.php +++ b/seed/php-model/unions/src/Types/UnionWithSameStringTypes.php @@ -42,16 +42,17 @@ class UnionWithSameStringTypes extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param string $customFormat * @return UnionWithSameStringTypes */ - public static function customFormat(string $customFormat): UnionWithSameStringTypes { + public static function customFormat(string $customFormat): UnionWithSameStringTypes + { return new UnionWithSameStringTypes([ 'type' => 'customFormat', 'value' => $customFormat, @@ -62,7 +63,8 @@ public static function customFormat(string $customFormat): UnionWithSameStringTy * @param string $regularString * @return UnionWithSameStringTypes */ - public static function regularString(string $regularString): UnionWithSameStringTypes { + public static function regularString(string $regularString): UnionWithSameStringTypes + { return new UnionWithSameStringTypes([ 'type' => 'regularString', 'value' => $regularString, @@ -73,7 +75,8 @@ public static function regularString(string $regularString): UnionWithSameString * @param string $patternString * @return UnionWithSameStringTypes */ - public static function patternString(string $patternString): UnionWithSameStringTypes { + public static function patternString(string $patternString): UnionWithSameStringTypes + { return new UnionWithSameStringTypes([ 'type' => 'patternString', 'value' => $patternString, @@ -83,81 +86,89 @@ public static function patternString(string $patternString): UnionWithSameString /** * @return bool */ - public function isCustomFormat(): bool { - return is_string($this->value)&& $this->type === 'customFormat'; + public function isCustomFormat(): bool + { + return is_string($this->value) && $this->type === 'customFormat'; } /** * @return string */ - public function asCustomFormat(): string { - if (!(is_string($this->value)&& $this->type === 'customFormat')){ + public function asCustomFormat(): string + { + if (!(is_string($this->value) && $this->type === 'customFormat')) { throw new Exception( "Expected customFormat; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isRegularString(): bool { - return is_string($this->value)&& $this->type === 'regularString'; + public function isRegularString(): bool + { + return is_string($this->value) && $this->type === 'regularString'; } /** * @return string */ - public function asRegularString(): string { - if (!(is_string($this->value)&& $this->type === 'regularString')){ + public function asRegularString(): string + { + if (!(is_string($this->value) && $this->type === 'regularString')) { throw new Exception( "Expected regularString; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isPatternString(): bool { - return is_string($this->value)&& $this->type === 'patternString'; + public function isPatternString(): bool + { + return is_string($this->value) && $this->type === 'patternString'; } /** * @return string */ - public function asPatternString(): string { - if (!(is_string($this->value)&& $this->type === 'patternString')){ + public function asPatternString(): string + { + if (!(is_string($this->value) && $this->type === 'patternString')) { throw new Exception( "Expected patternString; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'customFormat': $value = $this->value; $result['customFormat'] = $value; @@ -172,26 +183,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -200,47 +212,48 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'customFormat': - if (!array_key_exists('customFormat', $data)){ + if (!array_key_exists('customFormat', $data)) { throw new Exception( "JSON data is missing property 'customFormat'", ); } - + $args['value'] = $data['customFormat']; break; case 'regularString': - if (!array_key_exists('regularString', $data)){ + if (!array_key_exists('regularString', $data)) { throw new Exception( "JSON data is missing property 'regularString'", ); } - + $args['value'] = $data['regularString']; break; case 'patternString': - if (!array_key_exists('patternString', $data)){ + if (!array_key_exists('patternString', $data)) { throw new Exception( "JSON data is missing property 'patternString'", ); } - + $args['value'] = $data['patternString']; break; case '_unknown': @@ -248,7 +261,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/unions/src/Types/UnionWithSingleElement.php b/seed/php-model/unions/src/Types/UnionWithSingleElement.php index 8000de5cf6d5..5d17d877b09f 100644 --- a/seed/php-model/unions/src/Types/UnionWithSingleElement.php +++ b/seed/php-model/unions/src/Types/UnionWithSingleElement.php @@ -38,16 +38,17 @@ class UnionWithSingleElement extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param Foo $foo * @return UnionWithSingleElement */ - public static function foo(Foo $foo): UnionWithSingleElement { + public static function foo(Foo $foo): UnionWithSingleElement + { return new UnionWithSingleElement([ 'type' => 'foo', 'value' => $foo, @@ -57,67 +58,72 @@ public static function foo(Foo $foo): UnionWithSingleElement { /** * @return bool */ - public function isFoo(): bool { - return $this->value instanceof Foo&& $this->type === 'foo'; + public function isFoo(): bool + { + return $this->value instanceof Foo && $this->type === 'foo'; } /** * @return Foo */ - public function asFoo(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo')){ + public function asFoo(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo')) { throw new Exception( "Expected foo; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'foo': $value = $this->asFoo()->jsonSerialize(); $result = array_merge($value, $result); break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -126,22 +132,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'foo': $args['value'] = Foo::jsonDeserialize($data); break; @@ -150,7 +157,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/unions/src/Types/UnionWithSubTypes.php b/seed/php-model/unions/src/Types/UnionWithSubTypes.php index 9740344f1885..7157cd2a6d5b 100644 --- a/seed/php-model/unions/src/Types/UnionWithSubTypes.php +++ b/seed/php-model/unions/src/Types/UnionWithSubTypes.php @@ -42,16 +42,17 @@ class UnionWithSubTypes extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param Foo $foo * @return UnionWithSubTypes */ - public static function foo(Foo $foo): UnionWithSubTypes { + public static function foo(Foo $foo): UnionWithSubTypes + { return new UnionWithSubTypes([ 'type' => 'foo', 'value' => $foo, @@ -62,7 +63,8 @@ public static function foo(Foo $foo): UnionWithSubTypes { * @param FooExtended $fooExtended * @return UnionWithSubTypes */ - public static function fooExtended(FooExtended $fooExtended): UnionWithSubTypes { + public static function fooExtended(FooExtended $fooExtended): UnionWithSubTypes + { return new UnionWithSubTypes([ 'type' => 'fooExtended', 'value' => $fooExtended, @@ -72,61 +74,67 @@ public static function fooExtended(FooExtended $fooExtended): UnionWithSubTypes /** * @return bool */ - public function isFoo(): bool { - return $this->value instanceof Foo&& $this->type === 'foo'; + public function isFoo(): bool + { + return $this->value instanceof Foo && $this->type === 'foo'; } /** * @return Foo */ - public function asFoo(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo')){ + public function asFoo(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo')) { throw new Exception( "Expected foo; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isFooExtended(): bool { - return $this->value instanceof FooExtended&& $this->type === 'fooExtended'; + public function isFooExtended(): bool + { + return $this->value instanceof FooExtended && $this->type === 'fooExtended'; } /** * @return FooExtended */ - public function asFooExtended(): FooExtended { - if (!($this->value instanceof FooExtended&& $this->type === 'fooExtended')){ + public function asFooExtended(): FooExtended + { + if (!($this->value instanceof FooExtended && $this->type === 'fooExtended')) { throw new Exception( "Expected fooExtended; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'foo': $value = $this->asFoo()->jsonSerialize(); $result = array_merge($value, $result); @@ -137,26 +145,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -165,22 +174,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'foo': $args['value'] = Foo::jsonDeserialize($data); break; @@ -192,7 +202,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/unions/src/Types/UnionWithTime.php b/seed/php-model/unions/src/Types/UnionWithTime.php index 76df1c11c57b..5dedd81336fd 100644 --- a/seed/php-model/unions/src/Types/UnionWithTime.php +++ b/seed/php-model/unions/src/Types/UnionWithTime.php @@ -46,16 +46,17 @@ class UnionWithTime extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param int $value * @return UnionWithTime */ - public static function value(int $value): UnionWithTime { + public static function value(int $value): UnionWithTime + { return new UnionWithTime([ 'type' => 'value', 'value' => $value, @@ -66,7 +67,8 @@ public static function value(int $value): UnionWithTime { * @param DateTime $date * @return UnionWithTime */ - public static function date(DateTime $date): UnionWithTime { + public static function date(DateTime $date): UnionWithTime + { return new UnionWithTime([ 'type' => 'date', 'value' => $date, @@ -77,7 +79,8 @@ public static function date(DateTime $date): UnionWithTime { * @param DateTime $datetime * @return UnionWithTime */ - public static function datetime(DateTime $datetime): UnionWithTime { + public static function datetime(DateTime $datetime): UnionWithTime + { return new UnionWithTime([ 'type' => 'datetime', 'value' => $datetime, @@ -87,81 +90,89 @@ public static function datetime(DateTime $datetime): UnionWithTime { /** * @return bool */ - public function isValue(): bool { - return is_int($this->value)&& $this->type === 'value'; + public function isValue(): bool + { + return is_int($this->value) && $this->type === 'value'; } /** * @return int */ - public function asValue(): int { - if (!(is_int($this->value)&& $this->type === 'value')){ + public function asValue(): int + { + if (!(is_int($this->value) && $this->type === 'value')) { throw new Exception( "Expected value; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isDate(): bool { - return $this->value instanceof DateTime&& $this->type === 'date'; + public function isDate(): bool + { + return $this->value instanceof DateTime && $this->type === 'date'; } /** * @return DateTime */ - public function asDate(): DateTime { - if (!($this->value instanceof DateTime&& $this->type === 'date')){ + public function asDate(): DateTime + { + if (!($this->value instanceof DateTime && $this->type === 'date')) { throw new Exception( "Expected date; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isDatetime(): bool { - return $this->value instanceof DateTime&& $this->type === 'datetime'; + public function isDatetime(): bool + { + return $this->value instanceof DateTime && $this->type === 'datetime'; } /** * @return DateTime */ - public function asDatetime(): DateTime { - if (!($this->value instanceof DateTime&& $this->type === 'datetime')){ + public function asDatetime(): DateTime + { + if (!($this->value instanceof DateTime && $this->type === 'datetime')) { throw new Exception( "Expected datetime; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'value': $value = $this->value; $result['value'] = $value; @@ -176,26 +187,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -204,47 +216,48 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'value': - if (!array_key_exists('value', $data)){ + if (!array_key_exists('value', $data)) { throw new Exception( "JSON data is missing property 'value'", ); } - + $args['value'] = $data['value']; break; case 'date': - if (!array_key_exists('date', $data)){ + if (!array_key_exists('date', $data)) { throw new Exception( "JSON data is missing property 'date'", ); } - + $args['value'] = $data['date']; break; case 'datetime': - if (!array_key_exists('datetime', $data)){ + if (!array_key_exists('datetime', $data)) { throw new Exception( "JSON data is missing property 'datetime'", ); } - + $args['value'] = $data['datetime']; break; case '_unknown': @@ -252,7 +265,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/unions/src/Types/UnionWithoutKey.php b/seed/php-model/unions/src/Types/UnionWithoutKey.php index 798e78133766..c67ed338d10f 100644 --- a/seed/php-model/unions/src/Types/UnionWithoutKey.php +++ b/seed/php-model/unions/src/Types/UnionWithoutKey.php @@ -42,16 +42,17 @@ class UnionWithoutKey extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param Foo $foo * @return UnionWithoutKey */ - public static function foo(Foo $foo): UnionWithoutKey { + public static function foo(Foo $foo): UnionWithoutKey + { return new UnionWithoutKey([ 'type' => 'foo', 'value' => $foo, @@ -62,7 +63,8 @@ public static function foo(Foo $foo): UnionWithoutKey { * @param Bar $bar * @return UnionWithoutKey */ - public static function bar(Bar $bar): UnionWithoutKey { + public static function bar(Bar $bar): UnionWithoutKey + { return new UnionWithoutKey([ 'type' => 'bar', 'value' => $bar, @@ -72,61 +74,67 @@ public static function bar(Bar $bar): UnionWithoutKey { /** * @return bool */ - public function isFoo(): bool { - return $this->value instanceof Foo&& $this->type === 'foo'; + public function isFoo(): bool + { + return $this->value instanceof Foo && $this->type === 'foo'; } /** * @return Foo */ - public function asFoo(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo')){ + public function asFoo(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo')) { throw new Exception( "Expected foo; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isBar(): bool { - return $this->value instanceof Bar&& $this->type === 'bar'; + public function isBar(): bool + { + return $this->value instanceof Bar && $this->type === 'bar'; } /** * @return Bar */ - public function asBar(): Bar { - if (!($this->value instanceof Bar&& $this->type === 'bar')){ + public function asBar(): Bar + { + if (!($this->value instanceof Bar && $this->type === 'bar')) { throw new Exception( "Expected bar; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'foo': $value = $this->asFoo()->jsonSerialize(); $result = array_merge($value, $result); @@ -137,26 +145,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -165,22 +174,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'foo': $args['value'] = Foo::jsonDeserialize($data); break; @@ -192,7 +202,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/unions/src/Union/Circle.php b/seed/php-model/unions/src/Union/Circle.php index 257c64f20b7d..a361ed87c9b0 100644 --- a/seed/php-model/unions/src/Union/Circle.php +++ b/seed/php-model/unions/src/Union/Circle.php @@ -20,15 +20,15 @@ class Circle extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->radius = $values['radius']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions/src/Union/GetShapeRequest.php b/seed/php-model/unions/src/Union/GetShapeRequest.php index a84b148bd023..5da803399a3c 100644 --- a/seed/php-model/unions/src/Union/GetShapeRequest.php +++ b/seed/php-model/unions/src/Union/GetShapeRequest.php @@ -20,15 +20,15 @@ class GetShapeRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->id = $values['id']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions/src/Union/Shape.php b/seed/php-model/unions/src/Union/Shape.php index c369a6513c6e..2da9f99896d7 100644 --- a/seed/php-model/unions/src/Union/Shape.php +++ b/seed/php-model/unions/src/Union/Shape.php @@ -50,9 +50,10 @@ class Shape extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->id = $values['id'];$this->type = $values['type'];$this->value = $values['value']; + ) { + $this->id = $values['id']; + $this->type = $values['type']; + $this->value = $values['value']; } /** @@ -60,7 +61,8 @@ private function __construct( * @param Circle $circle * @return Shape */ - public static function circle(string $id, Circle $circle): Shape { + public static function circle(string $id, Circle $circle): Shape + { return new Shape([ 'id' => $id, 'type' => 'circle', @@ -73,7 +75,8 @@ public static function circle(string $id, Circle $circle): Shape { * @param Square $square * @return Shape */ - public static function square(string $id, Square $square): Shape { + public static function square(string $id, Square $square): Shape + { return new Shape([ 'id' => $id, 'type' => 'square', @@ -84,61 +87,67 @@ public static function square(string $id, Square $square): Shape { /** * @return bool */ - public function isCircle(): bool { - return $this->value instanceof Circle&& $this->type === 'circle'; + public function isCircle(): bool + { + return $this->value instanceof Circle && $this->type === 'circle'; } /** * @return Circle */ - public function asCircle(): Circle { - if (!($this->value instanceof Circle&& $this->type === 'circle')){ + public function asCircle(): Circle + { + if (!($this->value instanceof Circle && $this->type === 'circle')) { throw new Exception( "Expected circle; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isSquare(): bool { - return $this->value instanceof Square&& $this->type === 'square'; + public function isSquare(): bool + { + return $this->value instanceof Square && $this->type === 'square'; } /** * @return Square */ - public function asSquare(): Square { - if (!($this->value instanceof Square&& $this->type === 'square')){ + public function asSquare(): Square + { + if (!($this->value instanceof Square && $this->type === 'square')) { throw new Exception( "Expected square; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'circle': $value = $this->asCircle()->jsonSerialize(); $result = array_merge($value, $result); @@ -149,26 +158,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -177,34 +187,35 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('id', $data)){ + if (!array_key_exists('id', $data)) { throw new Exception( "JSON data is missing property 'id'", ); } - if (!(is_string($data['id']))){ + if (!(is_string($data['id']))) { throw new Exception( "Expected property 'id' in JSON data to be string, instead received " . get_debug_type($data['id']), ); } $args['id'] = $data['id']; - - if (!array_key_exists('type', $data)){ + + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'circle': $args['value'] = Circle::jsonDeserialize($data); break; @@ -216,7 +227,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-model/unions/src/Union/Square.php b/seed/php-model/unions/src/Union/Square.php index 5f4a6548abf5..c702fc27a457 100644 --- a/seed/php-model/unions/src/Union/Square.php +++ b/seed/php-model/unions/src/Union/Square.php @@ -20,15 +20,15 @@ class Square extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->length = $values['length']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions/src/Union/WithName.php b/seed/php-model/unions/src/Union/WithName.php index 8994d59ab9b1..533c12569c84 100644 --- a/seed/php-model/unions/src/Union/WithName.php +++ b/seed/php-model/unions/src/Union/WithName.php @@ -20,15 +20,15 @@ class WithName extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unions/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/unions/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/unions/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/unions/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/unions/tests/Core/Json/EnumTest.php b/seed/php-model/unions/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/unions/tests/Core/Json/EnumTest.php +++ b/seed/php-model/unions/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/unions/tests/Core/Json/TraitTest.php b/seed/php-model/unions/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/unions/tests/Core/Json/TraitTest.php +++ b/seed/php-model/unions/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/unions/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/unions/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/unions/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/unions/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/unknown/src/Core/Json/JsonEncoder.php b/seed/php-model/unknown/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/unknown/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/unknown/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/unknown/src/Core/Json/JsonProperty.php b/seed/php-model/unknown/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/unknown/src/Core/Json/JsonProperty.php +++ b/seed/php-model/unknown/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/unknown/src/Core/Types/ArrayType.php b/seed/php-model/unknown/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/unknown/src/Core/Types/ArrayType.php +++ b/seed/php-model/unknown/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/unknown/src/Core/Types/Constant.php b/seed/php-model/unknown/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/unknown/src/Core/Types/Constant.php +++ b/seed/php-model/unknown/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/unknown/src/Core/Types/Union.php b/seed/php-model/unknown/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/unknown/src/Core/Types/Union.php +++ b/seed/php-model/unknown/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/unknown/src/Unknown/MyObject.php b/seed/php-model/unknown/src/Unknown/MyObject.php index 711ab5519f35..d9225ef9aca0 100644 --- a/seed/php-model/unknown/src/Unknown/MyObject.php +++ b/seed/php-model/unknown/src/Unknown/MyObject.php @@ -20,15 +20,15 @@ class MyObject extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->unknown = $values['unknown']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/unknown/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/unknown/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/unknown/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/unknown/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/unknown/tests/Core/Json/EnumTest.php b/seed/php-model/unknown/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/unknown/tests/Core/Json/EnumTest.php +++ b/seed/php-model/unknown/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/unknown/tests/Core/Json/TraitTest.php b/seed/php-model/unknown/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/unknown/tests/Core/Json/TraitTest.php +++ b/seed/php-model/unknown/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/unknown/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/unknown/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/unknown/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/unknown/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/url-form-encoded/src/Core/Json/JsonEncoder.php b/seed/php-model/url-form-encoded/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/url-form-encoded/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/url-form-encoded/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/url-form-encoded/src/Core/Json/JsonProperty.php b/seed/php-model/url-form-encoded/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/url-form-encoded/src/Core/Json/JsonProperty.php +++ b/seed/php-model/url-form-encoded/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/url-form-encoded/src/Core/Types/ArrayType.php b/seed/php-model/url-form-encoded/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/url-form-encoded/src/Core/Types/ArrayType.php +++ b/seed/php-model/url-form-encoded/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/url-form-encoded/src/Core/Types/Constant.php b/seed/php-model/url-form-encoded/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/url-form-encoded/src/Core/Types/Constant.php +++ b/seed/php-model/url-form-encoded/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/url-form-encoded/src/Core/Types/Union.php b/seed/php-model/url-form-encoded/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/url-form-encoded/src/Core/Types/Union.php +++ b/seed/php-model/url-form-encoded/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/url-form-encoded/src/PostSubmitResponse.php b/seed/php-model/url-form-encoded/src/PostSubmitResponse.php index b9574c2f6cb1..07e2eedc3ca9 100644 --- a/seed/php-model/url-form-encoded/src/PostSubmitResponse.php +++ b/seed/php-model/url-form-encoded/src/PostSubmitResponse.php @@ -27,15 +27,16 @@ class PostSubmitResponse extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->status = $values['status'] ?? null;$this->message = $values['message'] ?? null; + ) { + $this->status = $values['status'] ?? null; + $this->message = $values['message'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/url-form-encoded/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/url-form-encoded/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/url-form-encoded/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/url-form-encoded/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/url-form-encoded/tests/Core/Json/EnumTest.php b/seed/php-model/url-form-encoded/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/url-form-encoded/tests/Core/Json/EnumTest.php +++ b/seed/php-model/url-form-encoded/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/url-form-encoded/tests/Core/Json/TraitTest.php b/seed/php-model/url-form-encoded/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/url-form-encoded/tests/Core/Json/TraitTest.php +++ b/seed/php-model/url-form-encoded/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/url-form-encoded/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/url-form-encoded/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/url-form-encoded/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/url-form-encoded/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/validation/src/Core/Json/JsonEncoder.php b/seed/php-model/validation/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/validation/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/validation/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/validation/src/Core/Json/JsonProperty.php b/seed/php-model/validation/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/validation/src/Core/Json/JsonProperty.php +++ b/seed/php-model/validation/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/validation/src/Core/Types/ArrayType.php b/seed/php-model/validation/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/validation/src/Core/Types/ArrayType.php +++ b/seed/php-model/validation/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/validation/src/Core/Types/Constant.php b/seed/php-model/validation/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/validation/src/Core/Types/Constant.php +++ b/seed/php-model/validation/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/validation/src/Core/Types/Union.php b/seed/php-model/validation/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/validation/src/Core/Types/Union.php +++ b/seed/php-model/validation/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/validation/src/Shape.php b/seed/php-model/validation/src/Shape.php index ef19109761c9..76a031011dfa 100644 --- a/seed/php-model/validation/src/Shape.php +++ b/seed/php-model/validation/src/Shape.php @@ -2,8 +2,8 @@ namespace Seed; -enum Shape - : string { +enum Shape: string +{ case Square = "SQUARE"; case Circle = "CIRCLE"; case Triangle = "TRIANGLE"; diff --git a/seed/php-model/validation/src/Type.php b/seed/php-model/validation/src/Type.php index f694feae72e5..e0c685fa851f 100644 --- a/seed/php-model/validation/src/Type.php +++ b/seed/php-model/validation/src/Type.php @@ -44,15 +44,18 @@ class Type extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->decimal = $values['decimal'];$this->even = $values['even'];$this->name = $values['name'];$this->shape = $values['shape']; + ) { + $this->decimal = $values['decimal']; + $this->even = $values['even']; + $this->name = $values['name']; + $this->shape = $values['shape']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/validation/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/validation/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/validation/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/validation/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/validation/tests/Core/Json/EnumTest.php b/seed/php-model/validation/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/validation/tests/Core/Json/EnumTest.php +++ b/seed/php-model/validation/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/validation/tests/Core/Json/TraitTest.php b/seed/php-model/validation/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/validation/tests/Core/Json/TraitTest.php +++ b/seed/php-model/validation/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/validation/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/validation/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/validation/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/validation/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/variables/src/Core/Json/JsonEncoder.php b/seed/php-model/variables/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/variables/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/variables/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/variables/src/Core/Json/JsonProperty.php b/seed/php-model/variables/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/variables/src/Core/Json/JsonProperty.php +++ b/seed/php-model/variables/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/variables/src/Core/Types/ArrayType.php b/seed/php-model/variables/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/variables/src/Core/Types/ArrayType.php +++ b/seed/php-model/variables/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/variables/src/Core/Types/Constant.php b/seed/php-model/variables/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/variables/src/Core/Types/Constant.php +++ b/seed/php-model/variables/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/variables/src/Core/Types/Union.php b/seed/php-model/variables/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/variables/src/Core/Types/Union.php +++ b/seed/php-model/variables/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/variables/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/variables/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/variables/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/variables/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/variables/tests/Core/Json/EnumTest.php b/seed/php-model/variables/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/variables/tests/Core/Json/EnumTest.php +++ b/seed/php-model/variables/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/variables/tests/Core/Json/TraitTest.php b/seed/php-model/variables/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/variables/tests/Core/Json/TraitTest.php +++ b/seed/php-model/variables/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/variables/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/variables/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/variables/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/variables/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/version-no-default/src/Core/Json/JsonEncoder.php b/seed/php-model/version-no-default/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/version-no-default/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/version-no-default/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/version-no-default/src/Core/Json/JsonProperty.php b/seed/php-model/version-no-default/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/version-no-default/src/Core/Json/JsonProperty.php +++ b/seed/php-model/version-no-default/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/version-no-default/src/Core/Types/ArrayType.php b/seed/php-model/version-no-default/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/version-no-default/src/Core/Types/ArrayType.php +++ b/seed/php-model/version-no-default/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/version-no-default/src/Core/Types/Constant.php b/seed/php-model/version-no-default/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/version-no-default/src/Core/Types/Constant.php +++ b/seed/php-model/version-no-default/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/version-no-default/src/Core/Types/Union.php b/seed/php-model/version-no-default/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/version-no-default/src/Core/Types/Union.php +++ b/seed/php-model/version-no-default/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/version-no-default/src/User/User.php b/seed/php-model/version-no-default/src/User/User.php index 7a5290436e3f..e9c0c5b98814 100644 --- a/seed/php-model/version-no-default/src/User/User.php +++ b/seed/php-model/version-no-default/src/User/User.php @@ -27,15 +27,16 @@ class User extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->name = $values['name']; + ) { + $this->id = $values['id']; + $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/version-no-default/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/version-no-default/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/version-no-default/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/version-no-default/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/version-no-default/tests/Core/Json/EnumTest.php b/seed/php-model/version-no-default/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/version-no-default/tests/Core/Json/EnumTest.php +++ b/seed/php-model/version-no-default/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/version-no-default/tests/Core/Json/TraitTest.php b/seed/php-model/version-no-default/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/version-no-default/tests/Core/Json/TraitTest.php +++ b/seed/php-model/version-no-default/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/version-no-default/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/version-no-default/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/version-no-default/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/version-no-default/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/version/src/Core/Json/JsonEncoder.php b/seed/php-model/version/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/version/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/version/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/version/src/Core/Json/JsonProperty.php b/seed/php-model/version/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/version/src/Core/Json/JsonProperty.php +++ b/seed/php-model/version/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/version/src/Core/Types/ArrayType.php b/seed/php-model/version/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/version/src/Core/Types/ArrayType.php +++ b/seed/php-model/version/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/version/src/Core/Types/Constant.php b/seed/php-model/version/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/version/src/Core/Types/Constant.php +++ b/seed/php-model/version/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/version/src/Core/Types/Union.php b/seed/php-model/version/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/version/src/Core/Types/Union.php +++ b/seed/php-model/version/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/version/src/User/User.php b/seed/php-model/version/src/User/User.php index 7a5290436e3f..e9c0c5b98814 100644 --- a/seed/php-model/version/src/User/User.php +++ b/seed/php-model/version/src/User/User.php @@ -27,15 +27,16 @@ class User extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->name = $values['name']; + ) { + $this->id = $values['id']; + $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/version/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/version/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/version/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/version/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/version/tests/Core/Json/EnumTest.php b/seed/php-model/version/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/version/tests/Core/Json/EnumTest.php +++ b/seed/php-model/version/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/version/tests/Core/Json/TraitTest.php b/seed/php-model/version/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/version/tests/Core/Json/TraitTest.php +++ b/seed/php-model/version/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/version/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/version/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/version/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/version/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/websocket-bearer-auth/src/Core/Json/JsonEncoder.php b/seed/php-model/websocket-bearer-auth/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/websocket-bearer-auth/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/websocket-bearer-auth/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/websocket-bearer-auth/src/Core/Json/JsonProperty.php b/seed/php-model/websocket-bearer-auth/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/websocket-bearer-auth/src/Core/Json/JsonProperty.php +++ b/seed/php-model/websocket-bearer-auth/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/websocket-bearer-auth/src/Core/Types/ArrayType.php b/seed/php-model/websocket-bearer-auth/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/websocket-bearer-auth/src/Core/Types/ArrayType.php +++ b/seed/php-model/websocket-bearer-auth/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/websocket-bearer-auth/src/Core/Types/Constant.php b/seed/php-model/websocket-bearer-auth/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/websocket-bearer-auth/src/Core/Types/Constant.php +++ b/seed/php-model/websocket-bearer-auth/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/websocket-bearer-auth/src/Core/Types/Union.php b/seed/php-model/websocket-bearer-auth/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/websocket-bearer-auth/src/Core/Types/Union.php +++ b/seed/php-model/websocket-bearer-auth/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/websocket-bearer-auth/src/Realtime/ReceiveEvent.php b/seed/php-model/websocket-bearer-auth/src/Realtime/ReceiveEvent.php index 4eaf5696b2c2..04f71765cef8 100644 --- a/seed/php-model/websocket-bearer-auth/src/Realtime/ReceiveEvent.php +++ b/seed/php-model/websocket-bearer-auth/src/Realtime/ReceiveEvent.php @@ -27,15 +27,16 @@ class ReceiveEvent extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->alpha = $values['alpha'];$this->beta = $values['beta']; + ) { + $this->alpha = $values['alpha']; + $this->beta = $values['beta']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/websocket-bearer-auth/src/Realtime/ReceiveEvent2.php b/seed/php-model/websocket-bearer-auth/src/Realtime/ReceiveEvent2.php index 826b90ca17f8..612b2cde828e 100644 --- a/seed/php-model/websocket-bearer-auth/src/Realtime/ReceiveEvent2.php +++ b/seed/php-model/websocket-bearer-auth/src/Realtime/ReceiveEvent2.php @@ -34,15 +34,17 @@ class ReceiveEvent2 extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->gamma = $values['gamma'];$this->delta = $values['delta'];$this->epsilon = $values['epsilon']; + ) { + $this->gamma = $values['gamma']; + $this->delta = $values['delta']; + $this->epsilon = $values['epsilon']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/websocket-bearer-auth/src/Realtime/ReceiveEvent3.php b/seed/php-model/websocket-bearer-auth/src/Realtime/ReceiveEvent3.php index a778b5a18a3d..fc383103b98f 100644 --- a/seed/php-model/websocket-bearer-auth/src/Realtime/ReceiveEvent3.php +++ b/seed/php-model/websocket-bearer-auth/src/Realtime/ReceiveEvent3.php @@ -20,15 +20,15 @@ class ReceiveEvent3 extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->receiveText3 = $values['receiveText3']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/websocket-bearer-auth/src/Realtime/ReceiveSnakeCase.php b/seed/php-model/websocket-bearer-auth/src/Realtime/ReceiveSnakeCase.php index 6624b7339033..65f20aaa5d52 100644 --- a/seed/php-model/websocket-bearer-auth/src/Realtime/ReceiveSnakeCase.php +++ b/seed/php-model/websocket-bearer-auth/src/Realtime/ReceiveSnakeCase.php @@ -27,15 +27,16 @@ class ReceiveSnakeCase extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->receiveText = $values['receiveText'];$this->receiveInt = $values['receiveInt']; + ) { + $this->receiveText = $values['receiveText']; + $this->receiveInt = $values['receiveInt']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/websocket-bearer-auth/src/Realtime/SendEvent.php b/seed/php-model/websocket-bearer-auth/src/Realtime/SendEvent.php index c278141cc590..f689eb0c1344 100644 --- a/seed/php-model/websocket-bearer-auth/src/Realtime/SendEvent.php +++ b/seed/php-model/websocket-bearer-auth/src/Realtime/SendEvent.php @@ -27,15 +27,16 @@ class SendEvent extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->sendText = $values['sendText'];$this->sendParam = $values['sendParam']; + ) { + $this->sendText = $values['sendText']; + $this->sendParam = $values['sendParam']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/websocket-bearer-auth/src/Realtime/SendEvent2.php b/seed/php-model/websocket-bearer-auth/src/Realtime/SendEvent2.php index 030d428f0c73..3393ff022971 100644 --- a/seed/php-model/websocket-bearer-auth/src/Realtime/SendEvent2.php +++ b/seed/php-model/websocket-bearer-auth/src/Realtime/SendEvent2.php @@ -27,15 +27,16 @@ class SendEvent2 extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->sendText2 = $values['sendText2'];$this->sendParam2 = $values['sendParam2']; + ) { + $this->sendText2 = $values['sendText2']; + $this->sendParam2 = $values['sendParam2']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/websocket-bearer-auth/src/Realtime/SendSnakeCase.php b/seed/php-model/websocket-bearer-auth/src/Realtime/SendSnakeCase.php index 395055425f78..f7c5cc3c5540 100644 --- a/seed/php-model/websocket-bearer-auth/src/Realtime/SendSnakeCase.php +++ b/seed/php-model/websocket-bearer-auth/src/Realtime/SendSnakeCase.php @@ -27,15 +27,16 @@ class SendSnakeCase extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->sendText = $values['sendText'];$this->sendParam = $values['sendParam']; + ) { + $this->sendText = $values['sendText']; + $this->sendParam = $values['sendParam']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/websocket-bearer-auth/src/RealtimeNoAuth/NoAuthReceiveEvent.php b/seed/php-model/websocket-bearer-auth/src/RealtimeNoAuth/NoAuthReceiveEvent.php index 8aa80b611fa6..74c405b734dd 100644 --- a/seed/php-model/websocket-bearer-auth/src/RealtimeNoAuth/NoAuthReceiveEvent.php +++ b/seed/php-model/websocket-bearer-auth/src/RealtimeNoAuth/NoAuthReceiveEvent.php @@ -20,15 +20,15 @@ class NoAuthReceiveEvent extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->response = $values['response']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/websocket-bearer-auth/src/RealtimeNoAuth/NoAuthSendEvent.php b/seed/php-model/websocket-bearer-auth/src/RealtimeNoAuth/NoAuthSendEvent.php index 0aab2b6eb694..3d403bc46c8d 100644 --- a/seed/php-model/websocket-bearer-auth/src/RealtimeNoAuth/NoAuthSendEvent.php +++ b/seed/php-model/websocket-bearer-auth/src/RealtimeNoAuth/NoAuthSendEvent.php @@ -20,15 +20,15 @@ class NoAuthSendEvent extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->text = $values['text']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/websocket-bearer-auth/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/websocket-bearer-auth/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/websocket-bearer-auth/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/websocket-bearer-auth/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/websocket-bearer-auth/tests/Core/Json/EnumTest.php b/seed/php-model/websocket-bearer-auth/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/websocket-bearer-auth/tests/Core/Json/EnumTest.php +++ b/seed/php-model/websocket-bearer-auth/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/websocket-bearer-auth/tests/Core/Json/TraitTest.php b/seed/php-model/websocket-bearer-auth/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/websocket-bearer-auth/tests/Core/Json/TraitTest.php +++ b/seed/php-model/websocket-bearer-auth/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/websocket-bearer-auth/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/websocket-bearer-auth/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/websocket-bearer-auth/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/websocket-bearer-auth/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/websocket-inferred-auth/src/Auth/TokenResponse.php b/seed/php-model/websocket-inferred-auth/src/Auth/TokenResponse.php index b30f2443d472..ac86f738cbfc 100644 --- a/seed/php-model/websocket-inferred-auth/src/Auth/TokenResponse.php +++ b/seed/php-model/websocket-inferred-auth/src/Auth/TokenResponse.php @@ -30,15 +30,16 @@ class TokenResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->accessToken = $values['accessToken'];$this->refreshToken = $values['refreshToken'] ?? null; + ) { + $this->accessToken = $values['accessToken']; + $this->refreshToken = $values['refreshToken'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/websocket-inferred-auth/src/Core/Json/JsonEncoder.php b/seed/php-model/websocket-inferred-auth/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/websocket-inferred-auth/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/websocket-inferred-auth/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/websocket-inferred-auth/src/Core/Json/JsonProperty.php b/seed/php-model/websocket-inferred-auth/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/websocket-inferred-auth/src/Core/Json/JsonProperty.php +++ b/seed/php-model/websocket-inferred-auth/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/websocket-inferred-auth/src/Core/Types/ArrayType.php b/seed/php-model/websocket-inferred-auth/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/websocket-inferred-auth/src/Core/Types/ArrayType.php +++ b/seed/php-model/websocket-inferred-auth/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/websocket-inferred-auth/src/Core/Types/Constant.php b/seed/php-model/websocket-inferred-auth/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/websocket-inferred-auth/src/Core/Types/Constant.php +++ b/seed/php-model/websocket-inferred-auth/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/websocket-inferred-auth/src/Core/Types/Union.php b/seed/php-model/websocket-inferred-auth/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/websocket-inferred-auth/src/Core/Types/Union.php +++ b/seed/php-model/websocket-inferred-auth/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/websocket-inferred-auth/src/Realtime/ReceiveEvent.php b/seed/php-model/websocket-inferred-auth/src/Realtime/ReceiveEvent.php index 4eaf5696b2c2..04f71765cef8 100644 --- a/seed/php-model/websocket-inferred-auth/src/Realtime/ReceiveEvent.php +++ b/seed/php-model/websocket-inferred-auth/src/Realtime/ReceiveEvent.php @@ -27,15 +27,16 @@ class ReceiveEvent extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->alpha = $values['alpha'];$this->beta = $values['beta']; + ) { + $this->alpha = $values['alpha']; + $this->beta = $values['beta']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/websocket-inferred-auth/src/Realtime/ReceiveEvent2.php b/seed/php-model/websocket-inferred-auth/src/Realtime/ReceiveEvent2.php index 826b90ca17f8..612b2cde828e 100644 --- a/seed/php-model/websocket-inferred-auth/src/Realtime/ReceiveEvent2.php +++ b/seed/php-model/websocket-inferred-auth/src/Realtime/ReceiveEvent2.php @@ -34,15 +34,17 @@ class ReceiveEvent2 extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->gamma = $values['gamma'];$this->delta = $values['delta'];$this->epsilon = $values['epsilon']; + ) { + $this->gamma = $values['gamma']; + $this->delta = $values['delta']; + $this->epsilon = $values['epsilon']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/websocket-inferred-auth/src/Realtime/ReceiveEvent3.php b/seed/php-model/websocket-inferred-auth/src/Realtime/ReceiveEvent3.php index a778b5a18a3d..fc383103b98f 100644 --- a/seed/php-model/websocket-inferred-auth/src/Realtime/ReceiveEvent3.php +++ b/seed/php-model/websocket-inferred-auth/src/Realtime/ReceiveEvent3.php @@ -20,15 +20,15 @@ class ReceiveEvent3 extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->receiveText3 = $values['receiveText3']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/websocket-inferred-auth/src/Realtime/ReceiveSnakeCase.php b/seed/php-model/websocket-inferred-auth/src/Realtime/ReceiveSnakeCase.php index 6624b7339033..65f20aaa5d52 100644 --- a/seed/php-model/websocket-inferred-auth/src/Realtime/ReceiveSnakeCase.php +++ b/seed/php-model/websocket-inferred-auth/src/Realtime/ReceiveSnakeCase.php @@ -27,15 +27,16 @@ class ReceiveSnakeCase extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->receiveText = $values['receiveText'];$this->receiveInt = $values['receiveInt']; + ) { + $this->receiveText = $values['receiveText']; + $this->receiveInt = $values['receiveInt']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/websocket-inferred-auth/src/Realtime/SendEvent.php b/seed/php-model/websocket-inferred-auth/src/Realtime/SendEvent.php index c278141cc590..f689eb0c1344 100644 --- a/seed/php-model/websocket-inferred-auth/src/Realtime/SendEvent.php +++ b/seed/php-model/websocket-inferred-auth/src/Realtime/SendEvent.php @@ -27,15 +27,16 @@ class SendEvent extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->sendText = $values['sendText'];$this->sendParam = $values['sendParam']; + ) { + $this->sendText = $values['sendText']; + $this->sendParam = $values['sendParam']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/websocket-inferred-auth/src/Realtime/SendEvent2.php b/seed/php-model/websocket-inferred-auth/src/Realtime/SendEvent2.php index 030d428f0c73..3393ff022971 100644 --- a/seed/php-model/websocket-inferred-auth/src/Realtime/SendEvent2.php +++ b/seed/php-model/websocket-inferred-auth/src/Realtime/SendEvent2.php @@ -27,15 +27,16 @@ class SendEvent2 extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->sendText2 = $values['sendText2'];$this->sendParam2 = $values['sendParam2']; + ) { + $this->sendText2 = $values['sendText2']; + $this->sendParam2 = $values['sendParam2']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/websocket-inferred-auth/src/Realtime/SendSnakeCase.php b/seed/php-model/websocket-inferred-auth/src/Realtime/SendSnakeCase.php index 395055425f78..f7c5cc3c5540 100644 --- a/seed/php-model/websocket-inferred-auth/src/Realtime/SendSnakeCase.php +++ b/seed/php-model/websocket-inferred-auth/src/Realtime/SendSnakeCase.php @@ -27,15 +27,16 @@ class SendSnakeCase extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->sendText = $values['sendText'];$this->sendParam = $values['sendParam']; + ) { + $this->sendText = $values['sendText']; + $this->sendParam = $values['sendParam']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/websocket-inferred-auth/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/websocket-inferred-auth/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/websocket-inferred-auth/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/websocket-inferred-auth/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/websocket-inferred-auth/tests/Core/Json/EnumTest.php b/seed/php-model/websocket-inferred-auth/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/websocket-inferred-auth/tests/Core/Json/EnumTest.php +++ b/seed/php-model/websocket-inferred-auth/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/websocket-inferred-auth/tests/Core/Json/TraitTest.php b/seed/php-model/websocket-inferred-auth/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/websocket-inferred-auth/tests/Core/Json/TraitTest.php +++ b/seed/php-model/websocket-inferred-auth/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/websocket-inferred-auth/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/websocket-inferred-auth/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/websocket-inferred-auth/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/websocket-inferred-auth/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/websocket/src/Core/Json/JsonEncoder.php b/seed/php-model/websocket/src/Core/Json/JsonEncoder.php index cef75455aad7..0dbf3fcc9948 100644 --- a/seed/php-model/websocket/src/Core/Json/JsonEncoder.php +++ b/seed/php-model/websocket/src/Core/Json/JsonEncoder.php @@ -13,7 +13,8 @@ class JsonEncoder * @return string The encoded string. * @throws JsonException */ - public static function encode(mixed $value): string { + public static function encode(mixed $value): string + { return json_encode($value, JSON_THROW_ON_ERROR); } -} \ No newline at end of file +} diff --git a/seed/php-model/websocket/src/Core/Json/JsonProperty.php b/seed/php-model/websocket/src/Core/Json/JsonProperty.php index 1371286c93c6..6ab737fac2a9 100644 --- a/seed/php-model/websocket/src/Core/Json/JsonProperty.php +++ b/seed/php-model/websocket/src/Core/Json/JsonProperty.php @@ -11,4 +11,3 @@ public function __construct(public string $name) { } } - diff --git a/seed/php-model/websocket/src/Core/Types/ArrayType.php b/seed/php-model/websocket/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-model/websocket/src/Core/Types/ArrayType.php +++ b/seed/php-model/websocket/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-model/websocket/src/Core/Types/Constant.php b/seed/php-model/websocket/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-model/websocket/src/Core/Types/Constant.php +++ b/seed/php-model/websocket/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-model/websocket/src/Core/Types/Union.php b/seed/php-model/websocket/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-model/websocket/src/Core/Types/Union.php +++ b/seed/php-model/websocket/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-model/websocket/src/Realtime/ReceiveEvent.php b/seed/php-model/websocket/src/Realtime/ReceiveEvent.php index 4eaf5696b2c2..04f71765cef8 100644 --- a/seed/php-model/websocket/src/Realtime/ReceiveEvent.php +++ b/seed/php-model/websocket/src/Realtime/ReceiveEvent.php @@ -27,15 +27,16 @@ class ReceiveEvent extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->alpha = $values['alpha'];$this->beta = $values['beta']; + ) { + $this->alpha = $values['alpha']; + $this->beta = $values['beta']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/websocket/src/Realtime/ReceiveEvent2.php b/seed/php-model/websocket/src/Realtime/ReceiveEvent2.php index 826b90ca17f8..612b2cde828e 100644 --- a/seed/php-model/websocket/src/Realtime/ReceiveEvent2.php +++ b/seed/php-model/websocket/src/Realtime/ReceiveEvent2.php @@ -34,15 +34,17 @@ class ReceiveEvent2 extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->gamma = $values['gamma'];$this->delta = $values['delta'];$this->epsilon = $values['epsilon']; + ) { + $this->gamma = $values['gamma']; + $this->delta = $values['delta']; + $this->epsilon = $values['epsilon']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/websocket/src/Realtime/ReceiveEvent3.php b/seed/php-model/websocket/src/Realtime/ReceiveEvent3.php index a778b5a18a3d..fc383103b98f 100644 --- a/seed/php-model/websocket/src/Realtime/ReceiveEvent3.php +++ b/seed/php-model/websocket/src/Realtime/ReceiveEvent3.php @@ -20,15 +20,15 @@ class ReceiveEvent3 extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->receiveText3 = $values['receiveText3']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/websocket/src/Realtime/ReceiveSnakeCase.php b/seed/php-model/websocket/src/Realtime/ReceiveSnakeCase.php index 6624b7339033..65f20aaa5d52 100644 --- a/seed/php-model/websocket/src/Realtime/ReceiveSnakeCase.php +++ b/seed/php-model/websocket/src/Realtime/ReceiveSnakeCase.php @@ -27,15 +27,16 @@ class ReceiveSnakeCase extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->receiveText = $values['receiveText'];$this->receiveInt = $values['receiveInt']; + ) { + $this->receiveText = $values['receiveText']; + $this->receiveInt = $values['receiveInt']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/websocket/src/Realtime/SendEvent.php b/seed/php-model/websocket/src/Realtime/SendEvent.php index c278141cc590..f689eb0c1344 100644 --- a/seed/php-model/websocket/src/Realtime/SendEvent.php +++ b/seed/php-model/websocket/src/Realtime/SendEvent.php @@ -27,15 +27,16 @@ class SendEvent extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->sendText = $values['sendText'];$this->sendParam = $values['sendParam']; + ) { + $this->sendText = $values['sendText']; + $this->sendParam = $values['sendParam']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/websocket/src/Realtime/SendEvent2.php b/seed/php-model/websocket/src/Realtime/SendEvent2.php index 030d428f0c73..3393ff022971 100644 --- a/seed/php-model/websocket/src/Realtime/SendEvent2.php +++ b/seed/php-model/websocket/src/Realtime/SendEvent2.php @@ -27,15 +27,16 @@ class SendEvent2 extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->sendText2 = $values['sendText2'];$this->sendParam2 = $values['sendParam2']; + ) { + $this->sendText2 = $values['sendText2']; + $this->sendParam2 = $values['sendParam2']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/websocket/src/Realtime/SendSnakeCase.php b/seed/php-model/websocket/src/Realtime/SendSnakeCase.php index 395055425f78..f7c5cc3c5540 100644 --- a/seed/php-model/websocket/src/Realtime/SendSnakeCase.php +++ b/seed/php-model/websocket/src/Realtime/SendSnakeCase.php @@ -27,15 +27,16 @@ class SendSnakeCase extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->sendText = $values['sendText'];$this->sendParam = $values['sendParam']; + ) { + $this->sendText = $values['sendText']; + $this->sendParam = $values['sendParam']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-model/websocket/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-model/websocket/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-model/websocket/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-model/websocket/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-model/websocket/tests/Core/Json/EnumTest.php b/seed/php-model/websocket/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-model/websocket/tests/Core/Json/EnumTest.php +++ b/seed/php-model/websocket/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-model/websocket/tests/Core/Json/TraitTest.php b/seed/php-model/websocket/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-model/websocket/tests/Core/Json/TraitTest.php +++ b/seed/php-model/websocket/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-model/websocket/tests/Core/Json/UnionPropertyTest.php b/seed/php-model/websocket/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-model/websocket/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-model/websocket/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/accept-header/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/accept-header/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/accept-header/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/accept-header/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/accept-header/src/Core/Client/RawClient.php b/seed/php-sdk/accept-header/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/accept-header/src/Core/Client/RawClient.php +++ b/seed/php-sdk/accept-header/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/accept-header/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/accept-header/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/accept-header/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/accept-header/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/accept-header/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/accept-header/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/accept-header/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/accept-header/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/accept-header/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/accept-header/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/accept-header/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/accept-header/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/accept-header/src/Core/Types/ArrayType.php b/seed/php-sdk/accept-header/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/accept-header/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/accept-header/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/accept-header/src/Core/Types/Constant.php b/seed/php-sdk/accept-header/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/accept-header/src/Core/Types/Constant.php +++ b/seed/php-sdk/accept-header/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/accept-header/src/Core/Types/Union.php b/seed/php-sdk/accept-header/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/accept-header/src/Core/Types/Union.php +++ b/seed/php-sdk/accept-header/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/accept-header/src/Exceptions/SeedApiException.php b/seed/php-sdk/accept-header/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/accept-header/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/accept-header/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/accept-header/src/Exceptions/SeedException.php b/seed/php-sdk/accept-header/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/accept-header/src/Exceptions/SeedException.php +++ b/seed/php-sdk/accept-header/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/accept-header/src/SeedClient.php b/seed/php-sdk/accept-header/src/SeedClient.php index ff14ae3b1286..5f5fab987233 100644 --- a/seed/php-sdk/accept-header/src/SeedClient.php +++ b/seed/php-sdk/accept-header/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var ServiceClient $service @@ -42,8 +42,7 @@ class SeedClient public function __construct( string $token, ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'Authorization' => "Bearer $token", 'X-Fern-Language' => 'PHP', @@ -51,18 +50,18 @@ public function __construct( 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->service = new ServiceClient($this->client, $this->options); } } diff --git a/seed/php-sdk/accept-header/src/Service/ServiceClient.php b/seed/php-sdk/accept-header/src/Service/ServiceClient.php index 0c4cd93a2d3c..1362768979dc 100644 --- a/seed/php-sdk/accept-header/src/Service/ServiceClient.php +++ b/seed/php-sdk/accept-header/src/Service/ServiceClient.php @@ -11,7 +11,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class ServiceClient +class ServiceClient { /** * @var array{ @@ -39,11 +39,10 @@ class ServiceClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -60,7 +59,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function endpoint(?array $options = null): void { + public function endpoint(?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -72,12 +72,12 @@ public function endpoint(?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/accept-header/src/Utils/File.php b/seed/php-sdk/accept-header/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/accept-header/src/Utils/File.php +++ b/seed/php-sdk/accept-header/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/accept-header/tests/Core/Client/RawClientTest.php b/seed/php-sdk/accept-header/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/accept-header/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/accept-header/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/accept-header/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/accept-header/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/accept-header/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/accept-header/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/accept-header/tests/Core/Json/EnumTest.php b/seed/php-sdk/accept-header/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/accept-header/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/accept-header/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/accept-header/tests/Core/Json/TraitTest.php b/seed/php-sdk/accept-header/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/accept-header/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/accept-header/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/accept-header/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/accept-header/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/accept-header/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/accept-header/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/alias-extends/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/alias-extends/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/alias-extends/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/alias-extends/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/alias-extends/src/Core/Client/RawClient.php b/seed/php-sdk/alias-extends/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/alias-extends/src/Core/Client/RawClient.php +++ b/seed/php-sdk/alias-extends/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/alias-extends/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/alias-extends/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/alias-extends/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/alias-extends/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/alias-extends/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/alias-extends/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/alias-extends/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/alias-extends/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/alias-extends/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/alias-extends/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/alias-extends/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/alias-extends/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/alias-extends/src/Core/Types/ArrayType.php b/seed/php-sdk/alias-extends/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/alias-extends/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/alias-extends/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/alias-extends/src/Core/Types/Constant.php b/seed/php-sdk/alias-extends/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/alias-extends/src/Core/Types/Constant.php +++ b/seed/php-sdk/alias-extends/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/alias-extends/src/Core/Types/Union.php b/seed/php-sdk/alias-extends/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/alias-extends/src/Core/Types/Union.php +++ b/seed/php-sdk/alias-extends/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/alias-extends/src/Exceptions/SeedApiException.php b/seed/php-sdk/alias-extends/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/alias-extends/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/alias-extends/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/alias-extends/src/Exceptions/SeedException.php b/seed/php-sdk/alias-extends/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/alias-extends/src/Exceptions/SeedException.php +++ b/seed/php-sdk/alias-extends/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/alias-extends/src/Requests/InlinedChildRequest.php b/seed/php-sdk/alias-extends/src/Requests/InlinedChildRequest.php index d10ba93b340e..619fb63a370e 100644 --- a/seed/php-sdk/alias-extends/src/Requests/InlinedChildRequest.php +++ b/seed/php-sdk/alias-extends/src/Requests/InlinedChildRequest.php @@ -24,8 +24,8 @@ class InlinedChildRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->child = $values['child'];$this->parent = $values['parent']; + ) { + $this->child = $values['child']; + $this->parent = $values['parent']; } } diff --git a/seed/php-sdk/alias-extends/src/SeedClient.php b/seed/php-sdk/alias-extends/src/SeedClient.php index 5131ed7c9a44..886a037ca9b7 100644 --- a/seed/php-sdk/alias-extends/src/SeedClient.php +++ b/seed/php-sdk/alias-extends/src/SeedClient.php @@ -12,7 +12,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class SeedClient +class SeedClient { /** * @var array{ @@ -41,22 +41,21 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); @@ -75,7 +74,8 @@ public function __construct( * @throws SeedException * @throws SeedApiException */ - public function extendedInlineRequestBody(InlinedChildRequest $request, ?array $options = null): void { + public function extendedInlineRequestBody(InlinedChildRequest $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -88,12 +88,12 @@ public function extendedInlineRequestBody(InlinedChildRequest $request, ?array $ $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/alias-extends/src/Traits/AliasType.php b/seed/php-sdk/alias-extends/src/Traits/AliasType.php index 17679092403d..f9b6ed67e282 100644 --- a/seed/php-sdk/alias-extends/src/Traits/AliasType.php +++ b/seed/php-sdk/alias-extends/src/Traits/AliasType.php @@ -7,7 +7,7 @@ /** * @property string $parent */ -trait AliasType +trait AliasType { /** * @var string $parent diff --git a/seed/php-sdk/alias-extends/src/Traits/Parent_.php b/seed/php-sdk/alias-extends/src/Traits/Parent_.php index 31cc4250b25d..b6198ecc686f 100644 --- a/seed/php-sdk/alias-extends/src/Traits/Parent_.php +++ b/seed/php-sdk/alias-extends/src/Traits/Parent_.php @@ -7,7 +7,7 @@ /** * @property string $parent */ -trait Parent_ +trait Parent_ { /** * @var string $parent diff --git a/seed/php-sdk/alias-extends/src/Types/Child.php b/seed/php-sdk/alias-extends/src/Types/Child.php index 170f3a197c9f..aca70872f1ba 100644 --- a/seed/php-sdk/alias-extends/src/Types/Child.php +++ b/seed/php-sdk/alias-extends/src/Types/Child.php @@ -24,15 +24,16 @@ class Child extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->parent = $values['parent'];$this->child = $values['child']; + ) { + $this->parent = $values['parent']; + $this->child = $values['child']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/alias-extends/src/Types/Parent_.php b/seed/php-sdk/alias-extends/src/Types/Parent_.php index 8fa00bd1a1fe..5ff1412efd59 100644 --- a/seed/php-sdk/alias-extends/src/Types/Parent_.php +++ b/seed/php-sdk/alias-extends/src/Types/Parent_.php @@ -20,15 +20,15 @@ class Parent_ extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->parent = $values['parent']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/alias-extends/src/Utils/File.php b/seed/php-sdk/alias-extends/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/alias-extends/src/Utils/File.php +++ b/seed/php-sdk/alias-extends/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/alias-extends/tests/Core/Client/RawClientTest.php b/seed/php-sdk/alias-extends/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/alias-extends/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/alias-extends/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/alias-extends/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/alias-extends/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/alias-extends/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/alias-extends/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/alias-extends/tests/Core/Json/EnumTest.php b/seed/php-sdk/alias-extends/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/alias-extends/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/alias-extends/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/alias-extends/tests/Core/Json/TraitTest.php b/seed/php-sdk/alias-extends/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/alias-extends/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/alias-extends/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/alias-extends/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/alias-extends/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/alias-extends/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/alias-extends/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/alias/composer-json/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/alias/composer-json/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/alias/composer-json/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/alias/composer-json/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/alias/composer-json/src/Core/Client/RawClient.php b/seed/php-sdk/alias/composer-json/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/alias/composer-json/src/Core/Client/RawClient.php +++ b/seed/php-sdk/alias/composer-json/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/alias/composer-json/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/alias/composer-json/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/alias/composer-json/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/alias/composer-json/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/alias/composer-json/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/alias/composer-json/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/alias/composer-json/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/alias/composer-json/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/alias/composer-json/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/alias/composer-json/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/alias/composer-json/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/alias/composer-json/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/alias/composer-json/src/Core/Types/ArrayType.php b/seed/php-sdk/alias/composer-json/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/alias/composer-json/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/alias/composer-json/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/alias/composer-json/src/Core/Types/Constant.php b/seed/php-sdk/alias/composer-json/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/alias/composer-json/src/Core/Types/Constant.php +++ b/seed/php-sdk/alias/composer-json/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/alias/composer-json/src/Core/Types/Union.php b/seed/php-sdk/alias/composer-json/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/alias/composer-json/src/Core/Types/Union.php +++ b/seed/php-sdk/alias/composer-json/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/alias/composer-json/src/Exceptions/SeedApiException.php b/seed/php-sdk/alias/composer-json/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/alias/composer-json/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/alias/composer-json/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/alias/composer-json/src/Exceptions/SeedException.php b/seed/php-sdk/alias/composer-json/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/alias/composer-json/src/Exceptions/SeedException.php +++ b/seed/php-sdk/alias/composer-json/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/alias/composer-json/src/SeedClient.php b/seed/php-sdk/alias/composer-json/src/SeedClient.php index 908dae1766a5..4e60ecae13e1 100644 --- a/seed/php-sdk/alias/composer-json/src/SeedClient.php +++ b/seed/php-sdk/alias/composer-json/src/SeedClient.php @@ -11,7 +11,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class SeedClient +class SeedClient { /** * @var array{ @@ -40,22 +40,21 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); @@ -74,7 +73,8 @@ public function __construct( * @throws SeedException * @throws SeedApiException */ - public function get(string $typeId, ?array $options = null): void { + public function get(string $typeId, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -86,12 +86,12 @@ public function get(string $typeId, ?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/alias/composer-json/src/Types/Type.php b/seed/php-sdk/alias/composer-json/src/Types/Type.php index fa963a69542d..097bb0328de3 100644 --- a/seed/php-sdk/alias/composer-json/src/Types/Type.php +++ b/seed/php-sdk/alias/composer-json/src/Types/Type.php @@ -30,15 +30,16 @@ class Type extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->name = $values['name']; + ) { + $this->id = $values['id']; + $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/alias/composer-json/src/Utils/File.php b/seed/php-sdk/alias/composer-json/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/alias/composer-json/src/Utils/File.php +++ b/seed/php-sdk/alias/composer-json/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/alias/composer-json/tests/Core/Client/RawClientTest.php b/seed/php-sdk/alias/composer-json/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/alias/composer-json/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/alias/composer-json/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/alias/composer-json/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/alias/composer-json/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/alias/composer-json/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/alias/composer-json/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/alias/composer-json/tests/Core/Json/EnumTest.php b/seed/php-sdk/alias/composer-json/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/alias/composer-json/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/alias/composer-json/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/alias/composer-json/tests/Core/Json/TraitTest.php b/seed/php-sdk/alias/composer-json/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/alias/composer-json/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/alias/composer-json/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/alias/composer-json/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/alias/composer-json/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/alias/composer-json/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/alias/composer-json/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/alias/no-custom-config/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/alias/no-custom-config/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/alias/no-custom-config/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/alias/no-custom-config/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/alias/no-custom-config/src/Core/Client/RawClient.php b/seed/php-sdk/alias/no-custom-config/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/alias/no-custom-config/src/Core/Client/RawClient.php +++ b/seed/php-sdk/alias/no-custom-config/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/alias/no-custom-config/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/alias/no-custom-config/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/alias/no-custom-config/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/alias/no-custom-config/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/alias/no-custom-config/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/alias/no-custom-config/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/alias/no-custom-config/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/alias/no-custom-config/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/alias/no-custom-config/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/alias/no-custom-config/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/alias/no-custom-config/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/alias/no-custom-config/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/alias/no-custom-config/src/Core/Types/ArrayType.php b/seed/php-sdk/alias/no-custom-config/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/alias/no-custom-config/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/alias/no-custom-config/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/alias/no-custom-config/src/Core/Types/Constant.php b/seed/php-sdk/alias/no-custom-config/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/alias/no-custom-config/src/Core/Types/Constant.php +++ b/seed/php-sdk/alias/no-custom-config/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/alias/no-custom-config/src/Core/Types/Union.php b/seed/php-sdk/alias/no-custom-config/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/alias/no-custom-config/src/Core/Types/Union.php +++ b/seed/php-sdk/alias/no-custom-config/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/alias/no-custom-config/src/Exceptions/SeedApiException.php b/seed/php-sdk/alias/no-custom-config/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/alias/no-custom-config/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/alias/no-custom-config/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/alias/no-custom-config/src/Exceptions/SeedException.php b/seed/php-sdk/alias/no-custom-config/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/alias/no-custom-config/src/Exceptions/SeedException.php +++ b/seed/php-sdk/alias/no-custom-config/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/alias/no-custom-config/src/SeedClient.php b/seed/php-sdk/alias/no-custom-config/src/SeedClient.php index 908dae1766a5..4e60ecae13e1 100644 --- a/seed/php-sdk/alias/no-custom-config/src/SeedClient.php +++ b/seed/php-sdk/alias/no-custom-config/src/SeedClient.php @@ -11,7 +11,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class SeedClient +class SeedClient { /** * @var array{ @@ -40,22 +40,21 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); @@ -74,7 +73,8 @@ public function __construct( * @throws SeedException * @throws SeedApiException */ - public function get(string $typeId, ?array $options = null): void { + public function get(string $typeId, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -86,12 +86,12 @@ public function get(string $typeId, ?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/alias/no-custom-config/src/Types/Type.php b/seed/php-sdk/alias/no-custom-config/src/Types/Type.php index fa963a69542d..097bb0328de3 100644 --- a/seed/php-sdk/alias/no-custom-config/src/Types/Type.php +++ b/seed/php-sdk/alias/no-custom-config/src/Types/Type.php @@ -30,15 +30,16 @@ class Type extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->name = $values['name']; + ) { + $this->id = $values['id']; + $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/alias/no-custom-config/src/Utils/File.php b/seed/php-sdk/alias/no-custom-config/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/alias/no-custom-config/src/Utils/File.php +++ b/seed/php-sdk/alias/no-custom-config/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/alias/no-custom-config/tests/Core/Client/RawClientTest.php b/seed/php-sdk/alias/no-custom-config/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/alias/no-custom-config/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/alias/no-custom-config/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/alias/no-custom-config/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/alias/no-custom-config/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/alias/no-custom-config/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/alias/no-custom-config/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/alias/no-custom-config/tests/Core/Json/EnumTest.php b/seed/php-sdk/alias/no-custom-config/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/alias/no-custom-config/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/alias/no-custom-config/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/alias/no-custom-config/tests/Core/Json/TraitTest.php b/seed/php-sdk/alias/no-custom-config/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/alias/no-custom-config/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/alias/no-custom-config/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/alias/no-custom-config/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/alias/no-custom-config/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/alias/no-custom-config/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/alias/no-custom-config/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/any-auth/src/Auth/AuthClient.php b/seed/php-sdk/any-auth/src/Auth/AuthClient.php index 684754285971..d66079e55918 100644 --- a/seed/php-sdk/any-auth/src/Auth/AuthClient.php +++ b/seed/php-sdk/any-auth/src/Auth/AuthClient.php @@ -14,7 +14,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class AuthClient +class AuthClient { /** * @var array{ @@ -42,11 +42,10 @@ class AuthClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -65,7 +64,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getToken(GetTokenRequest $request, ?array $options = null): TokenResponse { + public function getToken(GetTokenRequest $request, ?array $options = null): TokenResponse + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -78,15 +78,15 @@ public function getToken(GetTokenRequest $request, ?array $options = null): Toke $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return TokenResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/any-auth/src/Auth/Requests/GetTokenRequest.php b/seed/php-sdk/any-auth/src/Auth/Requests/GetTokenRequest.php index 4b7e15ee540f..87cdf357e742 100644 --- a/seed/php-sdk/any-auth/src/Auth/Requests/GetTokenRequest.php +++ b/seed/php-sdk/any-auth/src/Auth/Requests/GetTokenRequest.php @@ -48,8 +48,11 @@ class GetTokenRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->clientId = $values['clientId'];$this->clientSecret = $values['clientSecret'];$this->audience = $values['audience'];$this->grantType = $values['grantType'];$this->scope = $values['scope'] ?? null; + ) { + $this->clientId = $values['clientId']; + $this->clientSecret = $values['clientSecret']; + $this->audience = $values['audience']; + $this->grantType = $values['grantType']; + $this->scope = $values['scope'] ?? null; } } diff --git a/seed/php-sdk/any-auth/src/Auth/Types/TokenResponse.php b/seed/php-sdk/any-auth/src/Auth/Types/TokenResponse.php index 2a7c2ec254a1..f5379b9051d4 100644 --- a/seed/php-sdk/any-auth/src/Auth/Types/TokenResponse.php +++ b/seed/php-sdk/any-auth/src/Auth/Types/TokenResponse.php @@ -37,15 +37,17 @@ class TokenResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->accessToken = $values['accessToken'];$this->expiresIn = $values['expiresIn'];$this->refreshToken = $values['refreshToken'] ?? null; + ) { + $this->accessToken = $values['accessToken']; + $this->expiresIn = $values['expiresIn']; + $this->refreshToken = $values['refreshToken'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/any-auth/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/any-auth/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/any-auth/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/any-auth/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/any-auth/src/Core/Client/RawClient.php b/seed/php-sdk/any-auth/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/any-auth/src/Core/Client/RawClient.php +++ b/seed/php-sdk/any-auth/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/any-auth/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/any-auth/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/any-auth/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/any-auth/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/any-auth/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/any-auth/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/any-auth/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/any-auth/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/any-auth/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/any-auth/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/any-auth/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/any-auth/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/any-auth/src/Core/Types/ArrayType.php b/seed/php-sdk/any-auth/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/any-auth/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/any-auth/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/any-auth/src/Core/Types/Constant.php b/seed/php-sdk/any-auth/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/any-auth/src/Core/Types/Constant.php +++ b/seed/php-sdk/any-auth/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/any-auth/src/Core/Types/Union.php b/seed/php-sdk/any-auth/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/any-auth/src/Core/Types/Union.php +++ b/seed/php-sdk/any-auth/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/any-auth/src/Exceptions/SeedApiException.php b/seed/php-sdk/any-auth/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/any-auth/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/any-auth/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/any-auth/src/Exceptions/SeedException.php b/seed/php-sdk/any-auth/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/any-auth/src/Exceptions/SeedException.php +++ b/seed/php-sdk/any-auth/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/any-auth/src/SeedClient.php b/seed/php-sdk/any-auth/src/SeedClient.php index 5520930c29de..bff0a0dd92cc 100644 --- a/seed/php-sdk/any-auth/src/SeedClient.php +++ b/seed/php-sdk/any-auth/src/SeedClient.php @@ -8,7 +8,7 @@ use Seed\Core\Client\RawClient; use Exception; -class SeedClient +class SeedClient { /** * @var AuthClient $auth @@ -51,8 +51,7 @@ public function __construct( ?string $token = null, ?string $apiKey = null, ?array $options = null, - ) - { + ) { $token ??= $this->getFromEnvOrThrow('MY_TOKEN', 'Please pass in token or set the environment variable MY_TOKEN.'); $apiKey ??= $this->getFromEnvOrThrow('MY_API_KEY', 'Please pass in apiKey or set the environment variable MY_API_KEY.'); $defaultHeaders = [ @@ -63,18 +62,18 @@ public function __construct( 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->auth = new AuthClient($this->client, $this->options); $this->user = new UserClient($this->client, $this->options); } @@ -84,7 +83,8 @@ public function __construct( * @param string $message * @return string */ - private function getFromEnvOrThrow(string $env, string $message): string { + private function getFromEnvOrThrow(string $env, string $message): string + { $value = getenv($env); return $value ? (string) $value : throw new Exception($message); } diff --git a/seed/php-sdk/any-auth/src/User/Types/User.php b/seed/php-sdk/any-auth/src/User/Types/User.php index ef498cdc789b..566655f7df2d 100644 --- a/seed/php-sdk/any-auth/src/User/Types/User.php +++ b/seed/php-sdk/any-auth/src/User/Types/User.php @@ -27,15 +27,16 @@ class User extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->name = $values['name']; + ) { + $this->id = $values['id']; + $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/any-auth/src/User/UserClient.php b/seed/php-sdk/any-auth/src/User/UserClient.php index 52004a839e17..f7865627c127 100644 --- a/seed/php-sdk/any-auth/src/User/UserClient.php +++ b/seed/php-sdk/any-auth/src/User/UserClient.php @@ -14,7 +14,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class UserClient +class UserClient { /** * @var array{ @@ -42,11 +42,10 @@ class UserClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -64,7 +63,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function get(?array $options = null): array { + public function get(?array $options = null): array + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -76,15 +76,15 @@ public function get(?array $options = null): array { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, [User::class]); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -115,7 +115,8 @@ public function get(?array $options = null): array { * @throws SeedException * @throws SeedApiException */ - public function getAdmins(?array $options = null): array { + public function getAdmins(?array $options = null): array + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -127,15 +128,15 @@ public function getAdmins(?array $options = null): array { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, [User::class]); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/any-auth/src/Utils/File.php b/seed/php-sdk/any-auth/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/any-auth/src/Utils/File.php +++ b/seed/php-sdk/any-auth/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/any-auth/tests/Core/Client/RawClientTest.php b/seed/php-sdk/any-auth/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/any-auth/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/any-auth/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/any-auth/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/any-auth/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/any-auth/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/any-auth/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/any-auth/tests/Core/Json/EnumTest.php b/seed/php-sdk/any-auth/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/any-auth/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/any-auth/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/any-auth/tests/Core/Json/TraitTest.php b/seed/php-sdk/any-auth/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/any-auth/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/any-auth/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/any-auth/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/any-auth/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/any-auth/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/any-auth/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/api-wide-base-path/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/api-wide-base-path/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/api-wide-base-path/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/api-wide-base-path/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/api-wide-base-path/src/Core/Client/RawClient.php b/seed/php-sdk/api-wide-base-path/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/api-wide-base-path/src/Core/Client/RawClient.php +++ b/seed/php-sdk/api-wide-base-path/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/api-wide-base-path/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/api-wide-base-path/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/api-wide-base-path/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/api-wide-base-path/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/api-wide-base-path/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/api-wide-base-path/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/api-wide-base-path/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/api-wide-base-path/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/api-wide-base-path/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/api-wide-base-path/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/api-wide-base-path/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/api-wide-base-path/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/api-wide-base-path/src/Core/Types/ArrayType.php b/seed/php-sdk/api-wide-base-path/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/api-wide-base-path/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/api-wide-base-path/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/api-wide-base-path/src/Core/Types/Constant.php b/seed/php-sdk/api-wide-base-path/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/api-wide-base-path/src/Core/Types/Constant.php +++ b/seed/php-sdk/api-wide-base-path/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/api-wide-base-path/src/Core/Types/Union.php b/seed/php-sdk/api-wide-base-path/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/api-wide-base-path/src/Core/Types/Union.php +++ b/seed/php-sdk/api-wide-base-path/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/api-wide-base-path/src/Exceptions/SeedApiException.php b/seed/php-sdk/api-wide-base-path/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/api-wide-base-path/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/api-wide-base-path/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/api-wide-base-path/src/Exceptions/SeedException.php b/seed/php-sdk/api-wide-base-path/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/api-wide-base-path/src/Exceptions/SeedException.php +++ b/seed/php-sdk/api-wide-base-path/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/api-wide-base-path/src/SeedClient.php b/seed/php-sdk/api-wide-base-path/src/SeedClient.php index 8d7adcb3b0f2..0fd6a9d1029e 100644 --- a/seed/php-sdk/api-wide-base-path/src/SeedClient.php +++ b/seed/php-sdk/api-wide-base-path/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var ServiceClient $service @@ -40,26 +40,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->service = new ServiceClient($this->client, $this->options); } } diff --git a/seed/php-sdk/api-wide-base-path/src/Service/ServiceClient.php b/seed/php-sdk/api-wide-base-path/src/Service/ServiceClient.php index 2f9c923ec1eb..189c38fd4a69 100644 --- a/seed/php-sdk/api-wide-base-path/src/Service/ServiceClient.php +++ b/seed/php-sdk/api-wide-base-path/src/Service/ServiceClient.php @@ -11,7 +11,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class ServiceClient +class ServiceClient { /** * @var array{ @@ -39,11 +39,10 @@ class ServiceClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -64,7 +63,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function post(string $pathParam, string $serviceParam, int $endpointParam, string $resourceParam, ?array $options = null): void { + public function post(string $pathParam, string $serviceParam, int $endpointParam, string $resourceParam, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -76,12 +76,12 @@ public function post(string $pathParam, string $serviceParam, int $endpointParam $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/api-wide-base-path/src/Utils/File.php b/seed/php-sdk/api-wide-base-path/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/api-wide-base-path/src/Utils/File.php +++ b/seed/php-sdk/api-wide-base-path/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/api-wide-base-path/tests/Core/Client/RawClientTest.php b/seed/php-sdk/api-wide-base-path/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/api-wide-base-path/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/api-wide-base-path/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/api-wide-base-path/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/api-wide-base-path/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/api-wide-base-path/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/api-wide-base-path/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/api-wide-base-path/tests/Core/Json/EnumTest.php b/seed/php-sdk/api-wide-base-path/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/api-wide-base-path/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/api-wide-base-path/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/api-wide-base-path/tests/Core/Json/TraitTest.php b/seed/php-sdk/api-wide-base-path/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/api-wide-base-path/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/api-wide-base-path/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/api-wide-base-path/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/api-wide-base-path/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/api-wide-base-path/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/api-wide-base-path/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/audiences/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/audiences/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/audiences/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/audiences/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/audiences/src/Core/Client/RawClient.php b/seed/php-sdk/audiences/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/audiences/src/Core/Client/RawClient.php +++ b/seed/php-sdk/audiences/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/audiences/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/audiences/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/audiences/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/audiences/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/audiences/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/audiences/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/audiences/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/audiences/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/audiences/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/audiences/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/audiences/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/audiences/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/audiences/src/Core/Types/ArrayType.php b/seed/php-sdk/audiences/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/audiences/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/audiences/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/audiences/src/Core/Types/Constant.php b/seed/php-sdk/audiences/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/audiences/src/Core/Types/Constant.php +++ b/seed/php-sdk/audiences/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/audiences/src/Core/Types/Union.php b/seed/php-sdk/audiences/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/audiences/src/Core/Types/Union.php +++ b/seed/php-sdk/audiences/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/audiences/src/Environments.php b/seed/php-sdk/audiences/src/Environments.php index 1f9bf09a2841..6178a46c5819 100644 --- a/seed/php-sdk/audiences/src/Environments.php +++ b/seed/php-sdk/audiences/src/Environments.php @@ -2,8 +2,8 @@ namespace Seed; -enum Environments - : string { +enum Environments: string +{ case EnvironmentA = "https://api.example.a.com"; case EnvironmentB = "https://api.example.b.com"; } diff --git a/seed/php-sdk/audiences/src/Exceptions/SeedApiException.php b/seed/php-sdk/audiences/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/audiences/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/audiences/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/audiences/src/Exceptions/SeedException.php b/seed/php-sdk/audiences/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/audiences/src/Exceptions/SeedException.php +++ b/seed/php-sdk/audiences/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/audiences/src/FolderA/FolderAClient.php b/seed/php-sdk/audiences/src/FolderA/FolderAClient.php index 82be40ef3942..95005f3a386b 100644 --- a/seed/php-sdk/audiences/src/FolderA/FolderAClient.php +++ b/seed/php-sdk/audiences/src/FolderA/FolderAClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class FolderAClient +class FolderAClient { /** * @var ServiceClient $service @@ -39,11 +39,10 @@ class FolderAClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; $this->service = new ServiceClient($this->client, $this->options); diff --git a/seed/php-sdk/audiences/src/FolderA/Service/Requests/GetDirectThreadRequest.php b/seed/php-sdk/audiences/src/FolderA/Service/Requests/GetDirectThreadRequest.php index a051901fd510..ef1cb3d0bed1 100644 --- a/seed/php-sdk/audiences/src/FolderA/Service/Requests/GetDirectThreadRequest.php +++ b/seed/php-sdk/audiences/src/FolderA/Service/Requests/GetDirectThreadRequest.php @@ -24,8 +24,8 @@ class GetDirectThreadRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->ids = $values['ids'];$this->tags = $values['tags']; + ) { + $this->ids = $values['ids']; + $this->tags = $values['tags']; } } diff --git a/seed/php-sdk/audiences/src/FolderA/Service/ServiceClient.php b/seed/php-sdk/audiences/src/FolderA/Service/ServiceClient.php index 31cd9c993c35..535e6b9ab382 100644 --- a/seed/php-sdk/audiences/src/FolderA/Service/ServiceClient.php +++ b/seed/php-sdk/audiences/src/FolderA/Service/ServiceClient.php @@ -14,7 +14,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class ServiceClient +class ServiceClient { /** * @var array{ @@ -42,11 +42,10 @@ class ServiceClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -65,7 +64,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getDirectThread(GetDirectThreadRequest $request, ?array $options = null): Response { + public function getDirectThread(GetDirectThreadRequest $request, ?array $options = null): Response + { $options = array_merge($this->options, $options ?? []); $query = []; $query['ids'] = $request->ids; @@ -81,15 +81,15 @@ public function getDirectThread(GetDirectThreadRequest $request, ?array $options $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return Response::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/audiences/src/FolderA/Service/Types/Response.php b/seed/php-sdk/audiences/src/FolderA/Service/Types/Response.php index da119898dc2f..580d1b21a907 100644 --- a/seed/php-sdk/audiences/src/FolderA/Service/Types/Response.php +++ b/seed/php-sdk/audiences/src/FolderA/Service/Types/Response.php @@ -21,15 +21,15 @@ class Response extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->foo = $values['foo'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/audiences/src/FolderB/Common/Types/Foo.php b/seed/php-sdk/audiences/src/FolderB/Common/Types/Foo.php index 38bf554d8894..286a3eb7dfbe 100644 --- a/seed/php-sdk/audiences/src/FolderB/Common/Types/Foo.php +++ b/seed/php-sdk/audiences/src/FolderB/Common/Types/Foo.php @@ -21,15 +21,15 @@ class Foo extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->foo = $values['foo'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/audiences/src/FolderC/Common/Types/FolderCFoo.php b/seed/php-sdk/audiences/src/FolderC/Common/Types/FolderCFoo.php index 56373a4fe588..d5c1cc0d7d57 100644 --- a/seed/php-sdk/audiences/src/FolderC/Common/Types/FolderCFoo.php +++ b/seed/php-sdk/audiences/src/FolderC/Common/Types/FolderCFoo.php @@ -20,15 +20,15 @@ class FolderCFoo extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->barProperty = $values['barProperty']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/audiences/src/FolderD/FolderDClient.php b/seed/php-sdk/audiences/src/FolderD/FolderDClient.php index 03b9521a0527..5d660303871e 100644 --- a/seed/php-sdk/audiences/src/FolderD/FolderDClient.php +++ b/seed/php-sdk/audiences/src/FolderD/FolderDClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class FolderDClient +class FolderDClient { /** * @var ServiceClient $service @@ -39,11 +39,10 @@ class FolderDClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; $this->service = new ServiceClient($this->client, $this->options); diff --git a/seed/php-sdk/audiences/src/FolderD/Service/ServiceClient.php b/seed/php-sdk/audiences/src/FolderD/Service/ServiceClient.php index 938d1d1ebf00..684579864f48 100644 --- a/seed/php-sdk/audiences/src/FolderD/Service/ServiceClient.php +++ b/seed/php-sdk/audiences/src/FolderD/Service/ServiceClient.php @@ -13,7 +13,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class ServiceClient +class ServiceClient { /** * @var array{ @@ -41,11 +41,10 @@ class ServiceClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -63,7 +62,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getDirectThread(?array $options = null): Response { + public function getDirectThread(?array $options = null): Response + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -75,15 +75,15 @@ public function getDirectThread(?array $options = null): Response { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return Response::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/audiences/src/FolderD/Service/Types/Response.php b/seed/php-sdk/audiences/src/FolderD/Service/Types/Response.php index af8e6daa9c90..18ab956c7766 100644 --- a/seed/php-sdk/audiences/src/FolderD/Service/Types/Response.php +++ b/seed/php-sdk/audiences/src/FolderD/Service/Types/Response.php @@ -20,15 +20,15 @@ class Response extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->foo = $values['foo']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/audiences/src/Foo/FooClient.php b/seed/php-sdk/audiences/src/Foo/FooClient.php index 433f96c53a54..c92f249361f9 100644 --- a/seed/php-sdk/audiences/src/Foo/FooClient.php +++ b/seed/php-sdk/audiences/src/Foo/FooClient.php @@ -14,7 +14,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class FooClient +class FooClient { /** * @var array{ @@ -42,11 +42,10 @@ class FooClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -65,10 +64,11 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function find(FindRequest $request = new FindRequest(), ?array $options = null): ImportingType { + public function find(FindRequest $request = new FindRequest(), ?array $options = null): ImportingType + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->optionalString != null){ + if ($request->optionalString != null) { $query['optionalString'] = $request->optionalString; } try { @@ -83,15 +83,15 @@ public function find(FindRequest $request = new FindRequest(), ?array $options = $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ImportingType::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/audiences/src/Foo/Requests/FindRequest.php b/seed/php-sdk/audiences/src/Foo/Requests/FindRequest.php index 170192955723..150b59404e94 100644 --- a/seed/php-sdk/audiences/src/Foo/Requests/FindRequest.php +++ b/seed/php-sdk/audiences/src/Foo/Requests/FindRequest.php @@ -33,8 +33,9 @@ class FindRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->optionalString = $values['optionalString'] ?? null;$this->publicProperty = $values['publicProperty'] ?? null;$this->privateProperty = $values['privateProperty'] ?? null; + ) { + $this->optionalString = $values['optionalString'] ?? null; + $this->publicProperty = $values['publicProperty'] ?? null; + $this->privateProperty = $values['privateProperty'] ?? null; } } diff --git a/seed/php-sdk/audiences/src/Foo/Types/FilteredType.php b/seed/php-sdk/audiences/src/Foo/Types/FilteredType.php index a704f7286cec..0f20fe66197c 100644 --- a/seed/php-sdk/audiences/src/Foo/Types/FilteredType.php +++ b/seed/php-sdk/audiences/src/Foo/Types/FilteredType.php @@ -27,15 +27,16 @@ class FilteredType extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->publicProperty = $values['publicProperty'] ?? null;$this->privateProperty = $values['privateProperty']; + ) { + $this->publicProperty = $values['publicProperty'] ?? null; + $this->privateProperty = $values['privateProperty']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/audiences/src/Foo/Types/ImportingType.php b/seed/php-sdk/audiences/src/Foo/Types/ImportingType.php index 882a9c285d5c..4a3f6d2f556b 100644 --- a/seed/php-sdk/audiences/src/Foo/Types/ImportingType.php +++ b/seed/php-sdk/audiences/src/Foo/Types/ImportingType.php @@ -20,15 +20,15 @@ class ImportingType extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->imported = $values['imported']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/audiences/src/SeedClient.php b/seed/php-sdk/audiences/src/SeedClient.php index 739c8eb49a03..3cbaf6ab067b 100644 --- a/seed/php-sdk/audiences/src/SeedClient.php +++ b/seed/php-sdk/audiences/src/SeedClient.php @@ -8,7 +8,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var FolderAClient $folderA @@ -52,26 +52,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->folderA = new FolderAClient($this->client, $this->options); $this->folderD = new FolderDClient($this->client, $this->options); $this->foo = new FooClient($this->client, $this->options); diff --git a/seed/php-sdk/audiences/src/Utils/File.php b/seed/php-sdk/audiences/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/audiences/src/Utils/File.php +++ b/seed/php-sdk/audiences/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/audiences/tests/Core/Client/RawClientTest.php b/seed/php-sdk/audiences/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/audiences/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/audiences/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/audiences/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/audiences/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/audiences/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/audiences/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/audiences/tests/Core/Json/EnumTest.php b/seed/php-sdk/audiences/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/audiences/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/audiences/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/audiences/tests/Core/Json/TraitTest.php b/seed/php-sdk/audiences/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/audiences/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/audiences/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/audiences/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/audiences/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/audiences/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/audiences/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/basic-auth-environment-variables/src/BasicAuth/BasicAuthClient.php b/seed/php-sdk/basic-auth-environment-variables/src/BasicAuth/BasicAuthClient.php index 80b59388ea78..a8ed4e8ab3a3 100644 --- a/seed/php-sdk/basic-auth-environment-variables/src/BasicAuth/BasicAuthClient.php +++ b/seed/php-sdk/basic-auth-environment-variables/src/BasicAuth/BasicAuthClient.php @@ -13,7 +13,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class BasicAuthClient +class BasicAuthClient { /** * @var array{ @@ -41,11 +41,10 @@ class BasicAuthClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -65,7 +64,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getWithBasicAuth(?array $options = null): bool { + public function getWithBasicAuth(?array $options = null): bool + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -77,15 +77,15 @@ public function getWithBasicAuth(?array $options = null): bool { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeBool($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -119,7 +119,8 @@ public function getWithBasicAuth(?array $options = null): bool { * @throws SeedException * @throws SeedApiException */ - public function postWithBasicAuth(mixed $request, ?array $options = null): bool { + public function postWithBasicAuth(mixed $request, ?array $options = null): bool + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -132,15 +133,15 @@ public function postWithBasicAuth(mixed $request, ?array $options = null): bool $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeBool($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/basic-auth-environment-variables/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/basic-auth-environment-variables/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/basic-auth-environment-variables/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/basic-auth-environment-variables/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/basic-auth-environment-variables/src/Core/Client/RawClient.php b/seed/php-sdk/basic-auth-environment-variables/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/basic-auth-environment-variables/src/Core/Client/RawClient.php +++ b/seed/php-sdk/basic-auth-environment-variables/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/basic-auth-environment-variables/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/basic-auth-environment-variables/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/basic-auth-environment-variables/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/basic-auth-environment-variables/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/basic-auth-environment-variables/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/basic-auth-environment-variables/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/basic-auth-environment-variables/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/basic-auth-environment-variables/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/basic-auth-environment-variables/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/basic-auth-environment-variables/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/basic-auth-environment-variables/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/basic-auth-environment-variables/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/basic-auth-environment-variables/src/Core/Types/ArrayType.php b/seed/php-sdk/basic-auth-environment-variables/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/basic-auth-environment-variables/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/basic-auth-environment-variables/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/basic-auth-environment-variables/src/Core/Types/Constant.php b/seed/php-sdk/basic-auth-environment-variables/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/basic-auth-environment-variables/src/Core/Types/Constant.php +++ b/seed/php-sdk/basic-auth-environment-variables/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/basic-auth-environment-variables/src/Core/Types/Union.php b/seed/php-sdk/basic-auth-environment-variables/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/basic-auth-environment-variables/src/Core/Types/Union.php +++ b/seed/php-sdk/basic-auth-environment-variables/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/basic-auth-environment-variables/src/Errors/Types/UnauthorizedRequestErrorBody.php b/seed/php-sdk/basic-auth-environment-variables/src/Errors/Types/UnauthorizedRequestErrorBody.php index 0eac184d9204..131d5f01b080 100644 --- a/seed/php-sdk/basic-auth-environment-variables/src/Errors/Types/UnauthorizedRequestErrorBody.php +++ b/seed/php-sdk/basic-auth-environment-variables/src/Errors/Types/UnauthorizedRequestErrorBody.php @@ -20,15 +20,15 @@ class UnauthorizedRequestErrorBody extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->message = $values['message']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/basic-auth-environment-variables/src/Exceptions/SeedApiException.php b/seed/php-sdk/basic-auth-environment-variables/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/basic-auth-environment-variables/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/basic-auth-environment-variables/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/basic-auth-environment-variables/src/Exceptions/SeedException.php b/seed/php-sdk/basic-auth-environment-variables/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/basic-auth-environment-variables/src/Exceptions/SeedException.php +++ b/seed/php-sdk/basic-auth-environment-variables/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/basic-auth-environment-variables/src/SeedClient.php b/seed/php-sdk/basic-auth-environment-variables/src/SeedClient.php index fed8a426092d..92ea08d9fa9b 100644 --- a/seed/php-sdk/basic-auth-environment-variables/src/SeedClient.php +++ b/seed/php-sdk/basic-auth-environment-variables/src/SeedClient.php @@ -7,7 +7,7 @@ use Seed\Core\Client\RawClient; use Exception; -class SeedClient +class SeedClient { /** * @var BasicAuthClient $basicAuth @@ -45,8 +45,7 @@ public function __construct( ?string $username = null, ?string $accessToken = null, ?array $options = null, - ) - { + ) { $username ??= $this->getFromEnvOrThrow('USERNAME', 'Please pass in username or set the environment variable USERNAME.'); $accessToken ??= $this->getFromEnvOrThrow('PASSWORD', 'Please pass in accessToken or set the environment variable PASSWORD.'); $defaultHeaders = [ @@ -55,18 +54,18 @@ public function __construct( 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->basicAuth = new BasicAuthClient($this->client, $this->options); } @@ -75,7 +74,8 @@ public function __construct( * @param string $message * @return string */ - private function getFromEnvOrThrow(string $env, string $message): string { + private function getFromEnvOrThrow(string $env, string $message): string + { $value = getenv($env); return $value ? (string) $value : throw new Exception($message); } diff --git a/seed/php-sdk/basic-auth-environment-variables/src/Utils/File.php b/seed/php-sdk/basic-auth-environment-variables/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/basic-auth-environment-variables/src/Utils/File.php +++ b/seed/php-sdk/basic-auth-environment-variables/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/basic-auth-environment-variables/tests/Core/Client/RawClientTest.php b/seed/php-sdk/basic-auth-environment-variables/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/basic-auth-environment-variables/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/basic-auth-environment-variables/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/basic-auth-environment-variables/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/basic-auth-environment-variables/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/basic-auth-environment-variables/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/basic-auth-environment-variables/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/basic-auth-environment-variables/tests/Core/Json/EnumTest.php b/seed/php-sdk/basic-auth-environment-variables/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/basic-auth-environment-variables/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/basic-auth-environment-variables/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/basic-auth-environment-variables/tests/Core/Json/TraitTest.php b/seed/php-sdk/basic-auth-environment-variables/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/basic-auth-environment-variables/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/basic-auth-environment-variables/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/basic-auth-environment-variables/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/basic-auth-environment-variables/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/basic-auth-environment-variables/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/basic-auth-environment-variables/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/basic-auth/src/BasicAuth/BasicAuthClient.php b/seed/php-sdk/basic-auth/src/BasicAuth/BasicAuthClient.php index 80b59388ea78..a8ed4e8ab3a3 100644 --- a/seed/php-sdk/basic-auth/src/BasicAuth/BasicAuthClient.php +++ b/seed/php-sdk/basic-auth/src/BasicAuth/BasicAuthClient.php @@ -13,7 +13,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class BasicAuthClient +class BasicAuthClient { /** * @var array{ @@ -41,11 +41,10 @@ class BasicAuthClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -65,7 +64,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getWithBasicAuth(?array $options = null): bool { + public function getWithBasicAuth(?array $options = null): bool + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -77,15 +77,15 @@ public function getWithBasicAuth(?array $options = null): bool { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeBool($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -119,7 +119,8 @@ public function getWithBasicAuth(?array $options = null): bool { * @throws SeedException * @throws SeedApiException */ - public function postWithBasicAuth(mixed $request, ?array $options = null): bool { + public function postWithBasicAuth(mixed $request, ?array $options = null): bool + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -132,15 +133,15 @@ public function postWithBasicAuth(mixed $request, ?array $options = null): bool $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeBool($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/basic-auth/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/basic-auth/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/basic-auth/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/basic-auth/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/basic-auth/src/Core/Client/RawClient.php b/seed/php-sdk/basic-auth/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/basic-auth/src/Core/Client/RawClient.php +++ b/seed/php-sdk/basic-auth/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/basic-auth/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/basic-auth/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/basic-auth/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/basic-auth/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/basic-auth/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/basic-auth/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/basic-auth/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/basic-auth/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/basic-auth/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/basic-auth/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/basic-auth/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/basic-auth/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/basic-auth/src/Core/Types/ArrayType.php b/seed/php-sdk/basic-auth/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/basic-auth/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/basic-auth/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/basic-auth/src/Core/Types/Constant.php b/seed/php-sdk/basic-auth/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/basic-auth/src/Core/Types/Constant.php +++ b/seed/php-sdk/basic-auth/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/basic-auth/src/Core/Types/Union.php b/seed/php-sdk/basic-auth/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/basic-auth/src/Core/Types/Union.php +++ b/seed/php-sdk/basic-auth/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/basic-auth/src/Errors/Types/UnauthorizedRequestErrorBody.php b/seed/php-sdk/basic-auth/src/Errors/Types/UnauthorizedRequestErrorBody.php index 0eac184d9204..131d5f01b080 100644 --- a/seed/php-sdk/basic-auth/src/Errors/Types/UnauthorizedRequestErrorBody.php +++ b/seed/php-sdk/basic-auth/src/Errors/Types/UnauthorizedRequestErrorBody.php @@ -20,15 +20,15 @@ class UnauthorizedRequestErrorBody extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->message = $values['message']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/basic-auth/src/Exceptions/SeedApiException.php b/seed/php-sdk/basic-auth/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/basic-auth/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/basic-auth/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/basic-auth/src/Exceptions/SeedException.php b/seed/php-sdk/basic-auth/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/basic-auth/src/Exceptions/SeedException.php +++ b/seed/php-sdk/basic-auth/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/basic-auth/src/SeedClient.php b/seed/php-sdk/basic-auth/src/SeedClient.php index c2c21a9975f0..1690ed183e36 100644 --- a/seed/php-sdk/basic-auth/src/SeedClient.php +++ b/seed/php-sdk/basic-auth/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var BasicAuthClient $basicAuth @@ -44,26 +44,25 @@ public function __construct( string $username, string $password, ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->basicAuth = new BasicAuthClient($this->client, $this->options); } } diff --git a/seed/php-sdk/basic-auth/src/Utils/File.php b/seed/php-sdk/basic-auth/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/basic-auth/src/Utils/File.php +++ b/seed/php-sdk/basic-auth/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/basic-auth/tests/Core/Client/RawClientTest.php b/seed/php-sdk/basic-auth/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/basic-auth/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/basic-auth/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/basic-auth/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/basic-auth/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/basic-auth/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/basic-auth/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/basic-auth/tests/Core/Json/EnumTest.php b/seed/php-sdk/basic-auth/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/basic-auth/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/basic-auth/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/basic-auth/tests/Core/Json/TraitTest.php b/seed/php-sdk/basic-auth/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/basic-auth/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/basic-auth/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/basic-auth/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/basic-auth/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/basic-auth/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/basic-auth/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/bytes-download/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/bytes-download/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/bytes-download/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/bytes-download/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/bytes-download/src/Core/Client/RawClient.php b/seed/php-sdk/bytes-download/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/bytes-download/src/Core/Client/RawClient.php +++ b/seed/php-sdk/bytes-download/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/bytes-download/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/bytes-download/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/bytes-download/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/bytes-download/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/bytes-download/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/bytes-download/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/bytes-download/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/bytes-download/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/bytes-download/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/bytes-download/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/bytes-download/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/bytes-download/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/bytes-download/src/Core/Types/ArrayType.php b/seed/php-sdk/bytes-download/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/bytes-download/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/bytes-download/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/bytes-download/src/Core/Types/Constant.php b/seed/php-sdk/bytes-download/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/bytes-download/src/Core/Types/Constant.php +++ b/seed/php-sdk/bytes-download/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/bytes-download/src/Core/Types/Union.php b/seed/php-sdk/bytes-download/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/bytes-download/src/Core/Types/Union.php +++ b/seed/php-sdk/bytes-download/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/bytes-download/src/Exceptions/SeedApiException.php b/seed/php-sdk/bytes-download/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/bytes-download/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/bytes-download/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/bytes-download/src/Exceptions/SeedException.php b/seed/php-sdk/bytes-download/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/bytes-download/src/Exceptions/SeedException.php +++ b/seed/php-sdk/bytes-download/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/bytes-download/src/SeedClient.php b/seed/php-sdk/bytes-download/src/SeedClient.php index 8d7adcb3b0f2..0fd6a9d1029e 100644 --- a/seed/php-sdk/bytes-download/src/SeedClient.php +++ b/seed/php-sdk/bytes-download/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var ServiceClient $service @@ -40,26 +40,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->service = new ServiceClient($this->client, $this->options); } } diff --git a/seed/php-sdk/bytes-download/src/Service/ServiceClient.php b/seed/php-sdk/bytes-download/src/Service/ServiceClient.php index 00d9de6ed35a..da876c0d7b9b 100644 --- a/seed/php-sdk/bytes-download/src/Service/ServiceClient.php +++ b/seed/php-sdk/bytes-download/src/Service/ServiceClient.php @@ -11,7 +11,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class ServiceClient +class ServiceClient { /** * @var array{ @@ -39,11 +39,10 @@ class ServiceClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -60,7 +59,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function simple(?array $options = null): void { + public function simple(?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -72,12 +72,12 @@ public function simple(?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -108,7 +108,8 @@ public function simple(?array $options = null): void { * @throws SeedException * @throws SeedApiException */ - public function download(string $id, ?array $options = null): void { + public function download(string $id, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -122,7 +123,7 @@ public function download(string $id, ?array $options = null): void { $statusCode = $response->getStatusCode(); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/bytes-download/src/Utils/File.php b/seed/php-sdk/bytes-download/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/bytes-download/src/Utils/File.php +++ b/seed/php-sdk/bytes-download/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/bytes-download/tests/Core/Client/RawClientTest.php b/seed/php-sdk/bytes-download/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/bytes-download/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/bytes-download/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/bytes-download/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/bytes-download/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/bytes-download/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/bytes-download/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/bytes-download/tests/Core/Json/EnumTest.php b/seed/php-sdk/bytes-download/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/bytes-download/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/bytes-download/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/bytes-download/tests/Core/Json/TraitTest.php b/seed/php-sdk/bytes-download/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/bytes-download/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/bytes-download/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/bytes-download/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/bytes-download/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/bytes-download/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/bytes-download/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/bytes-upload/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/bytes-upload/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/bytes-upload/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/bytes-upload/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/bytes-upload/src/Core/Client/RawClient.php b/seed/php-sdk/bytes-upload/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/bytes-upload/src/Core/Client/RawClient.php +++ b/seed/php-sdk/bytes-upload/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/bytes-upload/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/bytes-upload/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/bytes-upload/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/bytes-upload/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/bytes-upload/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/bytes-upload/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/bytes-upload/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/bytes-upload/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/bytes-upload/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/bytes-upload/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/bytes-upload/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/bytes-upload/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/bytes-upload/src/Core/Types/ArrayType.php b/seed/php-sdk/bytes-upload/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/bytes-upload/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/bytes-upload/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/bytes-upload/src/Core/Types/Constant.php b/seed/php-sdk/bytes-upload/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/bytes-upload/src/Core/Types/Constant.php +++ b/seed/php-sdk/bytes-upload/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/bytes-upload/src/Core/Types/Union.php b/seed/php-sdk/bytes-upload/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/bytes-upload/src/Core/Types/Union.php +++ b/seed/php-sdk/bytes-upload/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/bytes-upload/src/Exceptions/SeedApiException.php b/seed/php-sdk/bytes-upload/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/bytes-upload/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/bytes-upload/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/bytes-upload/src/Exceptions/SeedException.php b/seed/php-sdk/bytes-upload/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/bytes-upload/src/Exceptions/SeedException.php +++ b/seed/php-sdk/bytes-upload/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/bytes-upload/src/SeedClient.php b/seed/php-sdk/bytes-upload/src/SeedClient.php index 8d7adcb3b0f2..0fd6a9d1029e 100644 --- a/seed/php-sdk/bytes-upload/src/SeedClient.php +++ b/seed/php-sdk/bytes-upload/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var ServiceClient $service @@ -40,26 +40,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->service = new ServiceClient($this->client, $this->options); } } diff --git a/seed/php-sdk/bytes-upload/src/Service/ServiceClient.php b/seed/php-sdk/bytes-upload/src/Service/ServiceClient.php index 6c36f99459d6..1507bc893fca 100644 --- a/seed/php-sdk/bytes-upload/src/Service/ServiceClient.php +++ b/seed/php-sdk/bytes-upload/src/Service/ServiceClient.php @@ -11,7 +11,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class ServiceClient +class ServiceClient { /** * @var array{ @@ -39,11 +39,10 @@ class ServiceClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -60,7 +59,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function upload(?array $options = null): void { + public function upload(?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -72,12 +72,12 @@ public function upload(?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/bytes-upload/src/Utils/File.php b/seed/php-sdk/bytes-upload/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/bytes-upload/src/Utils/File.php +++ b/seed/php-sdk/bytes-upload/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/bytes-upload/tests/Core/Client/RawClientTest.php b/seed/php-sdk/bytes-upload/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/bytes-upload/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/bytes-upload/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/bytes-upload/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/bytes-upload/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/bytes-upload/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/bytes-upload/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/bytes-upload/tests/Core/Json/EnumTest.php b/seed/php-sdk/bytes-upload/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/bytes-upload/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/bytes-upload/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/bytes-upload/tests/Core/Json/TraitTest.php b/seed/php-sdk/bytes-upload/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/bytes-upload/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/bytes-upload/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/bytes-upload/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/bytes-upload/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/bytes-upload/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/bytes-upload/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/circular-references-advanced/src/A/Types/A.php b/seed/php-sdk/circular-references-advanced/src/A/Types/A.php index 8065c13abdb0..31a4c8ba9e4f 100644 --- a/seed/php-sdk/circular-references-advanced/src/A/Types/A.php +++ b/seed/php-sdk/circular-references-advanced/src/A/Types/A.php @@ -17,15 +17,15 @@ class A extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->s = $values['s']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/circular-references-advanced/src/Ast/Traits/Berry.php b/seed/php-sdk/circular-references-advanced/src/Ast/Traits/Berry.php index 0e9565262ec6..87fff5f9a706 100644 --- a/seed/php-sdk/circular-references-advanced/src/Ast/Traits/Berry.php +++ b/seed/php-sdk/circular-references-advanced/src/Ast/Traits/Berry.php @@ -13,7 +13,7 @@ * |Dog * ) $animal */ -trait Berry +trait Berry { /** * @var ( @@ -21,6 +21,6 @@ trait Berry * |Dog * ) $animal */ - #[JsonProperty('animal'), Union(Cat::class,Dog::class)] + #[JsonProperty('animal'), Union(Cat::class, Dog::class)] public Cat|Dog $animal; } diff --git a/seed/php-sdk/circular-references-advanced/src/Ast/Types/Acai.php b/seed/php-sdk/circular-references-advanced/src/Ast/Types/Acai.php index e0a5b8797897..5dd3a96c023c 100644 --- a/seed/php-sdk/circular-references-advanced/src/Ast/Types/Acai.php +++ b/seed/php-sdk/circular-references-advanced/src/Ast/Types/Acai.php @@ -20,15 +20,15 @@ class Acai extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->animal = $values['animal']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/circular-references-advanced/src/Ast/Types/Berry.php b/seed/php-sdk/circular-references-advanced/src/Ast/Types/Berry.php index 12059c112bf7..6181660ff850 100644 --- a/seed/php-sdk/circular-references-advanced/src/Ast/Types/Berry.php +++ b/seed/php-sdk/circular-references-advanced/src/Ast/Types/Berry.php @@ -14,7 +14,7 @@ class Berry extends JsonSerializableType * |Dog * ) $animal */ - #[JsonProperty('animal'), Union(Cat::class,Dog::class)] + #[JsonProperty('animal'), Union(Cat::class, Dog::class)] public Cat|Dog $animal; /** @@ -27,15 +27,15 @@ class Berry extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->animal = $values['animal']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/circular-references-advanced/src/Ast/Types/BranchNode.php b/seed/php-sdk/circular-references-advanced/src/Ast/Types/BranchNode.php index c5452c10e383..73b993d107c2 100644 --- a/seed/php-sdk/circular-references-advanced/src/Ast/Types/BranchNode.php +++ b/seed/php-sdk/circular-references-advanced/src/Ast/Types/BranchNode.php @@ -28,15 +28,15 @@ class BranchNode extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->children = $values['children']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/circular-references-advanced/src/Ast/Types/Cat.php b/seed/php-sdk/circular-references-advanced/src/Ast/Types/Cat.php index ad02bda82749..cfc036e783b5 100644 --- a/seed/php-sdk/circular-references-advanced/src/Ast/Types/Cat.php +++ b/seed/php-sdk/circular-references-advanced/src/Ast/Types/Cat.php @@ -14,7 +14,7 @@ class Cat extends JsonSerializableType * |Fig * ) $fruit */ - #[JsonProperty('fruit'), Union(Acai::class,Fig::class)] + #[JsonProperty('fruit'), Union(Acai::class, Fig::class)] public Acai|Fig $fruit; /** @@ -27,15 +27,15 @@ class Cat extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->fruit = $values['fruit']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/circular-references-advanced/src/Ast/Types/ContainerValue.php b/seed/php-sdk/circular-references-advanced/src/Ast/Types/ContainerValue.php index 3a6d27c0ccd5..736e71012554 100644 --- a/seed/php-sdk/circular-references-advanced/src/Ast/Types/ContainerValue.php +++ b/seed/php-sdk/circular-references-advanced/src/Ast/Types/ContainerValue.php @@ -44,16 +44,17 @@ class ContainerValue extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param array $list * @return ContainerValue */ - public static function list(array $list): ContainerValue { + public static function list(array $list): ContainerValue + { return new ContainerValue([ 'type' => 'list', 'value' => $list, @@ -64,7 +65,8 @@ public static function list(array $list): ContainerValue { * @param ?FieldValue $optional * @return ContainerValue */ - public static function optional(?FieldValue $optional = null): ContainerValue { + public static function optional(?FieldValue $optional = null): ContainerValue + { return new ContainerValue([ 'type' => 'optional', 'value' => $optional, @@ -74,94 +76,101 @@ public static function optional(?FieldValue $optional = null): ContainerValue { /** * @return bool */ - public function isList_(): bool { - return is_array($this->value)&& $this->type === 'list'; + public function isList_(): bool + { + return is_array($this->value) && $this->type === 'list'; } /** * @return array */ - public function asList_(): array { - if (!(is_array($this->value)&& $this->type === 'list')){ + public function asList_(): array + { + if (!(is_array($this->value) && $this->type === 'list')) { throw new Exception( "Expected list; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isOptional(): bool { - return (is_null($this->value) || $this->value instanceof FieldValue)&& $this->type === 'optional'; + public function isOptional(): bool + { + return (is_null($this->value) || $this->value instanceof FieldValue) && $this->type === 'optional'; } /** * @return ?FieldValue */ - public function asOptional(): ?FieldValue { - if (!((is_null($this->value) || $this->value instanceof FieldValue)&& $this->type === 'optional')){ + public function asOptional(): ?FieldValue + { + if (!((is_null($this->value) || $this->value instanceof FieldValue) && $this->type === 'optional')) { throw new Exception( "Expected optional; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'list': $value = $this->value; $result['list'] = $value; break; case 'optional': $value = $this->asOptional(); - if (!is_null($value)){ + if (!is_null($value)) { $value = $value->jsonSerialize(); } $result['optional'] = $value; break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -170,42 +179,43 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'list': - if (!array_key_exists('list', $data)){ + if (!array_key_exists('list', $data)) { throw new Exception( "JSON data is missing property 'list'", ); } - + $args['value'] = $data['list']; break; case 'optional': - if (!array_key_exists('optional', $data)){ + if (!array_key_exists('optional', $data)) { throw new Exception( "JSON data is missing property 'optional'", ); } - - if (is_null($data['optional'])){ + + if (is_null($data['optional'])) { $args['value'] = null; - } else{ - if (!(is_array($data['optional']))){ + } else { + if (!(is_array($data['optional']))) { throw new Exception( "Expected property 'optional' in JSON data to be array, instead received " . get_debug_type($data['optional']), ); @@ -218,7 +228,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/circular-references-advanced/src/Ast/Types/Dog.php b/seed/php-sdk/circular-references-advanced/src/Ast/Types/Dog.php index 08828879407b..eaebc3fc3151 100644 --- a/seed/php-sdk/circular-references-advanced/src/Ast/Types/Dog.php +++ b/seed/php-sdk/circular-references-advanced/src/Ast/Types/Dog.php @@ -14,7 +14,7 @@ class Dog extends JsonSerializableType * |Fig * ) $fruit */ - #[JsonProperty('fruit'), Union(Acai::class,Fig::class)] + #[JsonProperty('fruit'), Union(Acai::class, Fig::class)] public Acai|Fig $fruit; /** @@ -27,15 +27,15 @@ class Dog extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->fruit = $values['fruit']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/circular-references-advanced/src/Ast/Types/FieldValue.php b/seed/php-sdk/circular-references-advanced/src/Ast/Types/FieldValue.php index fea08bfcdbff..892a235659bc 100644 --- a/seed/php-sdk/circular-references-advanced/src/Ast/Types/FieldValue.php +++ b/seed/php-sdk/circular-references-advanced/src/Ast/Types/FieldValue.php @@ -46,16 +46,17 @@ class FieldValue extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param value-of $primitiveValue * @return FieldValue */ - public static function primitiveValue(string $primitiveValue): FieldValue { + public static function primitiveValue(string $primitiveValue): FieldValue + { return new FieldValue([ 'type' => 'primitive_value', 'value' => $primitiveValue, @@ -66,7 +67,8 @@ public static function primitiveValue(string $primitiveValue): FieldValue { * @param ObjectValue $objectValue * @return FieldValue */ - public static function objectValue(ObjectValue $objectValue): FieldValue { + public static function objectValue(ObjectValue $objectValue): FieldValue + { return new FieldValue([ 'type' => 'object_value', 'value' => $objectValue, @@ -77,7 +79,8 @@ public static function objectValue(ObjectValue $objectValue): FieldValue { * @param ContainerValue $containerValue * @return FieldValue */ - public static function containerValue(ContainerValue $containerValue): FieldValue { + public static function containerValue(ContainerValue $containerValue): FieldValue + { return new FieldValue([ 'type' => 'container_value', 'value' => $containerValue, @@ -87,81 +90,89 @@ public static function containerValue(ContainerValue $containerValue): FieldValu /** * @return bool */ - public function isPrimitiveValue(): bool { - return $this->value instanceof PrimitiveValue&& $this->type === 'primitive_value'; + public function isPrimitiveValue(): bool + { + return $this->value instanceof PrimitiveValue && $this->type === 'primitive_value'; } /** * @return value-of */ - public function asPrimitiveValue(): string { - if (!($this->value instanceof PrimitiveValue&& $this->type === 'primitive_value')){ + public function asPrimitiveValue(): string + { + if (!($this->value instanceof PrimitiveValue && $this->type === 'primitive_value')) { throw new Exception( "Expected primitive_value; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isObjectValue(): bool { - return $this->value instanceof ObjectValue&& $this->type === 'object_value'; + public function isObjectValue(): bool + { + return $this->value instanceof ObjectValue && $this->type === 'object_value'; } /** * @return ObjectValue */ - public function asObjectValue(): ObjectValue { - if (!($this->value instanceof ObjectValue&& $this->type === 'object_value')){ + public function asObjectValue(): ObjectValue + { + if (!($this->value instanceof ObjectValue && $this->type === 'object_value')) { throw new Exception( "Expected object_value; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isContainerValue(): bool { - return $this->value instanceof ContainerValue&& $this->type === 'container_value'; + public function isContainerValue(): bool + { + return $this->value instanceof ContainerValue && $this->type === 'container_value'; } /** * @return ContainerValue */ - public function asContainerValue(): ContainerValue { - if (!($this->value instanceof ContainerValue&& $this->type === 'container_value')){ + public function asContainerValue(): ContainerValue + { + if (!($this->value instanceof ContainerValue && $this->type === 'container_value')) { throw new Exception( "Expected container_value; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'primitive_value': $value = $this->value; $result['primitive_value'] = $value; @@ -176,26 +187,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -204,42 +216,43 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'primitive_value': - if (!array_key_exists('primitive_value', $data)){ + if (!array_key_exists('primitive_value', $data)) { throw new Exception( "JSON data is missing property 'primitive_value'", ); } - + $args['value'] = $data['primitive_value']; break; case 'object_value': $args['value'] = ObjectValue::jsonDeserialize($data); break; case 'container_value': - if (!array_key_exists('container_value', $data)){ + if (!array_key_exists('container_value', $data)) { throw new Exception( "JSON data is missing property 'container_value'", ); } - - if (!(is_array($data['container_value']))){ + + if (!(is_array($data['container_value']))) { throw new Exception( "Expected property 'containerValue' in JSON data to be array, instead received " . get_debug_type($data['container_value']), ); @@ -251,7 +264,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/circular-references-advanced/src/Ast/Types/Fig.php b/seed/php-sdk/circular-references-advanced/src/Ast/Types/Fig.php index 1ec3d41b3d2a..b4cd45787ca3 100644 --- a/seed/php-sdk/circular-references-advanced/src/Ast/Types/Fig.php +++ b/seed/php-sdk/circular-references-advanced/src/Ast/Types/Fig.php @@ -14,7 +14,7 @@ class Fig extends JsonSerializableType * |Dog * ) $animal */ - #[JsonProperty('animal'), Union(Cat::class,Dog::class)] + #[JsonProperty('animal'), Union(Cat::class, Dog::class)] public Cat|Dog $animal; /** @@ -27,15 +27,15 @@ class Fig extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->animal = $values['animal']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/circular-references-advanced/src/Ast/Types/LeafNode.php b/seed/php-sdk/circular-references-advanced/src/Ast/Types/LeafNode.php index 41e4b5912d3b..61dfad7f0e91 100644 --- a/seed/php-sdk/circular-references-advanced/src/Ast/Types/LeafNode.php +++ b/seed/php-sdk/circular-references-advanced/src/Ast/Types/LeafNode.php @@ -6,22 +6,21 @@ class LeafNode extends JsonSerializableType { - /** * @param array{ * } $values */ public function __construct( array $values = [], - ) - { + ) { unset($values); } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/circular-references-advanced/src/Ast/Types/NodesWrapper.php b/seed/php-sdk/circular-references-advanced/src/Ast/Types/NodesWrapper.php index f975c9d45596..7028e462b1b7 100644 --- a/seed/php-sdk/circular-references-advanced/src/Ast/Types/NodesWrapper.php +++ b/seed/php-sdk/circular-references-advanced/src/Ast/Types/NodesWrapper.php @@ -28,15 +28,15 @@ class NodesWrapper extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->nodes = $values['nodes']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/circular-references-advanced/src/Ast/Types/ObjectFieldValue.php b/seed/php-sdk/circular-references-advanced/src/Ast/Types/ObjectFieldValue.php index d35b4a18ad1d..c74ecb5f5e81 100644 --- a/seed/php-sdk/circular-references-advanced/src/Ast/Types/ObjectFieldValue.php +++ b/seed/php-sdk/circular-references-advanced/src/Ast/Types/ObjectFieldValue.php @@ -30,15 +30,16 @@ class ObjectFieldValue extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->value = $values['value']; + ) { + $this->name = $values['name']; + $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/circular-references-advanced/src/Ast/Types/ObjectValue.php b/seed/php-sdk/circular-references-advanced/src/Ast/Types/ObjectValue.php index e12788376a1f..8ffbb48b2571 100644 --- a/seed/php-sdk/circular-references-advanced/src/Ast/Types/ObjectValue.php +++ b/seed/php-sdk/circular-references-advanced/src/Ast/Types/ObjectValue.php @@ -6,22 +6,21 @@ class ObjectValue extends JsonSerializableType { - /** * @param array{ * } $values */ public function __construct( array $values = [], - ) - { + ) { unset($values); } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/circular-references-advanced/src/Ast/Types/PrimitiveValue.php b/seed/php-sdk/circular-references-advanced/src/Ast/Types/PrimitiveValue.php index 506455dde2ba..0a82acbc694f 100644 --- a/seed/php-sdk/circular-references-advanced/src/Ast/Types/PrimitiveValue.php +++ b/seed/php-sdk/circular-references-advanced/src/Ast/Types/PrimitiveValue.php @@ -2,8 +2,8 @@ namespace Seed\Ast\Types; -enum PrimitiveValue - : string { +enum PrimitiveValue: string +{ case String = "STRING"; case Number = "NUMBER"; } diff --git a/seed/php-sdk/circular-references-advanced/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/circular-references-advanced/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/circular-references-advanced/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/circular-references-advanced/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/circular-references-advanced/src/Core/Client/RawClient.php b/seed/php-sdk/circular-references-advanced/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/circular-references-advanced/src/Core/Client/RawClient.php +++ b/seed/php-sdk/circular-references-advanced/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/circular-references-advanced/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/circular-references-advanced/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/circular-references-advanced/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/circular-references-advanced/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/circular-references-advanced/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/circular-references-advanced/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/circular-references-advanced/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/circular-references-advanced/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/circular-references-advanced/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/circular-references-advanced/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/circular-references-advanced/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/circular-references-advanced/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/circular-references-advanced/src/Core/Types/ArrayType.php b/seed/php-sdk/circular-references-advanced/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/circular-references-advanced/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/circular-references-advanced/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/circular-references-advanced/src/Core/Types/Constant.php b/seed/php-sdk/circular-references-advanced/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/circular-references-advanced/src/Core/Types/Constant.php +++ b/seed/php-sdk/circular-references-advanced/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/circular-references-advanced/src/Core/Types/Union.php b/seed/php-sdk/circular-references-advanced/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/circular-references-advanced/src/Core/Types/Union.php +++ b/seed/php-sdk/circular-references-advanced/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/circular-references-advanced/src/Exceptions/SeedApiException.php b/seed/php-sdk/circular-references-advanced/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/circular-references-advanced/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/circular-references-advanced/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/circular-references-advanced/src/Exceptions/SeedException.php b/seed/php-sdk/circular-references-advanced/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/circular-references-advanced/src/Exceptions/SeedException.php +++ b/seed/php-sdk/circular-references-advanced/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/circular-references-advanced/src/SeedClient.php b/seed/php-sdk/circular-references-advanced/src/SeedClient.php index efe6e7438b31..37a1c4dd76aa 100644 --- a/seed/php-sdk/circular-references-advanced/src/SeedClient.php +++ b/seed/php-sdk/circular-references-advanced/src/SeedClient.php @@ -2,7 +2,6 @@ namespace Seed; -class SeedClient +class SeedClient { - } diff --git a/seed/php-sdk/circular-references-advanced/src/Traits/RootType.php b/seed/php-sdk/circular-references-advanced/src/Traits/RootType.php index a2075e9032e2..ef8eae518c71 100644 --- a/seed/php-sdk/circular-references-advanced/src/Traits/RootType.php +++ b/seed/php-sdk/circular-references-advanced/src/Traits/RootType.php @@ -7,7 +7,7 @@ /** * @property string $s */ -trait RootType +trait RootType { /** * @var string $s diff --git a/seed/php-sdk/circular-references-advanced/src/Types/ImportingA.php b/seed/php-sdk/circular-references-advanced/src/Types/ImportingA.php index 9360dfc314f7..92629f18fddc 100644 --- a/seed/php-sdk/circular-references-advanced/src/Types/ImportingA.php +++ b/seed/php-sdk/circular-references-advanced/src/Types/ImportingA.php @@ -21,15 +21,15 @@ class ImportingA extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->a = $values['a'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/circular-references-advanced/src/Types/RootType.php b/seed/php-sdk/circular-references-advanced/src/Types/RootType.php index 160db49c247b..98cbdff52d4f 100644 --- a/seed/php-sdk/circular-references-advanced/src/Types/RootType.php +++ b/seed/php-sdk/circular-references-advanced/src/Types/RootType.php @@ -20,15 +20,15 @@ class RootType extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->s = $values['s']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/circular-references-advanced/src/Utils/File.php b/seed/php-sdk/circular-references-advanced/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/circular-references-advanced/src/Utils/File.php +++ b/seed/php-sdk/circular-references-advanced/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/circular-references-advanced/tests/Core/Client/RawClientTest.php b/seed/php-sdk/circular-references-advanced/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/circular-references-advanced/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/circular-references-advanced/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/circular-references-advanced/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/circular-references-advanced/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/circular-references-advanced/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/circular-references-advanced/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/circular-references-advanced/tests/Core/Json/EnumTest.php b/seed/php-sdk/circular-references-advanced/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/circular-references-advanced/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/circular-references-advanced/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/circular-references-advanced/tests/Core/Json/TraitTest.php b/seed/php-sdk/circular-references-advanced/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/circular-references-advanced/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/circular-references-advanced/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/circular-references-advanced/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/circular-references-advanced/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/circular-references-advanced/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/circular-references-advanced/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/circular-references/src/A/Types/A.php b/seed/php-sdk/circular-references/src/A/Types/A.php index 8065c13abdb0..31a4c8ba9e4f 100644 --- a/seed/php-sdk/circular-references/src/A/Types/A.php +++ b/seed/php-sdk/circular-references/src/A/Types/A.php @@ -17,15 +17,15 @@ class A extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->s = $values['s']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/circular-references/src/Ast/Types/ContainerValue.php b/seed/php-sdk/circular-references/src/Ast/Types/ContainerValue.php index 3a6d27c0ccd5..736e71012554 100644 --- a/seed/php-sdk/circular-references/src/Ast/Types/ContainerValue.php +++ b/seed/php-sdk/circular-references/src/Ast/Types/ContainerValue.php @@ -44,16 +44,17 @@ class ContainerValue extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param array $list * @return ContainerValue */ - public static function list(array $list): ContainerValue { + public static function list(array $list): ContainerValue + { return new ContainerValue([ 'type' => 'list', 'value' => $list, @@ -64,7 +65,8 @@ public static function list(array $list): ContainerValue { * @param ?FieldValue $optional * @return ContainerValue */ - public static function optional(?FieldValue $optional = null): ContainerValue { + public static function optional(?FieldValue $optional = null): ContainerValue + { return new ContainerValue([ 'type' => 'optional', 'value' => $optional, @@ -74,94 +76,101 @@ public static function optional(?FieldValue $optional = null): ContainerValue { /** * @return bool */ - public function isList_(): bool { - return is_array($this->value)&& $this->type === 'list'; + public function isList_(): bool + { + return is_array($this->value) && $this->type === 'list'; } /** * @return array */ - public function asList_(): array { - if (!(is_array($this->value)&& $this->type === 'list')){ + public function asList_(): array + { + if (!(is_array($this->value) && $this->type === 'list')) { throw new Exception( "Expected list; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isOptional(): bool { - return (is_null($this->value) || $this->value instanceof FieldValue)&& $this->type === 'optional'; + public function isOptional(): bool + { + return (is_null($this->value) || $this->value instanceof FieldValue) && $this->type === 'optional'; } /** * @return ?FieldValue */ - public function asOptional(): ?FieldValue { - if (!((is_null($this->value) || $this->value instanceof FieldValue)&& $this->type === 'optional')){ + public function asOptional(): ?FieldValue + { + if (!((is_null($this->value) || $this->value instanceof FieldValue) && $this->type === 'optional')) { throw new Exception( "Expected optional; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'list': $value = $this->value; $result['list'] = $value; break; case 'optional': $value = $this->asOptional(); - if (!is_null($value)){ + if (!is_null($value)) { $value = $value->jsonSerialize(); } $result['optional'] = $value; break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -170,42 +179,43 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'list': - if (!array_key_exists('list', $data)){ + if (!array_key_exists('list', $data)) { throw new Exception( "JSON data is missing property 'list'", ); } - + $args['value'] = $data['list']; break; case 'optional': - if (!array_key_exists('optional', $data)){ + if (!array_key_exists('optional', $data)) { throw new Exception( "JSON data is missing property 'optional'", ); } - - if (is_null($data['optional'])){ + + if (is_null($data['optional'])) { $args['value'] = null; - } else{ - if (!(is_array($data['optional']))){ + } else { + if (!(is_array($data['optional']))) { throw new Exception( "Expected property 'optional' in JSON data to be array, instead received " . get_debug_type($data['optional']), ); @@ -218,7 +228,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/circular-references/src/Ast/Types/FieldValue.php b/seed/php-sdk/circular-references/src/Ast/Types/FieldValue.php index fea08bfcdbff..892a235659bc 100644 --- a/seed/php-sdk/circular-references/src/Ast/Types/FieldValue.php +++ b/seed/php-sdk/circular-references/src/Ast/Types/FieldValue.php @@ -46,16 +46,17 @@ class FieldValue extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param value-of $primitiveValue * @return FieldValue */ - public static function primitiveValue(string $primitiveValue): FieldValue { + public static function primitiveValue(string $primitiveValue): FieldValue + { return new FieldValue([ 'type' => 'primitive_value', 'value' => $primitiveValue, @@ -66,7 +67,8 @@ public static function primitiveValue(string $primitiveValue): FieldValue { * @param ObjectValue $objectValue * @return FieldValue */ - public static function objectValue(ObjectValue $objectValue): FieldValue { + public static function objectValue(ObjectValue $objectValue): FieldValue + { return new FieldValue([ 'type' => 'object_value', 'value' => $objectValue, @@ -77,7 +79,8 @@ public static function objectValue(ObjectValue $objectValue): FieldValue { * @param ContainerValue $containerValue * @return FieldValue */ - public static function containerValue(ContainerValue $containerValue): FieldValue { + public static function containerValue(ContainerValue $containerValue): FieldValue + { return new FieldValue([ 'type' => 'container_value', 'value' => $containerValue, @@ -87,81 +90,89 @@ public static function containerValue(ContainerValue $containerValue): FieldValu /** * @return bool */ - public function isPrimitiveValue(): bool { - return $this->value instanceof PrimitiveValue&& $this->type === 'primitive_value'; + public function isPrimitiveValue(): bool + { + return $this->value instanceof PrimitiveValue && $this->type === 'primitive_value'; } /** * @return value-of */ - public function asPrimitiveValue(): string { - if (!($this->value instanceof PrimitiveValue&& $this->type === 'primitive_value')){ + public function asPrimitiveValue(): string + { + if (!($this->value instanceof PrimitiveValue && $this->type === 'primitive_value')) { throw new Exception( "Expected primitive_value; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isObjectValue(): bool { - return $this->value instanceof ObjectValue&& $this->type === 'object_value'; + public function isObjectValue(): bool + { + return $this->value instanceof ObjectValue && $this->type === 'object_value'; } /** * @return ObjectValue */ - public function asObjectValue(): ObjectValue { - if (!($this->value instanceof ObjectValue&& $this->type === 'object_value')){ + public function asObjectValue(): ObjectValue + { + if (!($this->value instanceof ObjectValue && $this->type === 'object_value')) { throw new Exception( "Expected object_value; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isContainerValue(): bool { - return $this->value instanceof ContainerValue&& $this->type === 'container_value'; + public function isContainerValue(): bool + { + return $this->value instanceof ContainerValue && $this->type === 'container_value'; } /** * @return ContainerValue */ - public function asContainerValue(): ContainerValue { - if (!($this->value instanceof ContainerValue&& $this->type === 'container_value')){ + public function asContainerValue(): ContainerValue + { + if (!($this->value instanceof ContainerValue && $this->type === 'container_value')) { throw new Exception( "Expected container_value; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'primitive_value': $value = $this->value; $result['primitive_value'] = $value; @@ -176,26 +187,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -204,42 +216,43 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'primitive_value': - if (!array_key_exists('primitive_value', $data)){ + if (!array_key_exists('primitive_value', $data)) { throw new Exception( "JSON data is missing property 'primitive_value'", ); } - + $args['value'] = $data['primitive_value']; break; case 'object_value': $args['value'] = ObjectValue::jsonDeserialize($data); break; case 'container_value': - if (!array_key_exists('container_value', $data)){ + if (!array_key_exists('container_value', $data)) { throw new Exception( "JSON data is missing property 'container_value'", ); } - - if (!(is_array($data['container_value']))){ + + if (!(is_array($data['container_value']))) { throw new Exception( "Expected property 'containerValue' in JSON data to be array, instead received " . get_debug_type($data['container_value']), ); @@ -251,7 +264,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/circular-references/src/Ast/Types/ObjectValue.php b/seed/php-sdk/circular-references/src/Ast/Types/ObjectValue.php index e12788376a1f..8ffbb48b2571 100644 --- a/seed/php-sdk/circular-references/src/Ast/Types/ObjectValue.php +++ b/seed/php-sdk/circular-references/src/Ast/Types/ObjectValue.php @@ -6,22 +6,21 @@ class ObjectValue extends JsonSerializableType { - /** * @param array{ * } $values */ public function __construct( array $values = [], - ) - { + ) { unset($values); } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/circular-references/src/Ast/Types/PrimitiveValue.php b/seed/php-sdk/circular-references/src/Ast/Types/PrimitiveValue.php index 506455dde2ba..0a82acbc694f 100644 --- a/seed/php-sdk/circular-references/src/Ast/Types/PrimitiveValue.php +++ b/seed/php-sdk/circular-references/src/Ast/Types/PrimitiveValue.php @@ -2,8 +2,8 @@ namespace Seed\Ast\Types; -enum PrimitiveValue - : string { +enum PrimitiveValue: string +{ case String = "STRING"; case Number = "NUMBER"; } diff --git a/seed/php-sdk/circular-references/src/Ast/Types/T.php b/seed/php-sdk/circular-references/src/Ast/Types/T.php index 4f4fb80081ee..0e13ce6bd03b 100644 --- a/seed/php-sdk/circular-references/src/Ast/Types/T.php +++ b/seed/php-sdk/circular-references/src/Ast/Types/T.php @@ -14,7 +14,7 @@ class T extends JsonSerializableType * |U * ) $child */ - #[JsonProperty('child'), Union(T::class,U::class)] + #[JsonProperty('child'), Union(T::class, U::class)] public T|U $child; /** @@ -27,15 +27,15 @@ class T extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->child = $values['child']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/circular-references/src/Ast/Types/U.php b/seed/php-sdk/circular-references/src/Ast/Types/U.php index 0c533fabf3ed..a4b60d20a6d0 100644 --- a/seed/php-sdk/circular-references/src/Ast/Types/U.php +++ b/seed/php-sdk/circular-references/src/Ast/Types/U.php @@ -20,15 +20,15 @@ class U extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->child = $values['child']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/circular-references/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/circular-references/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/circular-references/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/circular-references/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/circular-references/src/Core/Client/RawClient.php b/seed/php-sdk/circular-references/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/circular-references/src/Core/Client/RawClient.php +++ b/seed/php-sdk/circular-references/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/circular-references/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/circular-references/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/circular-references/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/circular-references/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/circular-references/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/circular-references/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/circular-references/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/circular-references/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/circular-references/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/circular-references/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/circular-references/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/circular-references/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/circular-references/src/Core/Types/ArrayType.php b/seed/php-sdk/circular-references/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/circular-references/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/circular-references/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/circular-references/src/Core/Types/Constant.php b/seed/php-sdk/circular-references/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/circular-references/src/Core/Types/Constant.php +++ b/seed/php-sdk/circular-references/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/circular-references/src/Core/Types/Union.php b/seed/php-sdk/circular-references/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/circular-references/src/Core/Types/Union.php +++ b/seed/php-sdk/circular-references/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/circular-references/src/Exceptions/SeedApiException.php b/seed/php-sdk/circular-references/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/circular-references/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/circular-references/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/circular-references/src/Exceptions/SeedException.php b/seed/php-sdk/circular-references/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/circular-references/src/Exceptions/SeedException.php +++ b/seed/php-sdk/circular-references/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/circular-references/src/SeedClient.php b/seed/php-sdk/circular-references/src/SeedClient.php index efe6e7438b31..37a1c4dd76aa 100644 --- a/seed/php-sdk/circular-references/src/SeedClient.php +++ b/seed/php-sdk/circular-references/src/SeedClient.php @@ -2,7 +2,6 @@ namespace Seed; -class SeedClient +class SeedClient { - } diff --git a/seed/php-sdk/circular-references/src/Traits/RootType.php b/seed/php-sdk/circular-references/src/Traits/RootType.php index a2075e9032e2..ef8eae518c71 100644 --- a/seed/php-sdk/circular-references/src/Traits/RootType.php +++ b/seed/php-sdk/circular-references/src/Traits/RootType.php @@ -7,7 +7,7 @@ /** * @property string $s */ -trait RootType +trait RootType { /** * @var string $s diff --git a/seed/php-sdk/circular-references/src/Types/ImportingA.php b/seed/php-sdk/circular-references/src/Types/ImportingA.php index 9360dfc314f7..92629f18fddc 100644 --- a/seed/php-sdk/circular-references/src/Types/ImportingA.php +++ b/seed/php-sdk/circular-references/src/Types/ImportingA.php @@ -21,15 +21,15 @@ class ImportingA extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->a = $values['a'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/circular-references/src/Types/RootType.php b/seed/php-sdk/circular-references/src/Types/RootType.php index 160db49c247b..98cbdff52d4f 100644 --- a/seed/php-sdk/circular-references/src/Types/RootType.php +++ b/seed/php-sdk/circular-references/src/Types/RootType.php @@ -20,15 +20,15 @@ class RootType extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->s = $values['s']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/circular-references/src/Utils/File.php b/seed/php-sdk/circular-references/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/circular-references/src/Utils/File.php +++ b/seed/php-sdk/circular-references/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/circular-references/tests/Core/Client/RawClientTest.php b/seed/php-sdk/circular-references/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/circular-references/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/circular-references/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/circular-references/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/circular-references/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/circular-references/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/circular-references/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/circular-references/tests/Core/Json/EnumTest.php b/seed/php-sdk/circular-references/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/circular-references/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/circular-references/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/circular-references/tests/Core/Json/TraitTest.php b/seed/php-sdk/circular-references/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/circular-references/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/circular-references/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/circular-references/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/circular-references/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/circular-references/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/circular-references/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/client-side-params/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/client-side-params/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/client-side-params/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/client-side-params/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/client-side-params/src/Core/Client/RawClient.php b/seed/php-sdk/client-side-params/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/client-side-params/src/Core/Client/RawClient.php +++ b/seed/php-sdk/client-side-params/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/client-side-params/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/client-side-params/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/client-side-params/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/client-side-params/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/client-side-params/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/client-side-params/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/client-side-params/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/client-side-params/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/client-side-params/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/client-side-params/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/client-side-params/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/client-side-params/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/client-side-params/src/Core/Types/ArrayType.php b/seed/php-sdk/client-side-params/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/client-side-params/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/client-side-params/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/client-side-params/src/Core/Types/Constant.php b/seed/php-sdk/client-side-params/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/client-side-params/src/Core/Types/Constant.php +++ b/seed/php-sdk/client-side-params/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/client-side-params/src/Core/Types/Union.php b/seed/php-sdk/client-side-params/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/client-side-params/src/Core/Types/Union.php +++ b/seed/php-sdk/client-side-params/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/client-side-params/src/Exceptions/SeedApiException.php b/seed/php-sdk/client-side-params/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/client-side-params/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/client-side-params/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/client-side-params/src/Exceptions/SeedException.php b/seed/php-sdk/client-side-params/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/client-side-params/src/Exceptions/SeedException.php +++ b/seed/php-sdk/client-side-params/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/client-side-params/src/SeedClient.php b/seed/php-sdk/client-side-params/src/SeedClient.php index 45122e4933eb..0fd2265f7040 100644 --- a/seed/php-sdk/client-side-params/src/SeedClient.php +++ b/seed/php-sdk/client-side-params/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var ServiceClient $service @@ -42,29 +42,28 @@ class SeedClient public function __construct( ?string $token = null, ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - if ($token != null){ + if ($token != null) { $defaultHeaders['Authorization'] = "Bearer $token"; } - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->service = new ServiceClient($this->client, $this->options); } } diff --git a/seed/php-sdk/client-side-params/src/Service/Requests/GetClientRequest.php b/seed/php-sdk/client-side-params/src/Service/Requests/GetClientRequest.php index 36d0954257c4..1a24e8c15737 100644 --- a/seed/php-sdk/client-side-params/src/Service/Requests/GetClientRequest.php +++ b/seed/php-sdk/client-side-params/src/Service/Requests/GetClientRequest.php @@ -24,8 +24,8 @@ class GetClientRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->fields = $values['fields'] ?? null;$this->includeFields = $values['includeFields'] ?? null; + ) { + $this->fields = $values['fields'] ?? null; + $this->includeFields = $values['includeFields'] ?? null; } } diff --git a/seed/php-sdk/client-side-params/src/Service/Requests/GetConnectionRequest.php b/seed/php-sdk/client-side-params/src/Service/Requests/GetConnectionRequest.php index bbcd491b88a8..b56c785fa57a 100644 --- a/seed/php-sdk/client-side-params/src/Service/Requests/GetConnectionRequest.php +++ b/seed/php-sdk/client-side-params/src/Service/Requests/GetConnectionRequest.php @@ -18,8 +18,7 @@ class GetConnectionRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->fields = $values['fields'] ?? null; } } diff --git a/seed/php-sdk/client-side-params/src/Service/Requests/GetResourceRequest.php b/seed/php-sdk/client-side-params/src/Service/Requests/GetResourceRequest.php index e6626338fcd2..64d692920e72 100644 --- a/seed/php-sdk/client-side-params/src/Service/Requests/GetResourceRequest.php +++ b/seed/php-sdk/client-side-params/src/Service/Requests/GetResourceRequest.php @@ -24,8 +24,8 @@ class GetResourceRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->includeMetadata = $values['includeMetadata'];$this->format = $values['format']; + ) { + $this->includeMetadata = $values['includeMetadata']; + $this->format = $values['format']; } } diff --git a/seed/php-sdk/client-side-params/src/Service/Requests/GetUserRequest.php b/seed/php-sdk/client-side-params/src/Service/Requests/GetUserRequest.php index 438fad709fcd..ae6712287351 100644 --- a/seed/php-sdk/client-side-params/src/Service/Requests/GetUserRequest.php +++ b/seed/php-sdk/client-side-params/src/Service/Requests/GetUserRequest.php @@ -24,8 +24,8 @@ class GetUserRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->fields = $values['fields'] ?? null;$this->includeFields = $values['includeFields'] ?? null; + ) { + $this->fields = $values['fields'] ?? null; + $this->includeFields = $values['includeFields'] ?? null; } } diff --git a/seed/php-sdk/client-side-params/src/Service/Requests/ListClientsRequest.php b/seed/php-sdk/client-side-params/src/Service/Requests/ListClientsRequest.php index c8590e75959e..5f58d0c32888 100644 --- a/seed/php-sdk/client-side-params/src/Service/Requests/ListClientsRequest.php +++ b/seed/php-sdk/client-side-params/src/Service/Requests/ListClientsRequest.php @@ -60,8 +60,14 @@ class ListClientsRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->fields = $values['fields'] ?? null;$this->includeFields = $values['includeFields'] ?? null;$this->page = $values['page'] ?? null;$this->perPage = $values['perPage'] ?? null;$this->includeTotals = $values['includeTotals'] ?? null;$this->isGlobal = $values['isGlobal'] ?? null;$this->isFirstParty = $values['isFirstParty'] ?? null;$this->appType = $values['appType'] ?? null; + ) { + $this->fields = $values['fields'] ?? null; + $this->includeFields = $values['includeFields'] ?? null; + $this->page = $values['page'] ?? null; + $this->perPage = $values['perPage'] ?? null; + $this->includeTotals = $values['includeTotals'] ?? null; + $this->isGlobal = $values['isGlobal'] ?? null; + $this->isFirstParty = $values['isFirstParty'] ?? null; + $this->appType = $values['appType'] ?? null; } } diff --git a/seed/php-sdk/client-side-params/src/Service/Requests/ListConnectionsRequest.php b/seed/php-sdk/client-side-params/src/Service/Requests/ListConnectionsRequest.php index 5a53aaace49f..bcf493b8edd1 100644 --- a/seed/php-sdk/client-side-params/src/Service/Requests/ListConnectionsRequest.php +++ b/seed/php-sdk/client-side-params/src/Service/Requests/ListConnectionsRequest.php @@ -30,8 +30,9 @@ class ListConnectionsRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->strategy = $values['strategy'] ?? null;$this->name = $values['name'] ?? null;$this->fields = $values['fields'] ?? null; + ) { + $this->strategy = $values['strategy'] ?? null; + $this->name = $values['name'] ?? null; + $this->fields = $values['fields'] ?? null; } } diff --git a/seed/php-sdk/client-side-params/src/Service/Requests/ListResourcesRequest.php b/seed/php-sdk/client-side-params/src/Service/Requests/ListResourcesRequest.php index b7a9e482c583..d5603dc017df 100644 --- a/seed/php-sdk/client-side-params/src/Service/Requests/ListResourcesRequest.php +++ b/seed/php-sdk/client-side-params/src/Service/Requests/ListResourcesRequest.php @@ -54,8 +54,13 @@ class ListResourcesRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->page = $values['page'];$this->perPage = $values['perPage'];$this->sort = $values['sort'];$this->order = $values['order'];$this->includeTotals = $values['includeTotals'];$this->fields = $values['fields'] ?? null;$this->search = $values['search'] ?? null; + ) { + $this->page = $values['page']; + $this->perPage = $values['perPage']; + $this->sort = $values['sort']; + $this->order = $values['order']; + $this->includeTotals = $values['includeTotals']; + $this->fields = $values['fields'] ?? null; + $this->search = $values['search'] ?? null; } } diff --git a/seed/php-sdk/client-side-params/src/Service/Requests/ListUsersRequest.php b/seed/php-sdk/client-side-params/src/Service/Requests/ListUsersRequest.php index ce61981f2d37..2797fa8295d0 100644 --- a/seed/php-sdk/client-side-params/src/Service/Requests/ListUsersRequest.php +++ b/seed/php-sdk/client-side-params/src/Service/Requests/ListUsersRequest.php @@ -60,8 +60,14 @@ class ListUsersRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->page = $values['page'] ?? null;$this->perPage = $values['perPage'] ?? null;$this->includeTotals = $values['includeTotals'] ?? null;$this->sort = $values['sort'] ?? null;$this->connection = $values['connection'] ?? null;$this->q = $values['q'] ?? null;$this->searchEngine = $values['searchEngine'] ?? null;$this->fields = $values['fields'] ?? null; + ) { + $this->page = $values['page'] ?? null; + $this->perPage = $values['perPage'] ?? null; + $this->includeTotals = $values['includeTotals'] ?? null; + $this->sort = $values['sort'] ?? null; + $this->connection = $values['connection'] ?? null; + $this->q = $values['q'] ?? null; + $this->searchEngine = $values['searchEngine'] ?? null; + $this->fields = $values['fields'] ?? null; } } diff --git a/seed/php-sdk/client-side-params/src/Service/Requests/SearchResourcesRequest.php b/seed/php-sdk/client-side-params/src/Service/Requests/SearchResourcesRequest.php index b1feb98921df..c93d5c7934c3 100644 --- a/seed/php-sdk/client-side-params/src/Service/Requests/SearchResourcesRequest.php +++ b/seed/php-sdk/client-side-params/src/Service/Requests/SearchResourcesRequest.php @@ -40,8 +40,10 @@ class SearchResourcesRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->limit = $values['limit'];$this->offset = $values['offset'];$this->query = $values['query'] ?? null;$this->filters = $values['filters'] ?? null; + ) { + $this->limit = $values['limit']; + $this->offset = $values['offset']; + $this->query = $values['query'] ?? null; + $this->filters = $values['filters'] ?? null; } } diff --git a/seed/php-sdk/client-side-params/src/Service/ServiceClient.php b/seed/php-sdk/client-side-params/src/Service/ServiceClient.php index 9fc4635b2a4e..bab25cd7de85 100644 --- a/seed/php-sdk/client-side-params/src/Service/ServiceClient.php +++ b/seed/php-sdk/client-side-params/src/Service/ServiceClient.php @@ -31,7 +31,7 @@ use Seed\Service\Requests\GetClientRequest; use Seed\Types\Types\Client; -class ServiceClient +class ServiceClient { /** * @var array{ @@ -59,11 +59,10 @@ class ServiceClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -84,7 +83,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function listResources(ListResourcesRequest $request, ?array $options = null): array { + public function listResources(ListResourcesRequest $request, ?array $options = null): array + { $options = array_merge($this->options, $options ?? []); $query = []; $query['page'] = $request->page; @@ -92,10 +92,10 @@ public function listResources(ListResourcesRequest $request, ?array $options = n $query['sort'] = $request->sort; $query['order'] = $request->order; $query['include_totals'] = $request->includeTotals; - if ($request->fields != null){ + if ($request->fields != null) { $query['fields'] = $request->fields; } - if ($request->search != null){ + if ($request->search != null) { $query['search'] = $request->search; } try { @@ -109,15 +109,15 @@ public function listResources(ListResourcesRequest $request, ?array $options = n $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, [Resource::class]); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -152,7 +152,8 @@ public function listResources(ListResourcesRequest $request, ?array $options = n * @throws SeedException * @throws SeedApiException */ - public function getResource(string $resourceId, GetResourceRequest $request, ?array $options = null): Resource { + public function getResource(string $resourceId, GetResourceRequest $request, ?array $options = null): Resource + { $options = array_merge($this->options, $options ?? []); $query = []; $query['include_metadata'] = $request->includeMetadata; @@ -168,15 +169,15 @@ public function getResource(string $resourceId, GetResourceRequest $request, ?ar $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return Resource::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -210,7 +211,8 @@ public function getResource(string $resourceId, GetResourceRequest $request, ?ar * @throws SeedException * @throws SeedApiException */ - public function searchResources(SearchResourcesRequest $request, ?array $options = null): SearchResponse { + public function searchResources(SearchResourcesRequest $request, ?array $options = null): SearchResponse + { $options = array_merge($this->options, $options ?? []); $query = []; $query['limit'] = $request->limit; @@ -227,15 +229,15 @@ public function searchResources(SearchResourcesRequest $request, ?array $options $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return SearchResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -269,31 +271,32 @@ public function searchResources(SearchResourcesRequest $request, ?array $options * @throws SeedException * @throws SeedApiException */ - public function listUsers(ListUsersRequest $request = new ListUsersRequest(), ?array $options = null): PaginatedUserResponse { + public function listUsers(ListUsersRequest $request = new ListUsersRequest(), ?array $options = null): PaginatedUserResponse + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->page != null){ + if ($request->page != null) { $query['page'] = $request->page; } - if ($request->perPage != null){ + if ($request->perPage != null) { $query['per_page'] = $request->perPage; } - if ($request->includeTotals != null){ + if ($request->includeTotals != null) { $query['include_totals'] = $request->includeTotals; } - if ($request->sort != null){ + if ($request->sort != null) { $query['sort'] = $request->sort; } - if ($request->connection != null){ + if ($request->connection != null) { $query['connection'] = $request->connection; } - if ($request->q != null){ + if ($request->q != null) { $query['q'] = $request->q; } - if ($request->searchEngine != null){ + if ($request->searchEngine != null) { $query['search_engine'] = $request->searchEngine; } - if ($request->fields != null){ + if ($request->fields != null) { $query['fields'] = $request->fields; } try { @@ -307,15 +310,15 @@ public function listUsers(ListUsersRequest $request = new ListUsersRequest(), ?a $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return PaginatedUserResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -350,13 +353,14 @@ public function listUsers(ListUsersRequest $request = new ListUsersRequest(), ?a * @throws SeedException * @throws SeedApiException */ - public function getUserById(string $userId, GetUserRequest $request = new GetUserRequest(), ?array $options = null): User { + public function getUserById(string $userId, GetUserRequest $request = new GetUserRequest(), ?array $options = null): User + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->fields != null){ + if ($request->fields != null) { $query['fields'] = $request->fields; } - if ($request->includeFields != null){ + if ($request->includeFields != null) { $query['include_fields'] = $request->includeFields; } try { @@ -370,15 +374,15 @@ public function getUserById(string $userId, GetUserRequest $request = new GetUse $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return User::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -412,7 +416,8 @@ public function getUserById(string $userId, GetUserRequest $request = new GetUse * @throws SeedException * @throws SeedApiException */ - public function createUser(CreateUserRequest $request, ?array $options = null): User { + public function createUser(CreateUserRequest $request, ?array $options = null): User + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -425,15 +430,15 @@ public function createUser(CreateUserRequest $request, ?array $options = null): $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return User::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -468,7 +473,8 @@ public function createUser(CreateUserRequest $request, ?array $options = null): * @throws SeedException * @throws SeedApiException */ - public function updateUser(string $userId, UpdateUserRequest $request, ?array $options = null): User { + public function updateUser(string $userId, UpdateUserRequest $request, ?array $options = null): User + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -481,15 +487,15 @@ public function updateUser(string $userId, UpdateUserRequest $request, ?array $o $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return User::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -522,7 +528,8 @@ public function updateUser(string $userId, UpdateUserRequest $request, ?array $o * @throws SeedException * @throws SeedApiException */ - public function deleteUser(string $userId, ?array $options = null): void { + public function deleteUser(string $userId, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -534,12 +541,12 @@ public function deleteUser(string $userId, ?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -573,16 +580,17 @@ public function deleteUser(string $userId, ?array $options = null): void { * @throws SeedException * @throws SeedApiException */ - public function listConnections(ListConnectionsRequest $request = new ListConnectionsRequest(), ?array $options = null): array { + public function listConnections(ListConnectionsRequest $request = new ListConnectionsRequest(), ?array $options = null): array + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->strategy != null){ + if ($request->strategy != null) { $query['strategy'] = $request->strategy; } - if ($request->name != null){ + if ($request->name != null) { $query['name'] = $request->name; } - if ($request->fields != null){ + if ($request->fields != null) { $query['fields'] = $request->fields; } try { @@ -596,15 +604,15 @@ public function listConnections(ListConnectionsRequest $request = new ListConnec $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, [Connection::class]); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -639,10 +647,11 @@ public function listConnections(ListConnectionsRequest $request = new ListConnec * @throws SeedException * @throws SeedApiException */ - public function getConnection(string $connectionId, GetConnectionRequest $request = new GetConnectionRequest(), ?array $options = null): Connection { + public function getConnection(string $connectionId, GetConnectionRequest $request = new GetConnectionRequest(), ?array $options = null): Connection + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->fields != null){ + if ($request->fields != null) { $query['fields'] = $request->fields; } try { @@ -656,15 +665,15 @@ public function getConnection(string $connectionId, GetConnectionRequest $reques $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return Connection::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -698,31 +707,32 @@ public function getConnection(string $connectionId, GetConnectionRequest $reques * @throws SeedException * @throws SeedApiException */ - public function listClients(ListClientsRequest $request = new ListClientsRequest(), ?array $options = null): PaginatedClientResponse { + public function listClients(ListClientsRequest $request = new ListClientsRequest(), ?array $options = null): PaginatedClientResponse + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->fields != null){ + if ($request->fields != null) { $query['fields'] = $request->fields; } - if ($request->includeFields != null){ + if ($request->includeFields != null) { $query['include_fields'] = $request->includeFields; } - if ($request->page != null){ + if ($request->page != null) { $query['page'] = $request->page; } - if ($request->perPage != null){ + if ($request->perPage != null) { $query['per_page'] = $request->perPage; } - if ($request->includeTotals != null){ + if ($request->includeTotals != null) { $query['include_totals'] = $request->includeTotals; } - if ($request->isGlobal != null){ + if ($request->isGlobal != null) { $query['is_global'] = $request->isGlobal; } - if ($request->isFirstParty != null){ + if ($request->isFirstParty != null) { $query['is_first_party'] = $request->isFirstParty; } - if ($request->appType != null){ + if ($request->appType != null) { $query['app_type'] = $request->appType; } try { @@ -736,15 +746,15 @@ public function listClients(ListClientsRequest $request = new ListClientsRequest $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return PaginatedClientResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -779,13 +789,14 @@ public function listClients(ListClientsRequest $request = new ListClientsRequest * @throws SeedException * @throws SeedApiException */ - public function getClient(string $clientId, GetClientRequest $request = new GetClientRequest(), ?array $options = null): Client { + public function getClient(string $clientId, GetClientRequest $request = new GetClientRequest(), ?array $options = null): Client + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->fields != null){ + if ($request->fields != null) { $query['fields'] = $request->fields; } - if ($request->includeFields != null){ + if ($request->includeFields != null) { $query['include_fields'] = $request->includeFields; } try { @@ -799,15 +810,15 @@ public function getClient(string $clientId, GetClientRequest $request = new GetC $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return Client::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/client-side-params/src/Types/Types/Client.php b/seed/php-sdk/client-side-params/src/Types/Types/Client.php index 7555adee83fe..6353e35903f0 100644 --- a/seed/php-sdk/client-side-params/src/Types/Types/Client.php +++ b/seed/php-sdk/client-side-params/src/Types/Types/Client.php @@ -227,15 +227,44 @@ class Client extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->clientId = $values['clientId'];$this->tenant = $values['tenant'] ?? null;$this->name = $values['name'];$this->description = $values['description'] ?? null;$this->global = $values['global'] ?? null;$this->clientSecret = $values['clientSecret'] ?? null;$this->appType = $values['appType'] ?? null;$this->logoUri = $values['logoUri'] ?? null;$this->isFirstParty = $values['isFirstParty'] ?? null;$this->oidcConformant = $values['oidcConformant'] ?? null;$this->callbacks = $values['callbacks'] ?? null;$this->allowedOrigins = $values['allowedOrigins'] ?? null;$this->webOrigins = $values['webOrigins'] ?? null;$this->grantTypes = $values['grantTypes'] ?? null;$this->jwtConfiguration = $values['jwtConfiguration'] ?? null;$this->signingKeys = $values['signingKeys'] ?? null;$this->encryptionKey = $values['encryptionKey'] ?? null;$this->sso = $values['sso'] ?? null;$this->ssoDisabled = $values['ssoDisabled'] ?? null;$this->crossOriginAuth = $values['crossOriginAuth'] ?? null;$this->crossOriginLoc = $values['crossOriginLoc'] ?? null;$this->customLoginPageOn = $values['customLoginPageOn'] ?? null;$this->customLoginPage = $values['customLoginPage'] ?? null;$this->customLoginPagePreview = $values['customLoginPagePreview'] ?? null;$this->formTemplate = $values['formTemplate'] ?? null;$this->isHerokuApp = $values['isHerokuApp'] ?? null;$this->addons = $values['addons'] ?? null;$this->tokenEndpointAuthMethod = $values['tokenEndpointAuthMethod'] ?? null;$this->clientMetadata = $values['clientMetadata'] ?? null;$this->mobile = $values['mobile'] ?? null; + ) { + $this->clientId = $values['clientId']; + $this->tenant = $values['tenant'] ?? null; + $this->name = $values['name']; + $this->description = $values['description'] ?? null; + $this->global = $values['global'] ?? null; + $this->clientSecret = $values['clientSecret'] ?? null; + $this->appType = $values['appType'] ?? null; + $this->logoUri = $values['logoUri'] ?? null; + $this->isFirstParty = $values['isFirstParty'] ?? null; + $this->oidcConformant = $values['oidcConformant'] ?? null; + $this->callbacks = $values['callbacks'] ?? null; + $this->allowedOrigins = $values['allowedOrigins'] ?? null; + $this->webOrigins = $values['webOrigins'] ?? null; + $this->grantTypes = $values['grantTypes'] ?? null; + $this->jwtConfiguration = $values['jwtConfiguration'] ?? null; + $this->signingKeys = $values['signingKeys'] ?? null; + $this->encryptionKey = $values['encryptionKey'] ?? null; + $this->sso = $values['sso'] ?? null; + $this->ssoDisabled = $values['ssoDisabled'] ?? null; + $this->crossOriginAuth = $values['crossOriginAuth'] ?? null; + $this->crossOriginLoc = $values['crossOriginLoc'] ?? null; + $this->customLoginPageOn = $values['customLoginPageOn'] ?? null; + $this->customLoginPage = $values['customLoginPage'] ?? null; + $this->customLoginPagePreview = $values['customLoginPagePreview'] ?? null; + $this->formTemplate = $values['formTemplate'] ?? null; + $this->isHerokuApp = $values['isHerokuApp'] ?? null; + $this->addons = $values['addons'] ?? null; + $this->tokenEndpointAuthMethod = $values['tokenEndpointAuthMethod'] ?? null; + $this->clientMetadata = $values['clientMetadata'] ?? null; + $this->mobile = $values['mobile'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/client-side-params/src/Types/Types/Connection.php b/seed/php-sdk/client-side-params/src/Types/Types/Connection.php index cb4e844dcfed..2dac1390a9ba 100644 --- a/seed/php-sdk/client-side-params/src/Types/Types/Connection.php +++ b/seed/php-sdk/client-side-params/src/Types/Types/Connection.php @@ -80,15 +80,23 @@ class Connection extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->name = $values['name'];$this->displayName = $values['displayName'] ?? null;$this->strategy = $values['strategy'];$this->options = $values['options'] ?? null;$this->enabledClients = $values['enabledClients'] ?? null;$this->realms = $values['realms'] ?? null;$this->isDomainConnection = $values['isDomainConnection'] ?? null;$this->metadata = $values['metadata'] ?? null; + ) { + $this->id = $values['id']; + $this->name = $values['name']; + $this->displayName = $values['displayName'] ?? null; + $this->strategy = $values['strategy']; + $this->options = $values['options'] ?? null; + $this->enabledClients = $values['enabledClients'] ?? null; + $this->realms = $values['realms'] ?? null; + $this->isDomainConnection = $values['isDomainConnection'] ?? null; + $this->metadata = $values['metadata'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/client-side-params/src/Types/Types/CreateUserRequest.php b/seed/php-sdk/client-side-params/src/Types/Types/CreateUserRequest.php index be01057411c4..2a0e63c9b9b7 100644 --- a/seed/php-sdk/client-side-params/src/Types/Types/CreateUserRequest.php +++ b/seed/php-sdk/client-side-params/src/Types/Types/CreateUserRequest.php @@ -77,15 +77,23 @@ class CreateUserRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->email = $values['email'];$this->emailVerified = $values['emailVerified'] ?? null;$this->username = $values['username'] ?? null;$this->password = $values['password'] ?? null;$this->phoneNumber = $values['phoneNumber'] ?? null;$this->phoneVerified = $values['phoneVerified'] ?? null;$this->userMetadata = $values['userMetadata'] ?? null;$this->appMetadata = $values['appMetadata'] ?? null;$this->connection = $values['connection']; + ) { + $this->email = $values['email']; + $this->emailVerified = $values['emailVerified'] ?? null; + $this->username = $values['username'] ?? null; + $this->password = $values['password'] ?? null; + $this->phoneNumber = $values['phoneNumber'] ?? null; + $this->phoneVerified = $values['phoneVerified'] ?? null; + $this->userMetadata = $values['userMetadata'] ?? null; + $this->appMetadata = $values['appMetadata'] ?? null; + $this->connection = $values['connection']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/client-side-params/src/Types/Types/Identity.php b/seed/php-sdk/client-side-params/src/Types/Types/Identity.php index 1c3c82166d49..d5b85b1bd943 100644 --- a/seed/php-sdk/client-side-params/src/Types/Types/Identity.php +++ b/seed/php-sdk/client-side-params/src/Types/Types/Identity.php @@ -55,15 +55,20 @@ class Identity extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->connection = $values['connection'];$this->userId = $values['userId'];$this->provider = $values['provider'];$this->isSocial = $values['isSocial'];$this->accessToken = $values['accessToken'] ?? null;$this->expiresIn = $values['expiresIn'] ?? null; + ) { + $this->connection = $values['connection']; + $this->userId = $values['userId']; + $this->provider = $values['provider']; + $this->isSocial = $values['isSocial']; + $this->accessToken = $values['accessToken'] ?? null; + $this->expiresIn = $values['expiresIn'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/client-side-params/src/Types/Types/PaginatedClientResponse.php b/seed/php-sdk/client-side-params/src/Types/Types/PaginatedClientResponse.php index 7edfbf92125c..a80a19522e32 100644 --- a/seed/php-sdk/client-side-params/src/Types/Types/PaginatedClientResponse.php +++ b/seed/php-sdk/client-side-params/src/Types/Types/PaginatedClientResponse.php @@ -52,15 +52,19 @@ class PaginatedClientResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->start = $values['start'];$this->limit = $values['limit'];$this->length = $values['length'];$this->total = $values['total'] ?? null;$this->clients = $values['clients']; + ) { + $this->start = $values['start']; + $this->limit = $values['limit']; + $this->length = $values['length']; + $this->total = $values['total'] ?? null; + $this->clients = $values['clients']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/client-side-params/src/Types/Types/PaginatedUserResponse.php b/seed/php-sdk/client-side-params/src/Types/Types/PaginatedUserResponse.php index c1e01dcb9fa9..d94776458a84 100644 --- a/seed/php-sdk/client-side-params/src/Types/Types/PaginatedUserResponse.php +++ b/seed/php-sdk/client-side-params/src/Types/Types/PaginatedUserResponse.php @@ -52,15 +52,19 @@ class PaginatedUserResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->users = $values['users'];$this->start = $values['start'];$this->limit = $values['limit'];$this->length = $values['length'];$this->total = $values['total'] ?? null; + ) { + $this->users = $values['users']; + $this->start = $values['start']; + $this->limit = $values['limit']; + $this->length = $values['length']; + $this->total = $values['total'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/client-side-params/src/Types/Types/Resource.php b/seed/php-sdk/client-side-params/src/Types/Types/Resource.php index a2119df7a516..82ed300a2546 100644 --- a/seed/php-sdk/client-side-params/src/Types/Types/Resource.php +++ b/seed/php-sdk/client-side-params/src/Types/Types/Resource.php @@ -58,15 +58,20 @@ class Resource extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->name = $values['name'];$this->description = $values['description'] ?? null;$this->createdAt = $values['createdAt'];$this->updatedAt = $values['updatedAt'];$this->metadata = $values['metadata'] ?? null; + ) { + $this->id = $values['id']; + $this->name = $values['name']; + $this->description = $values['description'] ?? null; + $this->createdAt = $values['createdAt']; + $this->updatedAt = $values['updatedAt']; + $this->metadata = $values['metadata'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/client-side-params/src/Types/Types/SearchResponse.php b/seed/php-sdk/client-side-params/src/Types/Types/SearchResponse.php index eb26785cf1ad..6a938d3e82e5 100644 --- a/seed/php-sdk/client-side-params/src/Types/Types/SearchResponse.php +++ b/seed/php-sdk/client-side-params/src/Types/Types/SearchResponse.php @@ -35,15 +35,17 @@ class SearchResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->results = $values['results'];$this->total = $values['total'] ?? null;$this->nextOffset = $values['nextOffset'] ?? null; + ) { + $this->results = $values['results']; + $this->total = $values['total'] ?? null; + $this->nextOffset = $values['nextOffset'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/client-side-params/src/Types/Types/UpdateUserRequest.php b/seed/php-sdk/client-side-params/src/Types/Types/UpdateUserRequest.php index ec7e05abd6a4..d97ebcb993aa 100644 --- a/seed/php-sdk/client-side-params/src/Types/Types/UpdateUserRequest.php +++ b/seed/php-sdk/client-side-params/src/Types/Types/UpdateUserRequest.php @@ -77,15 +77,23 @@ class UpdateUserRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->email = $values['email'] ?? null;$this->emailVerified = $values['emailVerified'] ?? null;$this->username = $values['username'] ?? null;$this->phoneNumber = $values['phoneNumber'] ?? null;$this->phoneVerified = $values['phoneVerified'] ?? null;$this->userMetadata = $values['userMetadata'] ?? null;$this->appMetadata = $values['appMetadata'] ?? null;$this->password = $values['password'] ?? null;$this->blocked = $values['blocked'] ?? null; + ) { + $this->email = $values['email'] ?? null; + $this->emailVerified = $values['emailVerified'] ?? null; + $this->username = $values['username'] ?? null; + $this->phoneNumber = $values['phoneNumber'] ?? null; + $this->phoneVerified = $values['phoneVerified'] ?? null; + $this->userMetadata = $values['userMetadata'] ?? null; + $this->appMetadata = $values['appMetadata'] ?? null; + $this->password = $values['password'] ?? null; + $this->blocked = $values['blocked'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/client-side-params/src/Types/Types/User.php b/seed/php-sdk/client-side-params/src/Types/Types/User.php index 7ee90d590206..efa26c035db8 100644 --- a/seed/php-sdk/client-side-params/src/Types/Types/User.php +++ b/seed/php-sdk/client-side-params/src/Types/Types/User.php @@ -166,15 +166,35 @@ class User extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->userId = $values['userId'];$this->email = $values['email'];$this->emailVerified = $values['emailVerified'];$this->username = $values['username'] ?? null;$this->phoneNumber = $values['phoneNumber'] ?? null;$this->phoneVerified = $values['phoneVerified'] ?? null;$this->createdAt = $values['createdAt'];$this->updatedAt = $values['updatedAt'];$this->identities = $values['identities'] ?? null;$this->appMetadata = $values['appMetadata'] ?? null;$this->userMetadata = $values['userMetadata'] ?? null;$this->picture = $values['picture'] ?? null;$this->name = $values['name'] ?? null;$this->nickname = $values['nickname'] ?? null;$this->multifactor = $values['multifactor'] ?? null;$this->lastIp = $values['lastIp'] ?? null;$this->lastLogin = $values['lastLogin'] ?? null;$this->loginsCount = $values['loginsCount'] ?? null;$this->blocked = $values['blocked'] ?? null;$this->givenName = $values['givenName'] ?? null;$this->familyName = $values['familyName'] ?? null; + ) { + $this->userId = $values['userId']; + $this->email = $values['email']; + $this->emailVerified = $values['emailVerified']; + $this->username = $values['username'] ?? null; + $this->phoneNumber = $values['phoneNumber'] ?? null; + $this->phoneVerified = $values['phoneVerified'] ?? null; + $this->createdAt = $values['createdAt']; + $this->updatedAt = $values['updatedAt']; + $this->identities = $values['identities'] ?? null; + $this->appMetadata = $values['appMetadata'] ?? null; + $this->userMetadata = $values['userMetadata'] ?? null; + $this->picture = $values['picture'] ?? null; + $this->name = $values['name'] ?? null; + $this->nickname = $values['nickname'] ?? null; + $this->multifactor = $values['multifactor'] ?? null; + $this->lastIp = $values['lastIp'] ?? null; + $this->lastLogin = $values['lastLogin'] ?? null; + $this->loginsCount = $values['loginsCount'] ?? null; + $this->blocked = $values['blocked'] ?? null; + $this->givenName = $values['givenName'] ?? null; + $this->familyName = $values['familyName'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/client-side-params/src/Utils/File.php b/seed/php-sdk/client-side-params/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/client-side-params/src/Utils/File.php +++ b/seed/php-sdk/client-side-params/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/client-side-params/tests/Core/Client/RawClientTest.php b/seed/php-sdk/client-side-params/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/client-side-params/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/client-side-params/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/client-side-params/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/client-side-params/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/client-side-params/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/client-side-params/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/client-side-params/tests/Core/Json/EnumTest.php b/seed/php-sdk/client-side-params/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/client-side-params/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/client-side-params/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/client-side-params/tests/Core/Json/TraitTest.php b/seed/php-sdk/client-side-params/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/client-side-params/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/client-side-params/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/client-side-params/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/client-side-params/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/client-side-params/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/client-side-params/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/content-type/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/content-type/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/content-type/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/content-type/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/content-type/src/Core/Client/RawClient.php b/seed/php-sdk/content-type/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/content-type/src/Core/Client/RawClient.php +++ b/seed/php-sdk/content-type/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/content-type/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/content-type/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/content-type/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/content-type/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/content-type/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/content-type/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/content-type/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/content-type/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/content-type/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/content-type/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/content-type/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/content-type/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/content-type/src/Core/Types/ArrayType.php b/seed/php-sdk/content-type/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/content-type/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/content-type/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/content-type/src/Core/Types/Constant.php b/seed/php-sdk/content-type/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/content-type/src/Core/Types/Constant.php +++ b/seed/php-sdk/content-type/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/content-type/src/Core/Types/Union.php b/seed/php-sdk/content-type/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/content-type/src/Core/Types/Union.php +++ b/seed/php-sdk/content-type/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/content-type/src/Exceptions/SeedApiException.php b/seed/php-sdk/content-type/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/content-type/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/content-type/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/content-type/src/Exceptions/SeedException.php b/seed/php-sdk/content-type/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/content-type/src/Exceptions/SeedException.php +++ b/seed/php-sdk/content-type/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/content-type/src/SeedClient.php b/seed/php-sdk/content-type/src/SeedClient.php index 8d7adcb3b0f2..0fd6a9d1029e 100644 --- a/seed/php-sdk/content-type/src/SeedClient.php +++ b/seed/php-sdk/content-type/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var ServiceClient $service @@ -40,26 +40,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->service = new ServiceClient($this->client, $this->options); } } diff --git a/seed/php-sdk/content-type/src/Service/Requests/NamedMixedPatchRequest.php b/seed/php-sdk/content-type/src/Service/Requests/NamedMixedPatchRequest.php index 3e207312eb25..51c269b19c58 100644 --- a/seed/php-sdk/content-type/src/Service/Requests/NamedMixedPatchRequest.php +++ b/seed/php-sdk/content-type/src/Service/Requests/NamedMixedPatchRequest.php @@ -34,8 +34,9 @@ class NamedMixedPatchRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->appId = $values['appId'] ?? null;$this->instructions = $values['instructions'] ?? null;$this->active = $values['active'] ?? null; + ) { + $this->appId = $values['appId'] ?? null; + $this->instructions = $values['instructions'] ?? null; + $this->active = $values['active'] ?? null; } } diff --git a/seed/php-sdk/content-type/src/Service/Requests/OptionalMergePatchRequest.php b/seed/php-sdk/content-type/src/Service/Requests/OptionalMergePatchRequest.php index 2378b6b5888d..37f624eda5a7 100644 --- a/seed/php-sdk/content-type/src/Service/Requests/OptionalMergePatchRequest.php +++ b/seed/php-sdk/content-type/src/Service/Requests/OptionalMergePatchRequest.php @@ -48,8 +48,11 @@ class OptionalMergePatchRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->requiredField = $values['requiredField'];$this->optionalString = $values['optionalString'] ?? null;$this->optionalInteger = $values['optionalInteger'] ?? null;$this->optionalBoolean = $values['optionalBoolean'] ?? null;$this->nullableString = $values['nullableString'] ?? null; + ) { + $this->requiredField = $values['requiredField']; + $this->optionalString = $values['optionalString'] ?? null; + $this->optionalInteger = $values['optionalInteger'] ?? null; + $this->optionalBoolean = $values['optionalBoolean'] ?? null; + $this->nullableString = $values['nullableString'] ?? null; } } diff --git a/seed/php-sdk/content-type/src/Service/Requests/PatchComplexRequest.php b/seed/php-sdk/content-type/src/Service/Requests/PatchComplexRequest.php index 8dac14972215..c6bec21c5ad1 100644 --- a/seed/php-sdk/content-type/src/Service/Requests/PatchComplexRequest.php +++ b/seed/php-sdk/content-type/src/Service/Requests/PatchComplexRequest.php @@ -84,8 +84,16 @@ class PatchComplexRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->name = $values['name'] ?? null;$this->age = $values['age'] ?? null;$this->active = $values['active'] ?? null;$this->metadata = $values['metadata'] ?? null;$this->tags = $values['tags'] ?? null;$this->email = $values['email'] ?? null;$this->nickname = $values['nickname'] ?? null;$this->bio = $values['bio'] ?? null;$this->profileImageUrl = $values['profileImageUrl'] ?? null;$this->settings = $values['settings'] ?? null; + ) { + $this->name = $values['name'] ?? null; + $this->age = $values['age'] ?? null; + $this->active = $values['active'] ?? null; + $this->metadata = $values['metadata'] ?? null; + $this->tags = $values['tags'] ?? null; + $this->email = $values['email'] ?? null; + $this->nickname = $values['nickname'] ?? null; + $this->bio = $values['bio'] ?? null; + $this->profileImageUrl = $values['profileImageUrl'] ?? null; + $this->settings = $values['settings'] ?? null; } } diff --git a/seed/php-sdk/content-type/src/Service/Requests/PatchProxyRequest.php b/seed/php-sdk/content-type/src/Service/Requests/PatchProxyRequest.php index 755aa80692d5..a778c28f18e2 100644 --- a/seed/php-sdk/content-type/src/Service/Requests/PatchProxyRequest.php +++ b/seed/php-sdk/content-type/src/Service/Requests/PatchProxyRequest.php @@ -27,8 +27,8 @@ class PatchProxyRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->application = $values['application'] ?? null;$this->requireAuth = $values['requireAuth'] ?? null; + ) { + $this->application = $values['application'] ?? null; + $this->requireAuth = $values['requireAuth'] ?? null; } } diff --git a/seed/php-sdk/content-type/src/Service/Requests/RegularPatchRequest.php b/seed/php-sdk/content-type/src/Service/Requests/RegularPatchRequest.php index 2f7cbcb71965..e0d56980a6f8 100644 --- a/seed/php-sdk/content-type/src/Service/Requests/RegularPatchRequest.php +++ b/seed/php-sdk/content-type/src/Service/Requests/RegularPatchRequest.php @@ -27,8 +27,8 @@ class RegularPatchRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->field1 = $values['field1'] ?? null;$this->field2 = $values['field2'] ?? null; + ) { + $this->field1 = $values['field1'] ?? null; + $this->field2 = $values['field2'] ?? null; } } diff --git a/seed/php-sdk/content-type/src/Service/ServiceClient.php b/seed/php-sdk/content-type/src/Service/ServiceClient.php index 848cc1966e6f..96bec83e37d7 100644 --- a/seed/php-sdk/content-type/src/Service/ServiceClient.php +++ b/seed/php-sdk/content-type/src/Service/ServiceClient.php @@ -16,7 +16,7 @@ use Seed\Service\Requests\OptionalMergePatchRequest; use Seed\Service\Requests\RegularPatchRequest; -class ServiceClient +class ServiceClient { /** * @var array{ @@ -44,11 +44,10 @@ class ServiceClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -66,7 +65,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function patch(PatchProxyRequest $request, ?array $options = null): void { + public function patch(PatchProxyRequest $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -79,12 +79,12 @@ public function patch(PatchProxyRequest $request, ?array $options = null): void $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -121,7 +121,8 @@ public function patch(PatchProxyRequest $request, ?array $options = null): void * @throws SeedException * @throws SeedApiException */ - public function patchComplex(string $id, PatchComplexRequest $request = new PatchComplexRequest(), ?array $options = null): void { + public function patchComplex(string $id, PatchComplexRequest $request = new PatchComplexRequest(), ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -134,12 +135,12 @@ public function patchComplex(string $id, PatchComplexRequest $request = new Patc $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -174,7 +175,8 @@ public function patchComplex(string $id, PatchComplexRequest $request = new Patc * @throws SeedException * @throws SeedApiException */ - public function namedPatchWithMixed(string $id, NamedMixedPatchRequest $request, ?array $options = null): void { + public function namedPatchWithMixed(string $id, NamedMixedPatchRequest $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -187,12 +189,12 @@ public function namedPatchWithMixed(string $id, NamedMixedPatchRequest $request, $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -228,7 +230,8 @@ public function namedPatchWithMixed(string $id, NamedMixedPatchRequest $request, * @throws SeedException * @throws SeedApiException */ - public function optionalMergePatchTest(OptionalMergePatchRequest $request, ?array $options = null): void { + public function optionalMergePatchTest(OptionalMergePatchRequest $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -241,12 +244,12 @@ public function optionalMergePatchTest(OptionalMergePatchRequest $request, ?arra $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -280,7 +283,8 @@ public function optionalMergePatchTest(OptionalMergePatchRequest $request, ?arra * @throws SeedException * @throws SeedApiException */ - public function regularPatch(string $id, RegularPatchRequest $request = new RegularPatchRequest(), ?array $options = null): void { + public function regularPatch(string $id, RegularPatchRequest $request = new RegularPatchRequest(), ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -293,12 +297,12 @@ public function regularPatch(string $id, RegularPatchRequest $request = new Regu $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/content-type/src/Utils/File.php b/seed/php-sdk/content-type/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/content-type/src/Utils/File.php +++ b/seed/php-sdk/content-type/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/content-type/tests/Core/Client/RawClientTest.php b/seed/php-sdk/content-type/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/content-type/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/content-type/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/content-type/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/content-type/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/content-type/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/content-type/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/content-type/tests/Core/Json/EnumTest.php b/seed/php-sdk/content-type/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/content-type/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/content-type/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/content-type/tests/Core/Json/TraitTest.php b/seed/php-sdk/content-type/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/content-type/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/content-type/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/content-type/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/content-type/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/content-type/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/content-type/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/cross-package-type-names/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/cross-package-type-names/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/cross-package-type-names/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/cross-package-type-names/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/cross-package-type-names/src/Core/Client/RawClient.php b/seed/php-sdk/cross-package-type-names/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/cross-package-type-names/src/Core/Client/RawClient.php +++ b/seed/php-sdk/cross-package-type-names/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/cross-package-type-names/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/cross-package-type-names/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/cross-package-type-names/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/cross-package-type-names/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/cross-package-type-names/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/cross-package-type-names/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/cross-package-type-names/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/cross-package-type-names/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/cross-package-type-names/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/cross-package-type-names/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/cross-package-type-names/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/cross-package-type-names/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/cross-package-type-names/src/Core/Types/ArrayType.php b/seed/php-sdk/cross-package-type-names/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/cross-package-type-names/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/cross-package-type-names/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/cross-package-type-names/src/Core/Types/Constant.php b/seed/php-sdk/cross-package-type-names/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/cross-package-type-names/src/Core/Types/Constant.php +++ b/seed/php-sdk/cross-package-type-names/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/cross-package-type-names/src/Core/Types/Union.php b/seed/php-sdk/cross-package-type-names/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/cross-package-type-names/src/Core/Types/Union.php +++ b/seed/php-sdk/cross-package-type-names/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/cross-package-type-names/src/Exceptions/SeedApiException.php b/seed/php-sdk/cross-package-type-names/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/cross-package-type-names/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/cross-package-type-names/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/cross-package-type-names/src/Exceptions/SeedException.php b/seed/php-sdk/cross-package-type-names/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/cross-package-type-names/src/Exceptions/SeedException.php +++ b/seed/php-sdk/cross-package-type-names/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/cross-package-type-names/src/FolderA/FolderAClient.php b/seed/php-sdk/cross-package-type-names/src/FolderA/FolderAClient.php index 82be40ef3942..95005f3a386b 100644 --- a/seed/php-sdk/cross-package-type-names/src/FolderA/FolderAClient.php +++ b/seed/php-sdk/cross-package-type-names/src/FolderA/FolderAClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class FolderAClient +class FolderAClient { /** * @var ServiceClient $service @@ -39,11 +39,10 @@ class FolderAClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; $this->service = new ServiceClient($this->client, $this->options); diff --git a/seed/php-sdk/cross-package-type-names/src/FolderA/Service/ServiceClient.php b/seed/php-sdk/cross-package-type-names/src/FolderA/Service/ServiceClient.php index 69cd637c748c..0f0bcabb90b6 100644 --- a/seed/php-sdk/cross-package-type-names/src/FolderA/Service/ServiceClient.php +++ b/seed/php-sdk/cross-package-type-names/src/FolderA/Service/ServiceClient.php @@ -13,7 +13,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class ServiceClient +class ServiceClient { /** * @var array{ @@ -41,11 +41,10 @@ class ServiceClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -63,7 +62,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getDirectThread(?array $options = null): Response { + public function getDirectThread(?array $options = null): Response + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -75,15 +75,15 @@ public function getDirectThread(?array $options = null): Response { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return Response::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/cross-package-type-names/src/FolderA/Service/Types/Response.php b/seed/php-sdk/cross-package-type-names/src/FolderA/Service/Types/Response.php index da119898dc2f..580d1b21a907 100644 --- a/seed/php-sdk/cross-package-type-names/src/FolderA/Service/Types/Response.php +++ b/seed/php-sdk/cross-package-type-names/src/FolderA/Service/Types/Response.php @@ -21,15 +21,15 @@ class Response extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->foo = $values['foo'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/cross-package-type-names/src/FolderB/Common/Types/Foo.php b/seed/php-sdk/cross-package-type-names/src/FolderB/Common/Types/Foo.php index 63267aff33cf..f618a22f1510 100644 --- a/seed/php-sdk/cross-package-type-names/src/FolderB/Common/Types/Foo.php +++ b/seed/php-sdk/cross-package-type-names/src/FolderB/Common/Types/Foo.php @@ -20,15 +20,15 @@ class Foo extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->foo = $values['foo'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/cross-package-type-names/src/FolderC/Common/Types/Foo.php b/seed/php-sdk/cross-package-type-names/src/FolderC/Common/Types/Foo.php index 17d634c8be94..894322869c8b 100644 --- a/seed/php-sdk/cross-package-type-names/src/FolderC/Common/Types/Foo.php +++ b/seed/php-sdk/cross-package-type-names/src/FolderC/Common/Types/Foo.php @@ -20,15 +20,15 @@ class Foo extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->barProperty = $values['barProperty']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/cross-package-type-names/src/FolderD/FolderDClient.php b/seed/php-sdk/cross-package-type-names/src/FolderD/FolderDClient.php index 03b9521a0527..5d660303871e 100644 --- a/seed/php-sdk/cross-package-type-names/src/FolderD/FolderDClient.php +++ b/seed/php-sdk/cross-package-type-names/src/FolderD/FolderDClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class FolderDClient +class FolderDClient { /** * @var ServiceClient $service @@ -39,11 +39,10 @@ class FolderDClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; $this->service = new ServiceClient($this->client, $this->options); diff --git a/seed/php-sdk/cross-package-type-names/src/FolderD/Service/ServiceClient.php b/seed/php-sdk/cross-package-type-names/src/FolderD/Service/ServiceClient.php index 9b41593cda74..0804ee6744ca 100644 --- a/seed/php-sdk/cross-package-type-names/src/FolderD/Service/ServiceClient.php +++ b/seed/php-sdk/cross-package-type-names/src/FolderD/Service/ServiceClient.php @@ -13,7 +13,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class ServiceClient +class ServiceClient { /** * @var array{ @@ -41,11 +41,10 @@ class ServiceClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -63,7 +62,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getDirectThread(?array $options = null): Response { + public function getDirectThread(?array $options = null): Response + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -75,15 +75,15 @@ public function getDirectThread(?array $options = null): Response { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return Response::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/cross-package-type-names/src/FolderD/Service/Types/Response.php b/seed/php-sdk/cross-package-type-names/src/FolderD/Service/Types/Response.php index ce243c5711c6..fe4afdf565a3 100644 --- a/seed/php-sdk/cross-package-type-names/src/FolderD/Service/Types/Response.php +++ b/seed/php-sdk/cross-package-type-names/src/FolderD/Service/Types/Response.php @@ -21,15 +21,15 @@ class Response extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->foo = $values['foo'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/cross-package-type-names/src/Foo/FooClient.php b/seed/php-sdk/cross-package-type-names/src/Foo/FooClient.php index 433f96c53a54..c92f249361f9 100644 --- a/seed/php-sdk/cross-package-type-names/src/Foo/FooClient.php +++ b/seed/php-sdk/cross-package-type-names/src/Foo/FooClient.php @@ -14,7 +14,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class FooClient +class FooClient { /** * @var array{ @@ -42,11 +42,10 @@ class FooClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -65,10 +64,11 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function find(FindRequest $request = new FindRequest(), ?array $options = null): ImportingType { + public function find(FindRequest $request = new FindRequest(), ?array $options = null): ImportingType + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->optionalString != null){ + if ($request->optionalString != null) { $query['optionalString'] = $request->optionalString; } try { @@ -83,15 +83,15 @@ public function find(FindRequest $request = new FindRequest(), ?array $options = $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ImportingType::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/cross-package-type-names/src/Foo/Requests/FindRequest.php b/seed/php-sdk/cross-package-type-names/src/Foo/Requests/FindRequest.php index 170192955723..150b59404e94 100644 --- a/seed/php-sdk/cross-package-type-names/src/Foo/Requests/FindRequest.php +++ b/seed/php-sdk/cross-package-type-names/src/Foo/Requests/FindRequest.php @@ -33,8 +33,9 @@ class FindRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->optionalString = $values['optionalString'] ?? null;$this->publicProperty = $values['publicProperty'] ?? null;$this->privateProperty = $values['privateProperty'] ?? null; + ) { + $this->optionalString = $values['optionalString'] ?? null; + $this->publicProperty = $values['publicProperty'] ?? null; + $this->privateProperty = $values['privateProperty'] ?? null; } } diff --git a/seed/php-sdk/cross-package-type-names/src/Foo/Types/ImportingType.php b/seed/php-sdk/cross-package-type-names/src/Foo/Types/ImportingType.php index 882a9c285d5c..4a3f6d2f556b 100644 --- a/seed/php-sdk/cross-package-type-names/src/Foo/Types/ImportingType.php +++ b/seed/php-sdk/cross-package-type-names/src/Foo/Types/ImportingType.php @@ -20,15 +20,15 @@ class ImportingType extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->imported = $values['imported']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/cross-package-type-names/src/SeedClient.php b/seed/php-sdk/cross-package-type-names/src/SeedClient.php index 739c8eb49a03..3cbaf6ab067b 100644 --- a/seed/php-sdk/cross-package-type-names/src/SeedClient.php +++ b/seed/php-sdk/cross-package-type-names/src/SeedClient.php @@ -8,7 +8,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var FolderAClient $folderA @@ -52,26 +52,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->folderA = new FolderAClient($this->client, $this->options); $this->folderD = new FolderDClient($this->client, $this->options); $this->foo = new FooClient($this->client, $this->options); diff --git a/seed/php-sdk/cross-package-type-names/src/Utils/File.php b/seed/php-sdk/cross-package-type-names/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/cross-package-type-names/src/Utils/File.php +++ b/seed/php-sdk/cross-package-type-names/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/cross-package-type-names/tests/Core/Client/RawClientTest.php b/seed/php-sdk/cross-package-type-names/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/cross-package-type-names/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/cross-package-type-names/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/cross-package-type-names/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/cross-package-type-names/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/cross-package-type-names/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/cross-package-type-names/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/cross-package-type-names/tests/Core/Json/EnumTest.php b/seed/php-sdk/cross-package-type-names/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/cross-package-type-names/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/cross-package-type-names/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/cross-package-type-names/tests/Core/Json/TraitTest.php b/seed/php-sdk/cross-package-type-names/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/cross-package-type-names/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/cross-package-type-names/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/cross-package-type-names/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/cross-package-type-names/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/cross-package-type-names/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/cross-package-type-names/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/empty-clients/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/empty-clients/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/empty-clients/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/empty-clients/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/empty-clients/src/Core/Client/RawClient.php b/seed/php-sdk/empty-clients/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/empty-clients/src/Core/Client/RawClient.php +++ b/seed/php-sdk/empty-clients/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/empty-clients/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/empty-clients/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/empty-clients/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/empty-clients/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/empty-clients/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/empty-clients/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/empty-clients/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/empty-clients/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/empty-clients/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/empty-clients/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/empty-clients/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/empty-clients/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/empty-clients/src/Core/Types/ArrayType.php b/seed/php-sdk/empty-clients/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/empty-clients/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/empty-clients/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/empty-clients/src/Core/Types/Constant.php b/seed/php-sdk/empty-clients/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/empty-clients/src/Core/Types/Constant.php +++ b/seed/php-sdk/empty-clients/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/empty-clients/src/Core/Types/Union.php b/seed/php-sdk/empty-clients/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/empty-clients/src/Core/Types/Union.php +++ b/seed/php-sdk/empty-clients/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/empty-clients/src/Exceptions/SeedApiException.php b/seed/php-sdk/empty-clients/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/empty-clients/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/empty-clients/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/empty-clients/src/Exceptions/SeedException.php b/seed/php-sdk/empty-clients/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/empty-clients/src/Exceptions/SeedException.php +++ b/seed/php-sdk/empty-clients/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/empty-clients/src/Level1/Level2/Types/Types/Address.php b/seed/php-sdk/empty-clients/src/Level1/Level2/Types/Types/Address.php index 493bbb979187..ecd1b797f75a 100644 --- a/seed/php-sdk/empty-clients/src/Level1/Level2/Types/Types/Address.php +++ b/seed/php-sdk/empty-clients/src/Level1/Level2/Types/Types/Address.php @@ -55,15 +55,20 @@ class Address extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->line1 = $values['line1'];$this->line2 = $values['line2'] ?? null;$this->city = $values['city'];$this->state = $values['state'];$this->zip = $values['zip'];$this->country = $values['country']; + ) { + $this->line1 = $values['line1']; + $this->line2 = $values['line2'] ?? null; + $this->city = $values['city']; + $this->state = $values['state']; + $this->zip = $values['zip']; + $this->country = $values['country']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/empty-clients/src/Level1/Level2/Types/Types/Person.php b/seed/php-sdk/empty-clients/src/Level1/Level2/Types/Types/Person.php index 8ec42e2f69b8..784d1144c5a7 100644 --- a/seed/php-sdk/empty-clients/src/Level1/Level2/Types/Types/Person.php +++ b/seed/php-sdk/empty-clients/src/Level1/Level2/Types/Types/Person.php @@ -27,15 +27,16 @@ class Person extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->address = $values['address']; + ) { + $this->name = $values['name']; + $this->address = $values['address']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/empty-clients/src/Level1/Types/Types/Address.php b/seed/php-sdk/empty-clients/src/Level1/Types/Types/Address.php index 2e7509840be7..871a8d430e48 100644 --- a/seed/php-sdk/empty-clients/src/Level1/Types/Types/Address.php +++ b/seed/php-sdk/empty-clients/src/Level1/Types/Types/Address.php @@ -55,15 +55,20 @@ class Address extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->line1 = $values['line1'];$this->line2 = $values['line2'] ?? null;$this->city = $values['city'];$this->state = $values['state'];$this->zip = $values['zip'];$this->country = $values['country']; + ) { + $this->line1 = $values['line1']; + $this->line2 = $values['line2'] ?? null; + $this->city = $values['city']; + $this->state = $values['state']; + $this->zip = $values['zip']; + $this->country = $values['country']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/empty-clients/src/Level1/Types/Types/Person.php b/seed/php-sdk/empty-clients/src/Level1/Types/Types/Person.php index 10d9692cc47c..726d5593a9ba 100644 --- a/seed/php-sdk/empty-clients/src/Level1/Types/Types/Person.php +++ b/seed/php-sdk/empty-clients/src/Level1/Types/Types/Person.php @@ -27,15 +27,16 @@ class Person extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->address = $values['address']; + ) { + $this->name = $values['name']; + $this->address = $values['address']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/empty-clients/src/SeedClient.php b/seed/php-sdk/empty-clients/src/SeedClient.php index efe6e7438b31..37a1c4dd76aa 100644 --- a/seed/php-sdk/empty-clients/src/SeedClient.php +++ b/seed/php-sdk/empty-clients/src/SeedClient.php @@ -2,7 +2,6 @@ namespace Seed; -class SeedClient +class SeedClient { - } diff --git a/seed/php-sdk/empty-clients/src/Utils/File.php b/seed/php-sdk/empty-clients/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/empty-clients/src/Utils/File.php +++ b/seed/php-sdk/empty-clients/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/empty-clients/tests/Core/Client/RawClientTest.php b/seed/php-sdk/empty-clients/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/empty-clients/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/empty-clients/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/empty-clients/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/empty-clients/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/empty-clients/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/empty-clients/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/empty-clients/tests/Core/Json/EnumTest.php b/seed/php-sdk/empty-clients/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/empty-clients/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/empty-clients/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/empty-clients/tests/Core/Json/TraitTest.php b/seed/php-sdk/empty-clients/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/empty-clients/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/empty-clients/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/empty-clients/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/empty-clients/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/empty-clients/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/empty-clients/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/enum/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/enum/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/enum/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/enum/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/enum/src/Core/Client/RawClient.php b/seed/php-sdk/enum/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/enum/src/Core/Client/RawClient.php +++ b/seed/php-sdk/enum/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/enum/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/enum/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/enum/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/enum/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/enum/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/enum/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/enum/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/enum/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/enum/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/enum/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/enum/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/enum/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/enum/src/Core/Types/ArrayType.php b/seed/php-sdk/enum/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/enum/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/enum/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/enum/src/Core/Types/Constant.php b/seed/php-sdk/enum/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/enum/src/Core/Types/Constant.php +++ b/seed/php-sdk/enum/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/enum/src/Core/Types/Union.php b/seed/php-sdk/enum/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/enum/src/Core/Types/Union.php +++ b/seed/php-sdk/enum/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/enum/src/Exceptions/SeedApiException.php b/seed/php-sdk/enum/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/enum/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/enum/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/enum/src/Exceptions/SeedException.php b/seed/php-sdk/enum/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/enum/src/Exceptions/SeedException.php +++ b/seed/php-sdk/enum/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/enum/src/Headers/HeadersClient.php b/seed/php-sdk/enum/src/Headers/HeadersClient.php index e947b0fe5f38..0594a663b526 100644 --- a/seed/php-sdk/enum/src/Headers/HeadersClient.php +++ b/seed/php-sdk/enum/src/Headers/HeadersClient.php @@ -12,7 +12,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class HeadersClient +class HeadersClient { /** * @var array{ @@ -40,11 +40,10 @@ class HeadersClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -62,15 +61,16 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function send(SendEnumAsHeaderRequest $request, ?array $options = null): void { + public function send(SendEnumAsHeaderRequest $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); $headers = []; $headers['operand'] = $request->operand; $headers['operandOrColor'] = $request->operandOrColor; - if ($request->maybeOperand != null){ + if ($request->maybeOperand != null) { $headers['maybeOperand'] = $request->maybeOperand; } - if ($request->maybeOperandOrColor != null){ + if ($request->maybeOperandOrColor != null) { $headers['maybeOperandOrColor'] = $request->maybeOperandOrColor; } try { @@ -84,12 +84,12 @@ public function send(SendEnumAsHeaderRequest $request, ?array $options = null): $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/enum/src/Headers/Requests/SendEnumAsHeaderRequest.php b/seed/php-sdk/enum/src/Headers/Requests/SendEnumAsHeaderRequest.php index 5f518dad216f..f77075946fa4 100644 --- a/seed/php-sdk/enum/src/Headers/Requests/SendEnumAsHeaderRequest.php +++ b/seed/php-sdk/enum/src/Headers/Requests/SendEnumAsHeaderRequest.php @@ -50,8 +50,10 @@ class SendEnumAsHeaderRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->operand = $values['operand'];$this->maybeOperand = $values['maybeOperand'] ?? null;$this->operandOrColor = $values['operandOrColor'];$this->maybeOperandOrColor = $values['maybeOperandOrColor'] ?? null; + ) { + $this->operand = $values['operand']; + $this->maybeOperand = $values['maybeOperand'] ?? null; + $this->operandOrColor = $values['operandOrColor']; + $this->maybeOperandOrColor = $values['maybeOperandOrColor'] ?? null; } } diff --git a/seed/php-sdk/enum/src/InlinedRequest/InlinedRequestClient.php b/seed/php-sdk/enum/src/InlinedRequest/InlinedRequestClient.php index bf9d116bd94d..8721c7f3be2f 100644 --- a/seed/php-sdk/enum/src/InlinedRequest/InlinedRequestClient.php +++ b/seed/php-sdk/enum/src/InlinedRequest/InlinedRequestClient.php @@ -12,7 +12,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class InlinedRequestClient +class InlinedRequestClient { /** * @var array{ @@ -40,11 +40,10 @@ class InlinedRequestClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -62,7 +61,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function send(SendEnumInlinedRequest $request, ?array $options = null): void { + public function send(SendEnumInlinedRequest $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -75,12 +75,12 @@ public function send(SendEnumInlinedRequest $request, ?array $options = null): v $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/enum/src/InlinedRequest/Requests/SendEnumInlinedRequest.php b/seed/php-sdk/enum/src/InlinedRequest/Requests/SendEnumInlinedRequest.php index 021742d977aa..1fe5f1c892a7 100644 --- a/seed/php-sdk/enum/src/InlinedRequest/Requests/SendEnumInlinedRequest.php +++ b/seed/php-sdk/enum/src/InlinedRequest/Requests/SendEnumInlinedRequest.php @@ -37,7 +37,7 @@ class SendEnumInlinedRequest extends JsonSerializableType * |value-of * )|null $maybeOperandOrColor */ - #[JsonProperty('maybeOperandOrColor'), Union('string','null')] + #[JsonProperty('maybeOperandOrColor'), Union('string', 'null')] public string|null $maybeOperandOrColor; /** @@ -56,8 +56,10 @@ class SendEnumInlinedRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->operand = $values['operand'];$this->maybeOperand = $values['maybeOperand'] ?? null;$this->operandOrColor = $values['operandOrColor'];$this->maybeOperandOrColor = $values['maybeOperandOrColor'] ?? null; + ) { + $this->operand = $values['operand']; + $this->maybeOperand = $values['maybeOperand'] ?? null; + $this->operandOrColor = $values['operandOrColor']; + $this->maybeOperandOrColor = $values['maybeOperandOrColor'] ?? null; } } diff --git a/seed/php-sdk/enum/src/MultipartForm/MultipartFormClient.php b/seed/php-sdk/enum/src/MultipartForm/MultipartFormClient.php index 3e9232b0f73d..3f2d33886617 100644 --- a/seed/php-sdk/enum/src/MultipartForm/MultipartFormClient.php +++ b/seed/php-sdk/enum/src/MultipartForm/MultipartFormClient.php @@ -13,7 +13,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class MultipartFormClient +class MultipartFormClient { /** * @var array{ @@ -41,11 +41,10 @@ class MultipartFormClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -62,18 +61,19 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function multipartForm(MultipartFormRequest $request, ?array $options = null): void { + public function multipartForm(MultipartFormRequest $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); $body = new MultipartFormData(); $body->add(name: 'color', value: $request->color); - if ($request->maybeColor != null){ + if ($request->maybeColor != null) { $body->add(name: 'maybeColor', value: $request->maybeColor); } - foreach ($request->colorList as $element){ + foreach ($request->colorList as $element) { $body->add(name: 'colorList', value: $element); } - if ($request->maybeColorList != null){ - foreach ($request->maybeColorList as $element){ + if ($request->maybeColorList != null) { + foreach ($request->maybeColorList as $element) { $body->add(name: 'maybeColorList', value: $element); } } @@ -88,12 +88,12 @@ public function multipartForm(MultipartFormRequest $request, ?array $options = n $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/enum/src/MultipartForm/Requests/MultipartFormRequest.php b/seed/php-sdk/enum/src/MultipartForm/Requests/MultipartFormRequest.php index 28812646eb97..a5135522c4b6 100644 --- a/seed/php-sdk/enum/src/MultipartForm/Requests/MultipartFormRequest.php +++ b/seed/php-sdk/enum/src/MultipartForm/Requests/MultipartFormRequest.php @@ -43,8 +43,10 @@ class MultipartFormRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->color = $values['color'];$this->maybeColor = $values['maybeColor'] ?? null;$this->colorList = $values['colorList'];$this->maybeColorList = $values['maybeColorList'] ?? null; + ) { + $this->color = $values['color']; + $this->maybeColor = $values['maybeColor'] ?? null; + $this->colorList = $values['colorList']; + $this->maybeColorList = $values['maybeColorList'] ?? null; } } diff --git a/seed/php-sdk/enum/src/PathParam/PathParamClient.php b/seed/php-sdk/enum/src/PathParam/PathParamClient.php index 97ea5d3bcd26..a77ec900622e 100644 --- a/seed/php-sdk/enum/src/PathParam/PathParamClient.php +++ b/seed/php-sdk/enum/src/PathParam/PathParamClient.php @@ -13,7 +13,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class PathParamClient +class PathParamClient { /** * @var array{ @@ -41,11 +41,10 @@ class PathParamClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -67,7 +66,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function send(string $operand, string $operandOrColor, ?array $options = null): void { + public function send(string $operand, string $operandOrColor, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -79,12 +79,12 @@ public function send(string $operand, string $operandOrColor, ?array $options = $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/enum/src/QueryParam/QueryParamClient.php b/seed/php-sdk/enum/src/QueryParam/QueryParamClient.php index 58470e26b54f..e17f41903e25 100644 --- a/seed/php-sdk/enum/src/QueryParam/QueryParamClient.php +++ b/seed/php-sdk/enum/src/QueryParam/QueryParamClient.php @@ -13,7 +13,7 @@ use Psr\Http\Client\ClientExceptionInterface; use Seed\QueryParam\Requests\SendEnumListAsQueryParamRequest; -class QueryParamClient +class QueryParamClient { /** * @var array{ @@ -41,11 +41,10 @@ class QueryParamClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -63,15 +62,16 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function send(SendEnumAsQueryParamRequest $request, ?array $options = null): void { + public function send(SendEnumAsQueryParamRequest $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); $query = []; $query['operand'] = $request->operand; $query['operandOrColor'] = $request->operandOrColor; - if ($request->maybeOperand != null){ + if ($request->maybeOperand != null) { $query['maybeOperand'] = $request->maybeOperand; } - if ($request->maybeOperandOrColor != null){ + if ($request->maybeOperandOrColor != null) { $query['maybeOperandOrColor'] = $request->maybeOperandOrColor; } try { @@ -85,12 +85,12 @@ public function send(SendEnumAsQueryParamRequest $request, ?array $options = nul $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -121,15 +121,16 @@ public function send(SendEnumAsQueryParamRequest $request, ?array $options = nul * @throws SeedException * @throws SeedApiException */ - public function sendList(SendEnumListAsQueryParamRequest $request, ?array $options = null): void { + public function sendList(SendEnumListAsQueryParamRequest $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); $query = []; $query['operand'] = $request->operand; $query['operandOrColor'] = $request->operandOrColor; - if ($request->maybeOperand != null){ + if ($request->maybeOperand != null) { $query['maybeOperand'] = $request->maybeOperand; } - if ($request->maybeOperandOrColor != null){ + if ($request->maybeOperandOrColor != null) { $query['maybeOperandOrColor'] = $request->maybeOperandOrColor; } try { @@ -143,12 +144,12 @@ public function sendList(SendEnumListAsQueryParamRequest $request, ?array $optio $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/enum/src/QueryParam/Requests/SendEnumAsQueryParamRequest.php b/seed/php-sdk/enum/src/QueryParam/Requests/SendEnumAsQueryParamRequest.php index e1d88c83b711..97dbd19653cf 100644 --- a/seed/php-sdk/enum/src/QueryParam/Requests/SendEnumAsQueryParamRequest.php +++ b/seed/php-sdk/enum/src/QueryParam/Requests/SendEnumAsQueryParamRequest.php @@ -50,8 +50,10 @@ class SendEnumAsQueryParamRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->operand = $values['operand'];$this->maybeOperand = $values['maybeOperand'] ?? null;$this->operandOrColor = $values['operandOrColor'];$this->maybeOperandOrColor = $values['maybeOperandOrColor'] ?? null; + ) { + $this->operand = $values['operand']; + $this->maybeOperand = $values['maybeOperand'] ?? null; + $this->operandOrColor = $values['operandOrColor']; + $this->maybeOperandOrColor = $values['maybeOperandOrColor'] ?? null; } } diff --git a/seed/php-sdk/enum/src/QueryParam/Requests/SendEnumListAsQueryParamRequest.php b/seed/php-sdk/enum/src/QueryParam/Requests/SendEnumListAsQueryParamRequest.php index 8fdae07a660b..8882fcf7d9cc 100644 --- a/seed/php-sdk/enum/src/QueryParam/Requests/SendEnumListAsQueryParamRequest.php +++ b/seed/php-sdk/enum/src/QueryParam/Requests/SendEnumListAsQueryParamRequest.php @@ -50,8 +50,10 @@ class SendEnumListAsQueryParamRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->operand = $values['operand'];$this->maybeOperand = $values['maybeOperand'] ?? null;$this->operandOrColor = $values['operandOrColor'];$this->maybeOperandOrColor = $values['maybeOperandOrColor'] ?? null; + ) { + $this->operand = $values['operand']; + $this->maybeOperand = $values['maybeOperand'] ?? null; + $this->operandOrColor = $values['operandOrColor']; + $this->maybeOperandOrColor = $values['maybeOperandOrColor'] ?? null; } } diff --git a/seed/php-sdk/enum/src/SeedClient.php b/seed/php-sdk/enum/src/SeedClient.php index 35c4a0a0871a..b0b7480bc141 100644 --- a/seed/php-sdk/enum/src/SeedClient.php +++ b/seed/php-sdk/enum/src/SeedClient.php @@ -10,7 +10,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var HeadersClient $headers @@ -64,26 +64,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->headers = new HeadersClient($this->client, $this->options); $this->inlinedRequest = new InlinedRequestClient($this->client, $this->options); $this->multipartForm = new MultipartFormClient($this->client, $this->options); diff --git a/seed/php-sdk/enum/src/Types/Color.php b/seed/php-sdk/enum/src/Types/Color.php index 671adc86d093..d90c83c591f6 100644 --- a/seed/php-sdk/enum/src/Types/Color.php +++ b/seed/php-sdk/enum/src/Types/Color.php @@ -2,8 +2,8 @@ namespace Seed\Types; -enum Color - : string { +enum Color: string +{ case Red = "red"; case Blue = "blue"; } diff --git a/seed/php-sdk/enum/src/Types/EnumWithCustom.php b/seed/php-sdk/enum/src/Types/EnumWithCustom.php index 32eb40042dcb..47306aa3538e 100644 --- a/seed/php-sdk/enum/src/Types/EnumWithCustom.php +++ b/seed/php-sdk/enum/src/Types/EnumWithCustom.php @@ -2,8 +2,8 @@ namespace Seed\Types; -enum EnumWithCustom - : string { +enum EnumWithCustom: string +{ case Safe = "safe"; case Custom = "Custom"; } diff --git a/seed/php-sdk/enum/src/Types/EnumWithSpecialCharacters.php b/seed/php-sdk/enum/src/Types/EnumWithSpecialCharacters.php index a1828c99ee4e..d00b8543dd78 100644 --- a/seed/php-sdk/enum/src/Types/EnumWithSpecialCharacters.php +++ b/seed/php-sdk/enum/src/Types/EnumWithSpecialCharacters.php @@ -2,8 +2,8 @@ namespace Seed\Types; -enum EnumWithSpecialCharacters - : string { +enum EnumWithSpecialCharacters: string +{ case Bla = "\\\$bla"; case Yo = "\\\$yo"; } diff --git a/seed/php-sdk/enum/src/Types/Operand.php b/seed/php-sdk/enum/src/Types/Operand.php index a2bb43367db1..e750c5677cc2 100644 --- a/seed/php-sdk/enum/src/Types/Operand.php +++ b/seed/php-sdk/enum/src/Types/Operand.php @@ -2,8 +2,8 @@ namespace Seed\Types; -enum Operand - : string { +enum Operand: string +{ case GreaterThan = ">"; case EqualTo = "="; case LessThan = "less_than"; diff --git a/seed/php-sdk/enum/src/Types/SpecialEnum.php b/seed/php-sdk/enum/src/Types/SpecialEnum.php index 4409d5c850e3..57e1cbba4cd3 100644 --- a/seed/php-sdk/enum/src/Types/SpecialEnum.php +++ b/seed/php-sdk/enum/src/Types/SpecialEnum.php @@ -2,8 +2,8 @@ namespace Seed\Types; -enum SpecialEnum - : string { +enum SpecialEnum: string +{ case A = ""; case B = "Hello \\\"World\\\""; case C = "Hello 'World'"; diff --git a/seed/php-sdk/enum/src/Unknown/Types/Status.php b/seed/php-sdk/enum/src/Unknown/Types/Status.php index 21cd2db6b0d5..a081e2c59cd8 100644 --- a/seed/php-sdk/enum/src/Unknown/Types/Status.php +++ b/seed/php-sdk/enum/src/Unknown/Types/Status.php @@ -2,8 +2,8 @@ namespace Seed\Unknown\Types; -enum Status - : string { +enum Status: string +{ case Known = "Known"; case Unknown = "Unknown"; } diff --git a/seed/php-sdk/enum/src/Utils/File.php b/seed/php-sdk/enum/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/enum/src/Utils/File.php +++ b/seed/php-sdk/enum/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/enum/tests/Core/Client/RawClientTest.php b/seed/php-sdk/enum/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/enum/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/enum/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/enum/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/enum/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/enum/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/enum/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/enum/tests/Core/Json/EnumTest.php b/seed/php-sdk/enum/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/enum/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/enum/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/enum/tests/Core/Json/TraitTest.php b/seed/php-sdk/enum/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/enum/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/enum/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/enum/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/enum/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/enum/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/enum/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/error-property/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/error-property/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/error-property/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/error-property/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/error-property/src/Core/Client/RawClient.php b/seed/php-sdk/error-property/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/error-property/src/Core/Client/RawClient.php +++ b/seed/php-sdk/error-property/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/error-property/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/error-property/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/error-property/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/error-property/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/error-property/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/error-property/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/error-property/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/error-property/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/error-property/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/error-property/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/error-property/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/error-property/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/error-property/src/Core/Types/ArrayType.php b/seed/php-sdk/error-property/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/error-property/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/error-property/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/error-property/src/Core/Types/Constant.php b/seed/php-sdk/error-property/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/error-property/src/Core/Types/Constant.php +++ b/seed/php-sdk/error-property/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/error-property/src/Core/Types/Union.php b/seed/php-sdk/error-property/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/error-property/src/Core/Types/Union.php +++ b/seed/php-sdk/error-property/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/error-property/src/Errors/Types/PropertyBasedErrorTestBody.php b/seed/php-sdk/error-property/src/Errors/Types/PropertyBasedErrorTestBody.php index 3a4d21327803..0b41da52f997 100644 --- a/seed/php-sdk/error-property/src/Errors/Types/PropertyBasedErrorTestBody.php +++ b/seed/php-sdk/error-property/src/Errors/Types/PropertyBasedErrorTestBody.php @@ -20,15 +20,15 @@ class PropertyBasedErrorTestBody extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->message = $values['message']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/error-property/src/Exceptions/SeedApiException.php b/seed/php-sdk/error-property/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/error-property/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/error-property/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/error-property/src/Exceptions/SeedException.php b/seed/php-sdk/error-property/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/error-property/src/Exceptions/SeedException.php +++ b/seed/php-sdk/error-property/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/error-property/src/PropertyBasedError/PropertyBasedErrorClient.php b/seed/php-sdk/error-property/src/PropertyBasedError/PropertyBasedErrorClient.php index c9beee1dc51b..5fe60eeca6fe 100644 --- a/seed/php-sdk/error-property/src/PropertyBasedError/PropertyBasedErrorClient.php +++ b/seed/php-sdk/error-property/src/PropertyBasedError/PropertyBasedErrorClient.php @@ -13,7 +13,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class PropertyBasedErrorClient +class PropertyBasedErrorClient { /** * @var array{ @@ -41,11 +41,10 @@ class PropertyBasedErrorClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -65,7 +64,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function throwError(?array $options = null): string { + public function throwError(?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -77,15 +77,15 @@ public function throwError(?array $options = null): string { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/error-property/src/SeedClient.php b/seed/php-sdk/error-property/src/SeedClient.php index ceaca07e9c4b..5203169d2795 100644 --- a/seed/php-sdk/error-property/src/SeedClient.php +++ b/seed/php-sdk/error-property/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var PropertyBasedErrorClient $propertyBasedError @@ -40,26 +40,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->propertyBasedError = new PropertyBasedErrorClient($this->client, $this->options); } } diff --git a/seed/php-sdk/error-property/src/Utils/File.php b/seed/php-sdk/error-property/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/error-property/src/Utils/File.php +++ b/seed/php-sdk/error-property/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/error-property/tests/Core/Client/RawClientTest.php b/seed/php-sdk/error-property/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/error-property/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/error-property/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/error-property/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/error-property/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/error-property/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/error-property/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/error-property/tests/Core/Json/EnumTest.php b/seed/php-sdk/error-property/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/error-property/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/error-property/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/error-property/tests/Core/Json/TraitTest.php b/seed/php-sdk/error-property/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/error-property/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/error-property/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/error-property/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/error-property/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/error-property/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/error-property/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/errors/src/Commons/Types/ErrorBody.php b/seed/php-sdk/errors/src/Commons/Types/ErrorBody.php index c22a5dbed0e2..f2b6485f2696 100644 --- a/seed/php-sdk/errors/src/Commons/Types/ErrorBody.php +++ b/seed/php-sdk/errors/src/Commons/Types/ErrorBody.php @@ -27,15 +27,16 @@ class ErrorBody extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->message = $values['message'];$this->code = $values['code']; + ) { + $this->message = $values['message']; + $this->code = $values['code']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/errors/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/errors/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/errors/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/errors/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/errors/src/Core/Client/RawClient.php b/seed/php-sdk/errors/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/errors/src/Core/Client/RawClient.php +++ b/seed/php-sdk/errors/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/errors/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/errors/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/errors/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/errors/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/errors/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/errors/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/errors/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/errors/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/errors/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/errors/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/errors/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/errors/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/errors/src/Core/Types/ArrayType.php b/seed/php-sdk/errors/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/errors/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/errors/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/errors/src/Core/Types/Constant.php b/seed/php-sdk/errors/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/errors/src/Core/Types/Constant.php +++ b/seed/php-sdk/errors/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/errors/src/Core/Types/Union.php b/seed/php-sdk/errors/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/errors/src/Core/Types/Union.php +++ b/seed/php-sdk/errors/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/errors/src/Exceptions/SeedApiException.php b/seed/php-sdk/errors/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/errors/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/errors/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/errors/src/Exceptions/SeedException.php b/seed/php-sdk/errors/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/errors/src/Exceptions/SeedException.php +++ b/seed/php-sdk/errors/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/errors/src/SeedClient.php b/seed/php-sdk/errors/src/SeedClient.php index d77173ccfbb3..2d5df69a584a 100644 --- a/seed/php-sdk/errors/src/SeedClient.php +++ b/seed/php-sdk/errors/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var SimpleClient $simple @@ -40,26 +40,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->simple = new SimpleClient($this->client, $this->options); } } diff --git a/seed/php-sdk/errors/src/Simple/SimpleClient.php b/seed/php-sdk/errors/src/Simple/SimpleClient.php index bb212563f4f4..f6091800b6da 100644 --- a/seed/php-sdk/errors/src/Simple/SimpleClient.php +++ b/seed/php-sdk/errors/src/Simple/SimpleClient.php @@ -14,7 +14,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class SimpleClient +class SimpleClient { /** * @var array{ @@ -42,11 +42,10 @@ class SimpleClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -65,7 +64,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function fooWithoutEndpointError(FooRequest $request, ?array $options = null): FooResponse { + public function fooWithoutEndpointError(FooRequest $request, ?array $options = null): FooResponse + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -78,15 +78,15 @@ public function fooWithoutEndpointError(FooRequest $request, ?array $options = n $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return FooResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -118,7 +118,8 @@ public function fooWithoutEndpointError(FooRequest $request, ?array $options = n * @throws SeedException * @throws SeedApiException */ - public function foo(FooRequest $request, ?array $options = null): FooResponse { + public function foo(FooRequest $request, ?array $options = null): FooResponse + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -131,15 +132,15 @@ public function foo(FooRequest $request, ?array $options = null): FooResponse { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return FooResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -171,7 +172,8 @@ public function foo(FooRequest $request, ?array $options = null): FooResponse { * @throws SeedException * @throws SeedApiException */ - public function fooWithExamples(FooRequest $request, ?array $options = null): FooResponse { + public function fooWithExamples(FooRequest $request, ?array $options = null): FooResponse + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -184,15 +186,15 @@ public function fooWithExamples(FooRequest $request, ?array $options = null): Fo $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return FooResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/errors/src/Simple/Types/FooRequest.php b/seed/php-sdk/errors/src/Simple/Types/FooRequest.php index 566bb415c75e..5bbafd74626c 100644 --- a/seed/php-sdk/errors/src/Simple/Types/FooRequest.php +++ b/seed/php-sdk/errors/src/Simple/Types/FooRequest.php @@ -20,15 +20,15 @@ class FooRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->bar = $values['bar']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/errors/src/Simple/Types/FooResponse.php b/seed/php-sdk/errors/src/Simple/Types/FooResponse.php index 3360499e6c8f..87eef38182a7 100644 --- a/seed/php-sdk/errors/src/Simple/Types/FooResponse.php +++ b/seed/php-sdk/errors/src/Simple/Types/FooResponse.php @@ -20,15 +20,15 @@ class FooResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->bar = $values['bar']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/errors/src/Utils/File.php b/seed/php-sdk/errors/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/errors/src/Utils/File.php +++ b/seed/php-sdk/errors/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/errors/tests/Core/Client/RawClientTest.php b/seed/php-sdk/errors/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/errors/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/errors/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/errors/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/errors/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/errors/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/errors/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/errors/tests/Core/Json/EnumTest.php b/seed/php-sdk/errors/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/errors/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/errors/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/errors/tests/Core/Json/TraitTest.php b/seed/php-sdk/errors/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/errors/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/errors/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/errors/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/errors/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/errors/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/errors/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/examples/no-custom-config/src/Commons/Types/Types/Data.php b/seed/php-sdk/examples/no-custom-config/src/Commons/Types/Types/Data.php index 7e78dbc4f09d..3565c94ce984 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Commons/Types/Types/Data.php +++ b/seed/php-sdk/examples/no-custom-config/src/Commons/Types/Types/Data.php @@ -40,16 +40,17 @@ class Data extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param string $string * @return Data */ - public static function string(string $string): Data { + public static function string(string $string): Data + { return new Data([ 'type' => 'string', 'value' => $string, @@ -60,7 +61,8 @@ public static function string(string $string): Data { * @param string $base64 * @return Data */ - public static function base64(string $base64): Data { + public static function base64(string $base64): Data + { return new Data([ 'type' => 'base64', 'value' => $base64, @@ -70,61 +72,67 @@ public static function base64(string $base64): Data { /** * @return bool */ - public function isString(): bool { - return is_string($this->value)&& $this->type === 'string'; + public function isString(): bool + { + return is_string($this->value) && $this->type === 'string'; } /** * @return string */ - public function asString(): string { - if (!(is_string($this->value)&& $this->type === 'string')){ + public function asString(): string + { + if (!(is_string($this->value) && $this->type === 'string')) { throw new Exception( "Expected string; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isBase64(): bool { - return is_string($this->value)&& $this->type === 'base64'; + public function isBase64(): bool + { + return is_string($this->value) && $this->type === 'base64'; } /** * @return string */ - public function asBase64(): string { - if (!(is_string($this->value)&& $this->type === 'base64')){ + public function asBase64(): string + { + if (!(is_string($this->value) && $this->type === 'base64')) { throw new Exception( "Expected base64; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'string': $value = $this->value; $result['string'] = $value; @@ -135,26 +143,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -163,38 +172,39 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'string': - if (!array_key_exists('string', $data)){ + if (!array_key_exists('string', $data)) { throw new Exception( "JSON data is missing property 'string'", ); } - + $args['value'] = $data['string']; break; case 'base64': - if (!array_key_exists('base64', $data)){ + if (!array_key_exists('base64', $data)) { throw new Exception( "JSON data is missing property 'base64'", ); } - + $args['value'] = $data['base64']; break; case '_unknown': @@ -202,7 +212,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/examples/no-custom-config/src/Commons/Types/Types/EventInfo.php b/seed/php-sdk/examples/no-custom-config/src/Commons/Types/Types/EventInfo.php index 05fa6f69b326..51bb58d0305d 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Commons/Types/Types/EventInfo.php +++ b/seed/php-sdk/examples/no-custom-config/src/Commons/Types/Types/EventInfo.php @@ -42,16 +42,17 @@ class EventInfo extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param Metadata $metadata * @return EventInfo */ - public static function metadata(Metadata $metadata): EventInfo { + public static function metadata(Metadata $metadata): EventInfo + { return new EventInfo([ 'type' => 'metadata', 'value' => $metadata, @@ -62,7 +63,8 @@ public static function metadata(Metadata $metadata): EventInfo { * @param string $tag * @return EventInfo */ - public static function tag(string $tag): EventInfo { + public static function tag(string $tag): EventInfo + { return new EventInfo([ 'type' => 'tag', 'value' => $tag, @@ -72,61 +74,67 @@ public static function tag(string $tag): EventInfo { /** * @return bool */ - public function isMetadata(): bool { - return $this->value instanceof Metadata&& $this->type === 'metadata'; + public function isMetadata(): bool + { + return $this->value instanceof Metadata && $this->type === 'metadata'; } /** * @return Metadata */ - public function asMetadata(): Metadata { - if (!($this->value instanceof Metadata&& $this->type === 'metadata')){ + public function asMetadata(): Metadata + { + if (!($this->value instanceof Metadata && $this->type === 'metadata')) { throw new Exception( "Expected metadata; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isTag(): bool { - return is_string($this->value)&& $this->type === 'tag'; + public function isTag(): bool + { + return is_string($this->value) && $this->type === 'tag'; } /** * @return string */ - public function asTag(): string { - if (!(is_string($this->value)&& $this->type === 'tag')){ + public function asTag(): string + { + if (!(is_string($this->value) && $this->type === 'tag')) { throw new Exception( "Expected tag; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'metadata': $value = $this->asMetadata()->jsonSerialize(); $result = array_merge($value, $result); @@ -137,26 +145,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -165,32 +174,33 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'metadata': $args['value'] = Metadata::jsonDeserialize($data); break; case 'tag': - if (!array_key_exists('tag', $data)){ + if (!array_key_exists('tag', $data)) { throw new Exception( "JSON data is missing property 'tag'", ); } - + $args['value'] = $data['tag']; break; case '_unknown': @@ -198,7 +208,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/examples/no-custom-config/src/Commons/Types/Types/Metadata.php b/seed/php-sdk/examples/no-custom-config/src/Commons/Types/Types/Metadata.php index 5b01c883d1b7..80b27e6b8775 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Commons/Types/Types/Metadata.php +++ b/seed/php-sdk/examples/no-custom-config/src/Commons/Types/Types/Metadata.php @@ -35,15 +35,17 @@ class Metadata extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->data = $values['data'] ?? null;$this->jsonString = $values['jsonString'] ?? null; + ) { + $this->id = $values['id']; + $this->data = $values['data'] ?? null; + $this->jsonString = $values['jsonString'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/examples/no-custom-config/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/examples/no-custom-config/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/examples/no-custom-config/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/examples/no-custom-config/src/Core/Client/RawClient.php b/seed/php-sdk/examples/no-custom-config/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Core/Client/RawClient.php +++ b/seed/php-sdk/examples/no-custom-config/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/examples/no-custom-config/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/examples/no-custom-config/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/examples/no-custom-config/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/examples/no-custom-config/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/examples/no-custom-config/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/examples/no-custom-config/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/examples/no-custom-config/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/examples/no-custom-config/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/examples/no-custom-config/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/examples/no-custom-config/src/Core/Types/ArrayType.php b/seed/php-sdk/examples/no-custom-config/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/examples/no-custom-config/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/examples/no-custom-config/src/Core/Types/Constant.php b/seed/php-sdk/examples/no-custom-config/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Core/Types/Constant.php +++ b/seed/php-sdk/examples/no-custom-config/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/examples/no-custom-config/src/Core/Types/Union.php b/seed/php-sdk/examples/no-custom-config/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Core/Types/Union.php +++ b/seed/php-sdk/examples/no-custom-config/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/examples/no-custom-config/src/Environments.php b/seed/php-sdk/examples/no-custom-config/src/Environments.php index fd74f6324708..f49ef601cec1 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Environments.php +++ b/seed/php-sdk/examples/no-custom-config/src/Environments.php @@ -2,8 +2,8 @@ namespace Seed; -enum Environments - : string { +enum Environments: string +{ case Production = "https://production.com/api"; case Staging = "https://staging.com/api"; } diff --git a/seed/php-sdk/examples/no-custom-config/src/Exceptions/SeedApiException.php b/seed/php-sdk/examples/no-custom-config/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/examples/no-custom-config/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/examples/no-custom-config/src/Exceptions/SeedException.php b/seed/php-sdk/examples/no-custom-config/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Exceptions/SeedException.php +++ b/seed/php-sdk/examples/no-custom-config/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/examples/no-custom-config/src/File/FileClient.php b/seed/php-sdk/examples/no-custom-config/src/File/FileClient.php index 45aad0840c9b..6740a72fa4d1 100644 --- a/seed/php-sdk/examples/no-custom-config/src/File/FileClient.php +++ b/seed/php-sdk/examples/no-custom-config/src/File/FileClient.php @@ -7,7 +7,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class FileClient +class FileClient { /** * @var NotificationClient $notification @@ -45,11 +45,10 @@ class FileClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; $this->notification = new NotificationClient($this->client, $this->options); diff --git a/seed/php-sdk/examples/no-custom-config/src/File/Notification/NotificationClient.php b/seed/php-sdk/examples/no-custom-config/src/File/Notification/NotificationClient.php index 2622321005e3..7d6de8b9fe78 100644 --- a/seed/php-sdk/examples/no-custom-config/src/File/Notification/NotificationClient.php +++ b/seed/php-sdk/examples/no-custom-config/src/File/Notification/NotificationClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class NotificationClient +class NotificationClient { /** * @var ServiceClient $service @@ -39,11 +39,10 @@ class NotificationClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; $this->service = new ServiceClient($this->client, $this->options); diff --git a/seed/php-sdk/examples/no-custom-config/src/File/Notification/Service/ServiceClient.php b/seed/php-sdk/examples/no-custom-config/src/File/Notification/Service/ServiceClient.php index fe0d50b635c2..41f74acdd82c 100644 --- a/seed/php-sdk/examples/no-custom-config/src/File/Notification/Service/ServiceClient.php +++ b/seed/php-sdk/examples/no-custom-config/src/File/Notification/Service/ServiceClient.php @@ -13,7 +13,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class ServiceClient +class ServiceClient { /** * @var array{ @@ -41,11 +41,10 @@ class ServiceClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -64,7 +63,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getException(string $notificationId, ?array $options = null): Exception { + public function getException(string $notificationId, ?array $options = null): Exception + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -76,15 +76,15 @@ public function getException(string $notificationId, ?array $options = null): Ex $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return Exception::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/examples/no-custom-config/src/File/Service/Requests/GetFileRequest.php b/seed/php-sdk/examples/no-custom-config/src/File/Service/Requests/GetFileRequest.php index 9278b4039be6..37b5d16c020f 100644 --- a/seed/php-sdk/examples/no-custom-config/src/File/Service/Requests/GetFileRequest.php +++ b/seed/php-sdk/examples/no-custom-config/src/File/Service/Requests/GetFileRequest.php @@ -18,8 +18,7 @@ class GetFileRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->xFileApiVersion = $values['xFileApiVersion']; } } diff --git a/seed/php-sdk/examples/no-custom-config/src/File/Service/ServiceClient.php b/seed/php-sdk/examples/no-custom-config/src/File/Service/ServiceClient.php index 9c2728f78084..207f666e6d4e 100644 --- a/seed/php-sdk/examples/no-custom-config/src/File/Service/ServiceClient.php +++ b/seed/php-sdk/examples/no-custom-config/src/File/Service/ServiceClient.php @@ -14,7 +14,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class ServiceClient +class ServiceClient { /** * @var array{ @@ -42,11 +42,10 @@ class ServiceClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -68,7 +67,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getFile(string $filename, GetFileRequest $request, ?array $options = null): File { + public function getFile(string $filename, GetFileRequest $request, ?array $options = null): File + { $options = array_merge($this->options, $options ?? []); $headers = []; $headers['X-File-API-Version'] = $request->xFileApiVersion; @@ -83,15 +83,15 @@ public function getFile(string $filename, GetFileRequest $request, ?array $optio $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return File::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/examples/no-custom-config/src/Health/HealthClient.php b/seed/php-sdk/examples/no-custom-config/src/Health/HealthClient.php index 6e88ae7f3219..1891d8e7debe 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Health/HealthClient.php +++ b/seed/php-sdk/examples/no-custom-config/src/Health/HealthClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class HealthClient +class HealthClient { /** * @var ServiceClient $service @@ -39,11 +39,10 @@ class HealthClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; $this->service = new ServiceClient($this->client, $this->options); diff --git a/seed/php-sdk/examples/no-custom-config/src/Health/Service/ServiceClient.php b/seed/php-sdk/examples/no-custom-config/src/Health/Service/ServiceClient.php index fffd85e9a51f..68fb11ecc5c8 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Health/Service/ServiceClient.php +++ b/seed/php-sdk/examples/no-custom-config/src/Health/Service/ServiceClient.php @@ -13,7 +13,7 @@ use Seed\Core\Json\JsonDecoder; use JsonException; -class ServiceClient +class ServiceClient { /** * @var array{ @@ -41,11 +41,10 @@ class ServiceClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -65,7 +64,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function check(string $id, ?array $options = null): void { + public function check(string $id, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -77,12 +77,12 @@ public function check(string $id, ?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -115,7 +115,8 @@ public function check(string $id, ?array $options = null): void { * @throws SeedException * @throws SeedApiException */ - public function ping(?array $options = null): bool { + public function ping(?array $options = null): bool + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -127,15 +128,15 @@ public function ping(?array $options = null): bool { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeBool($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/examples/no-custom-config/src/SeedClient.php b/seed/php-sdk/examples/no-custom-config/src/SeedClient.php index 3ecaa568d991..6946f151d021 100644 --- a/seed/php-sdk/examples/no-custom-config/src/SeedClient.php +++ b/seed/php-sdk/examples/no-custom-config/src/SeedClient.php @@ -19,7 +19,7 @@ use Seed\Types\ComplexType; use Seed\Types\Identifier; -class SeedClient +class SeedClient { /** * @var FileClient $file @@ -65,29 +65,28 @@ class SeedClient public function __construct( ?string $token = null, ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - if ($token != null){ + if ($token != null) { $defaultHeaders['Authorization'] = "Bearer $token"; } - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->file = new FileClient($this->client, $this->options); $this->health = new HealthClient($this->client, $this->options); $this->service = new ServiceClient($this->client, $this->options); @@ -107,7 +106,8 @@ public function __construct( * @throws SeedException * @throws SeedApiException */ - public function echo_(string $request, ?array $options = null): string { + public function echo_(string $request, ?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -120,15 +120,15 @@ public function echo_(string $request, ?array $options = null): string { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -163,7 +163,8 @@ public function echo_(string $request, ?array $options = null): string { * @throws SeedException * @throws SeedApiException */ - public function createType(string $request, ?array $options = null): Identifier { + public function createType(string $request, ?array $options = null): Identifier + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -176,15 +177,15 @@ public function createType(string $request, ?array $options = null): Identifier $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return Identifier::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/examples/no-custom-config/src/Service/Requests/GetMetadataRequest.php b/seed/php-sdk/examples/no-custom-config/src/Service/Requests/GetMetadataRequest.php index 50642b0ae945..fa997e5d5b6f 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Service/Requests/GetMetadataRequest.php +++ b/seed/php-sdk/examples/no-custom-config/src/Service/Requests/GetMetadataRequest.php @@ -30,8 +30,9 @@ class GetMetadataRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->shallow = $values['shallow'] ?? null;$this->tag = $values['tag'] ?? null;$this->xApiVersion = $values['xApiVersion']; + ) { + $this->shallow = $values['shallow'] ?? null; + $this->tag = $values['tag'] ?? null; + $this->xApiVersion = $values['xApiVersion']; } } diff --git a/seed/php-sdk/examples/no-custom-config/src/Service/ServiceClient.php b/seed/php-sdk/examples/no-custom-config/src/Service/ServiceClient.php index 5ed7880920c8..24096998ecf3 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Service/ServiceClient.php +++ b/seed/php-sdk/examples/no-custom-config/src/Service/ServiceClient.php @@ -19,7 +19,7 @@ use Seed\Types\Types\Response; use Seed\Types\Types\RefreshTokenRequest; -class ServiceClient +class ServiceClient { /** * @var array{ @@ -47,11 +47,10 @@ class ServiceClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -70,7 +69,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getMovie(string $movieId, ?array $options = null): Movie { + public function getMovie(string $movieId, ?array $options = null): Movie + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -82,15 +82,15 @@ public function getMovie(string $movieId, ?array $options = null): Movie { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return Movie::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -122,7 +122,8 @@ public function getMovie(string $movieId, ?array $options = null): Movie { * @throws SeedException * @throws SeedApiException */ - public function createMovie(Movie $request, ?array $options = null): string { + public function createMovie(Movie $request, ?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -135,15 +136,15 @@ public function createMovie(Movie $request, ?array $options = null): string { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -175,13 +176,14 @@ public function createMovie(Movie $request, ?array $options = null): string { * @throws SeedException * @throws SeedApiException */ - public function getMetadata(GetMetadataRequest $request, ?array $options = null): Metadata { + public function getMetadata(GetMetadataRequest $request, ?array $options = null): Metadata + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->shallow != null){ + if ($request->shallow != null) { $query['shallow'] = $request->shallow; } - if ($request->tag != null){ + if ($request->tag != null) { $query['tag'] = $request->tag; } $headers = []; @@ -198,15 +200,15 @@ public function getMetadata(GetMetadataRequest $request, ?array $options = null) $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return Metadata::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -238,7 +240,8 @@ public function getMetadata(GetMetadataRequest $request, ?array $options = null) * @throws SeedException * @throws SeedApiException */ - public function createBigEntity(BigEntity $request, ?array $options = null): Response { + public function createBigEntity(BigEntity $request, ?array $options = null): Response + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -251,15 +254,15 @@ public function createBigEntity(BigEntity $request, ?array $options = null): Res $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return Response::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -290,7 +293,8 @@ public function createBigEntity(BigEntity $request, ?array $options = null): Res * @throws SeedException * @throws SeedApiException */ - public function refreshToken(?RefreshTokenRequest $request = null, ?array $options = null): void { + public function refreshToken(?RefreshTokenRequest $request = null, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -303,12 +307,12 @@ public function refreshToken(?RefreshTokenRequest $request = null, ?array $optio $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/examples/no-custom-config/src/Types/BasicType.php b/seed/php-sdk/examples/no-custom-config/src/Types/BasicType.php index 993439fce90c..3c77dac7cada 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Types/BasicType.php +++ b/seed/php-sdk/examples/no-custom-config/src/Types/BasicType.php @@ -2,8 +2,8 @@ namespace Seed\Types; -enum BasicType - : string { +enum BasicType: string +{ case Primitive = "primitive"; case Literal = "literal"; } diff --git a/seed/php-sdk/examples/no-custom-config/src/Types/ComplexType.php b/seed/php-sdk/examples/no-custom-config/src/Types/ComplexType.php index b223bfddc00e..0063ebb7422b 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Types/ComplexType.php +++ b/seed/php-sdk/examples/no-custom-config/src/Types/ComplexType.php @@ -2,8 +2,8 @@ namespace Seed\Types; -enum ComplexType - : string { +enum ComplexType: string +{ case Object = "object"; case Union = "union"; case Unknown = "unknown"; diff --git a/seed/php-sdk/examples/no-custom-config/src/Types/Identifier.php b/seed/php-sdk/examples/no-custom-config/src/Types/Identifier.php index 8ea0b83be86b..b1dc23ff17f6 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Types/Identifier.php +++ b/seed/php-sdk/examples/no-custom-config/src/Types/Identifier.php @@ -40,15 +40,17 @@ class Identifier extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value'];$this->label = $values['label']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; + $this->label = $values['label']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/examples/no-custom-config/src/Types/Traits/Movie.php b/seed/php-sdk/examples/no-custom-config/src/Types/Traits/Movie.php index 59d8a5590cd6..5193090f6b76 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Types/Traits/Movie.php +++ b/seed/php-sdk/examples/no-custom-config/src/Types/Traits/Movie.php @@ -17,7 +17,7 @@ * @property array $metadata * @property int $revenue */ -trait Movie +trait Movie { /** * @var string $id diff --git a/seed/php-sdk/examples/no-custom-config/src/Types/TypeWithSingleCharPropertyEqualToTypeStartingLetter.php b/seed/php-sdk/examples/no-custom-config/src/Types/TypeWithSingleCharPropertyEqualToTypeStartingLetter.php index e709ec79b0ff..f3b36bb6a39f 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Types/TypeWithSingleCharPropertyEqualToTypeStartingLetter.php +++ b/seed/php-sdk/examples/no-custom-config/src/Types/TypeWithSingleCharPropertyEqualToTypeStartingLetter.php @@ -27,15 +27,16 @@ class TypeWithSingleCharPropertyEqualToTypeStartingLetter extends JsonSerializab */ public function __construct( array $values, - ) - { - $this->t = $values['t'];$this->ty = $values['ty']; + ) { + $this->t = $values['t']; + $this->ty = $values['ty']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/examples/no-custom-config/src/Types/Types/Actor.php b/seed/php-sdk/examples/no-custom-config/src/Types/Types/Actor.php index 454ec34fbc0d..820c8c16fb4f 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Types/Types/Actor.php +++ b/seed/php-sdk/examples/no-custom-config/src/Types/Types/Actor.php @@ -27,15 +27,16 @@ class Actor extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->id = $values['id']; + ) { + $this->name = $values['name']; + $this->id = $values['id']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/examples/no-custom-config/src/Types/Types/Actress.php b/seed/php-sdk/examples/no-custom-config/src/Types/Types/Actress.php index 634f66792ece..1b31a539a2ed 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Types/Types/Actress.php +++ b/seed/php-sdk/examples/no-custom-config/src/Types/Types/Actress.php @@ -27,15 +27,16 @@ class Actress extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->id = $values['id']; + ) { + $this->name = $values['name']; + $this->id = $values['id']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/examples/no-custom-config/src/Types/Types/BigEntity.php b/seed/php-sdk/examples/no-custom-config/src/Types/Types/BigEntity.php index 279e857261ee..5418e315e379 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Types/Types/BigEntity.php +++ b/seed/php-sdk/examples/no-custom-config/src/Types/Types/BigEntity.php @@ -17,7 +17,7 @@ class BigEntity extends JsonSerializableType * |StuntDouble * )|null $castMember */ - #[JsonProperty('castMember'), Union(Actor::class,Actress::class,StuntDouble::class,'null')] + #[JsonProperty('castMember'), Union(Actor::class, Actress::class, StuntDouble::class, 'null')] public Actor|Actress|StuntDouble|null $castMember; /** @@ -115,15 +115,27 @@ class BigEntity extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->castMember = $values['castMember'] ?? null;$this->extendedMovie = $values['extendedMovie'] ?? null;$this->entity = $values['entity'] ?? null;$this->metadata = $values['metadata'] ?? null;$this->commonMetadata = $values['commonMetadata'] ?? null;$this->eventInfo = $values['eventInfo'] ?? null;$this->data = $values['data'] ?? null;$this->migration = $values['migration'] ?? null;$this->exception = $values['exception'] ?? null;$this->test = $values['test'] ?? null;$this->node = $values['node'] ?? null;$this->directory = $values['directory'] ?? null;$this->moment = $values['moment'] ?? null; + ) { + $this->castMember = $values['castMember'] ?? null; + $this->extendedMovie = $values['extendedMovie'] ?? null; + $this->entity = $values['entity'] ?? null; + $this->metadata = $values['metadata'] ?? null; + $this->commonMetadata = $values['commonMetadata'] ?? null; + $this->eventInfo = $values['eventInfo'] ?? null; + $this->data = $values['data'] ?? null; + $this->migration = $values['migration'] ?? null; + $this->exception = $values['exception'] ?? null; + $this->test = $values['test'] ?? null; + $this->node = $values['node'] ?? null; + $this->directory = $values['directory'] ?? null; + $this->moment = $values['moment'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/examples/no-custom-config/src/Types/Types/CronJob.php b/seed/php-sdk/examples/no-custom-config/src/Types/Types/CronJob.php index 3771a8e185bd..0e42a5401c1e 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Types/Types/CronJob.php +++ b/seed/php-sdk/examples/no-custom-config/src/Types/Types/CronJob.php @@ -20,15 +20,15 @@ class CronJob extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->expression = $values['expression']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/examples/no-custom-config/src/Types/Types/Directory.php b/seed/php-sdk/examples/no-custom-config/src/Types/Types/Directory.php index 1acc01ad4d89..23d8d3268d63 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Types/Types/Directory.php +++ b/seed/php-sdk/examples/no-custom-config/src/Types/Types/Directory.php @@ -35,15 +35,17 @@ class Directory extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->files = $values['files'] ?? null;$this->directories = $values['directories'] ?? null; + ) { + $this->name = $values['name']; + $this->files = $values['files'] ?? null; + $this->directories = $values['directories'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/examples/no-custom-config/src/Types/Types/Entity.php b/seed/php-sdk/examples/no-custom-config/src/Types/Types/Entity.php index 11897ec989c3..2bb9c116fdea 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Types/Types/Entity.php +++ b/seed/php-sdk/examples/no-custom-config/src/Types/Types/Entity.php @@ -35,15 +35,16 @@ class Entity extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->type = $values['type'];$this->name = $values['name']; + ) { + $this->type = $values['type']; + $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/examples/no-custom-config/src/Types/Types/ExceptionInfo.php b/seed/php-sdk/examples/no-custom-config/src/Types/Types/ExceptionInfo.php index 1faa323c71f9..b0718a06ab85 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Types/Types/ExceptionInfo.php +++ b/seed/php-sdk/examples/no-custom-config/src/Types/Types/ExceptionInfo.php @@ -34,15 +34,17 @@ class ExceptionInfo extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->exceptionType = $values['exceptionType'];$this->exceptionMessage = $values['exceptionMessage'];$this->exceptionStacktrace = $values['exceptionStacktrace']; + ) { + $this->exceptionType = $values['exceptionType']; + $this->exceptionMessage = $values['exceptionMessage']; + $this->exceptionStacktrace = $values['exceptionStacktrace']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/examples/no-custom-config/src/Types/Types/ExtendedMovie.php b/seed/php-sdk/examples/no-custom-config/src/Types/Types/ExtendedMovie.php index de70f53e4d02..d2e823e54c38 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Types/Types/ExtendedMovie.php +++ b/seed/php-sdk/examples/no-custom-config/src/Types/Types/ExtendedMovie.php @@ -34,15 +34,25 @@ class ExtendedMovie extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->prequel = $values['prequel'] ?? null;$this->title = $values['title'];$this->from = $values['from'];$this->rating = $values['rating'];$this->type = $values['type'];$this->tag = $values['tag'];$this->book = $values['book'] ?? null;$this->metadata = $values['metadata'];$this->revenue = $values['revenue'];$this->cast = $values['cast']; + ) { + $this->id = $values['id']; + $this->prequel = $values['prequel'] ?? null; + $this->title = $values['title']; + $this->from = $values['from']; + $this->rating = $values['rating']; + $this->type = $values['type']; + $this->tag = $values['tag']; + $this->book = $values['book'] ?? null; + $this->metadata = $values['metadata']; + $this->revenue = $values['revenue']; + $this->cast = $values['cast']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/examples/no-custom-config/src/Types/Types/File.php b/seed/php-sdk/examples/no-custom-config/src/Types/Types/File.php index d1e82d62725a..ba6867fd4231 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Types/Types/File.php +++ b/seed/php-sdk/examples/no-custom-config/src/Types/Types/File.php @@ -27,15 +27,16 @@ class File extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->contents = $values['contents']; + ) { + $this->name = $values['name']; + $this->contents = $values['contents']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/examples/no-custom-config/src/Types/Types/Metadata.php b/seed/php-sdk/examples/no-custom-config/src/Types/Types/Metadata.php index 7f68dbb8a150..7d3d6d838f44 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Types/Types/Metadata.php +++ b/seed/php-sdk/examples/no-custom-config/src/Types/Types/Metadata.php @@ -56,9 +56,11 @@ class Metadata extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->extra = $values['extra'];$this->tags = $values['tags'];$this->type = $values['type'];$this->value = $values['value']; + ) { + $this->extra = $values['extra']; + $this->tags = $values['tags']; + $this->type = $values['type']; + $this->value = $values['value']; } /** @@ -67,7 +69,8 @@ private function __construct( * @param string $html * @return Metadata */ - public static function html(array $extra, array $tags, string $html): Metadata { + public static function html(array $extra, array $tags, string $html): Metadata + { return new Metadata([ 'extra' => $extra, 'tags' => $tags, @@ -82,7 +85,8 @@ public static function html(array $extra, array $tags, string $html): Metadata { * @param string $markdown * @return Metadata */ - public static function markdown(array $extra, array $tags, string $markdown): Metadata { + public static function markdown(array $extra, array $tags, string $markdown): Metadata + { return new Metadata([ 'extra' => $extra, 'tags' => $tags, @@ -94,61 +98,67 @@ public static function markdown(array $extra, array $tags, string $markdown): Me /** * @return bool */ - public function isHtml(): bool { - return is_string($this->value)&& $this->type === 'html'; + public function isHtml(): bool + { + return is_string($this->value) && $this->type === 'html'; } /** * @return string */ - public function asHtml(): string { - if (!(is_string($this->value)&& $this->type === 'html')){ + public function asHtml(): string + { + if (!(is_string($this->value) && $this->type === 'html')) { throw new Exception( "Expected html; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isMarkdown(): bool { - return is_string($this->value)&& $this->type === 'markdown'; + public function isMarkdown(): bool + { + return is_string($this->value) && $this->type === 'markdown'; } /** * @return string */ - public function asMarkdown(): string { - if (!(is_string($this->value)&& $this->type === 'markdown')){ + public function asMarkdown(): string + { + if (!(is_string($this->value) && $this->type === 'markdown')) { throw new Exception( "Expected markdown; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'html': $value = $this->value; $result['html'] = $value; @@ -159,26 +169,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -187,62 +198,63 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('extra', $data)){ + if (!array_key_exists('extra', $data)) { throw new Exception( "JSON data is missing property 'extra'", ); } - if (!(is_array($data['extra']))){ + if (!(is_array($data['extra']))) { throw new Exception( "Expected property 'extra' in JSON data to be map, instead received " . get_debug_type($data['extra']), ); } $args['extra'] = $data['extra']; - - if (!array_key_exists('tags', $data)){ + + if (!array_key_exists('tags', $data)) { throw new Exception( "JSON data is missing property 'tags'", ); } - if (!(is_array($data['tags']))){ + if (!(is_array($data['tags']))) { throw new Exception( "Expected property 'tags' in JSON data to be array, instead received " . get_debug_type($data['tags']), ); } $args['tags'] = $data['tags']; - - if (!array_key_exists('type', $data)){ + + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'html': - if (!array_key_exists('html', $data)){ + if (!array_key_exists('html', $data)) { throw new Exception( "JSON data is missing property 'html'", ); } - + $args['value'] = $data['html']; break; case 'markdown': - if (!array_key_exists('markdown', $data)){ + if (!array_key_exists('markdown', $data)) { throw new Exception( "JSON data is missing property 'markdown'", ); } - + $args['value'] = $data['markdown']; break; case '_unknown': @@ -250,7 +262,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/examples/no-custom-config/src/Types/Types/Migration.php b/seed/php-sdk/examples/no-custom-config/src/Types/Types/Migration.php index 60dc11864d94..461c76a11b06 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Types/Types/Migration.php +++ b/seed/php-sdk/examples/no-custom-config/src/Types/Types/Migration.php @@ -27,15 +27,16 @@ class Migration extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->status = $values['status']; + ) { + $this->name = $values['name']; + $this->status = $values['status']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/examples/no-custom-config/src/Types/Types/MigrationStatus.php b/seed/php-sdk/examples/no-custom-config/src/Types/Types/MigrationStatus.php index b1c21bac9a67..ae94c6d619e9 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Types/Types/MigrationStatus.php +++ b/seed/php-sdk/examples/no-custom-config/src/Types/Types/MigrationStatus.php @@ -2,8 +2,8 @@ namespace Seed\Types\Types; -enum MigrationStatus - : string { +enum MigrationStatus: string +{ case Running = "RUNNING"; case Failed = "FAILED"; case Finished = "FINISHED"; diff --git a/seed/php-sdk/examples/no-custom-config/src/Types/Types/Moment.php b/seed/php-sdk/examples/no-custom-config/src/Types/Types/Moment.php index a3efecdc4cf1..92339bb1d156 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Types/Types/Moment.php +++ b/seed/php-sdk/examples/no-custom-config/src/Types/Types/Moment.php @@ -36,15 +36,17 @@ class Moment extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->date = $values['date'];$this->datetime = $values['datetime']; + ) { + $this->id = $values['id']; + $this->date = $values['date']; + $this->datetime = $values['datetime']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/examples/no-custom-config/src/Types/Types/Movie.php b/seed/php-sdk/examples/no-custom-config/src/Types/Types/Movie.php index 41a1a4d1925e..83e4dae75f00 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Types/Types/Movie.php +++ b/seed/php-sdk/examples/no-custom-config/src/Types/Types/Movie.php @@ -84,15 +84,24 @@ class Movie extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->prequel = $values['prequel'] ?? null;$this->title = $values['title'];$this->from = $values['from'];$this->rating = $values['rating'];$this->type = $values['type'];$this->tag = $values['tag'];$this->book = $values['book'] ?? null;$this->metadata = $values['metadata'];$this->revenue = $values['revenue']; + ) { + $this->id = $values['id']; + $this->prequel = $values['prequel'] ?? null; + $this->title = $values['title']; + $this->from = $values['from']; + $this->rating = $values['rating']; + $this->type = $values['type']; + $this->tag = $values['tag']; + $this->book = $values['book'] ?? null; + $this->metadata = $values['metadata']; + $this->revenue = $values['revenue']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/examples/no-custom-config/src/Types/Types/Node.php b/seed/php-sdk/examples/no-custom-config/src/Types/Types/Node.php index c0e7f568e9ae..803293ea94c3 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Types/Types/Node.php +++ b/seed/php-sdk/examples/no-custom-config/src/Types/Types/Node.php @@ -35,15 +35,17 @@ class Node extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->nodes = $values['nodes'] ?? null;$this->trees = $values['trees'] ?? null; + ) { + $this->name = $values['name']; + $this->nodes = $values['nodes'] ?? null; + $this->trees = $values['trees'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/examples/no-custom-config/src/Types/Types/RefreshTokenRequest.php b/seed/php-sdk/examples/no-custom-config/src/Types/Types/RefreshTokenRequest.php index 36735b55f933..367cbed68df5 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Types/Types/RefreshTokenRequest.php +++ b/seed/php-sdk/examples/no-custom-config/src/Types/Types/RefreshTokenRequest.php @@ -20,15 +20,15 @@ class RefreshTokenRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->ttl = $values['ttl']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/examples/no-custom-config/src/Types/Types/Request.php b/seed/php-sdk/examples/no-custom-config/src/Types/Types/Request.php index 708473764bc5..0364760e0a2a 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Types/Types/Request.php +++ b/seed/php-sdk/examples/no-custom-config/src/Types/Types/Request.php @@ -20,15 +20,15 @@ class Request extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->request = $values['request']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/examples/no-custom-config/src/Types/Types/Response.php b/seed/php-sdk/examples/no-custom-config/src/Types/Types/Response.php index 20936570b9a8..c5b6961f8690 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Types/Types/Response.php +++ b/seed/php-sdk/examples/no-custom-config/src/Types/Types/Response.php @@ -29,15 +29,16 @@ class Response extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->response = $values['response'];$this->identifiers = $values['identifiers']; + ) { + $this->response = $values['response']; + $this->identifiers = $values['identifiers']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/examples/no-custom-config/src/Types/Types/ResponseType.php b/seed/php-sdk/examples/no-custom-config/src/Types/Types/ResponseType.php index c1e6d30ab6d4..c8e85b097d02 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Types/Types/ResponseType.php +++ b/seed/php-sdk/examples/no-custom-config/src/Types/Types/ResponseType.php @@ -28,15 +28,15 @@ class ResponseType extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->type = $values['type']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/examples/no-custom-config/src/Types/Types/StuntDouble.php b/seed/php-sdk/examples/no-custom-config/src/Types/Types/StuntDouble.php index 0ee52ee72746..3694206a9ce7 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Types/Types/StuntDouble.php +++ b/seed/php-sdk/examples/no-custom-config/src/Types/Types/StuntDouble.php @@ -27,15 +27,16 @@ class StuntDouble extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->actorOrActressId = $values['actorOrActressId']; + ) { + $this->name = $values['name']; + $this->actorOrActressId = $values['actorOrActressId']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/examples/no-custom-config/src/Types/Types/Test.php b/seed/php-sdk/examples/no-custom-config/src/Types/Types/Test.php index 88df88894c1e..162640567f74 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Types/Types/Test.php +++ b/seed/php-sdk/examples/no-custom-config/src/Types/Types/Test.php @@ -40,16 +40,17 @@ class Test extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param bool $and * @return Test */ - public static function and(bool $and): Test { + public static function and(bool $and): Test + { return new Test([ 'type' => 'and', 'value' => $and, @@ -60,7 +61,8 @@ public static function and(bool $and): Test { * @param bool $or * @return Test */ - public static function or(bool $or): Test { + public static function or(bool $or): Test + { return new Test([ 'type' => 'or', 'value' => $or, @@ -70,61 +72,67 @@ public static function or(bool $or): Test { /** * @return bool */ - public function isAnd_(): bool { - return is_bool($this->value)&& $this->type === 'and'; + public function isAnd_(): bool + { + return is_bool($this->value) && $this->type === 'and'; } /** * @return bool */ - public function asAnd_(): bool { - if (!(is_bool($this->value)&& $this->type === 'and')){ + public function asAnd_(): bool + { + if (!(is_bool($this->value) && $this->type === 'and')) { throw new Exception( "Expected and; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isOr_(): bool { - return is_bool($this->value)&& $this->type === 'or'; + public function isOr_(): bool + { + return is_bool($this->value) && $this->type === 'or'; } /** * @return bool */ - public function asOr_(): bool { - if (!(is_bool($this->value)&& $this->type === 'or')){ + public function asOr_(): bool + { + if (!(is_bool($this->value) && $this->type === 'or')) { throw new Exception( "Expected or; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'and': $value = $this->value; $result['and'] = $value; @@ -135,26 +143,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -163,38 +172,39 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'and': - if (!array_key_exists('and', $data)){ + if (!array_key_exists('and', $data)) { throw new Exception( "JSON data is missing property 'and'", ); } - + $args['value'] = $data['and']; break; case 'or': - if (!array_key_exists('or', $data)){ + if (!array_key_exists('or', $data)) { throw new Exception( "JSON data is missing property 'or'", ); } - + $args['value'] = $data['or']; break; case '_unknown': @@ -202,7 +212,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/examples/no-custom-config/src/Types/Types/Tree.php b/seed/php-sdk/examples/no-custom-config/src/Types/Types/Tree.php index 4298482fab4c..672753da74d3 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Types/Types/Tree.php +++ b/seed/php-sdk/examples/no-custom-config/src/Types/Types/Tree.php @@ -21,15 +21,15 @@ class Tree extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->nodes = $values['nodes'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/examples/no-custom-config/src/Utils/File.php b/seed/php-sdk/examples/no-custom-config/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/examples/no-custom-config/src/Utils/File.php +++ b/seed/php-sdk/examples/no-custom-config/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/examples/no-custom-config/tests/Core/Client/RawClientTest.php b/seed/php-sdk/examples/no-custom-config/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/examples/no-custom-config/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/examples/no-custom-config/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/examples/no-custom-config/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/examples/no-custom-config/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/examples/no-custom-config/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/examples/no-custom-config/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/examples/no-custom-config/tests/Core/Json/EnumTest.php b/seed/php-sdk/examples/no-custom-config/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/examples/no-custom-config/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/examples/no-custom-config/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/examples/no-custom-config/tests/Core/Json/TraitTest.php b/seed/php-sdk/examples/no-custom-config/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/examples/no-custom-config/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/examples/no-custom-config/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/examples/no-custom-config/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/examples/no-custom-config/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/examples/no-custom-config/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/examples/no-custom-config/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/examples/readme-config/src/Commons/Types/Types/Data.php b/seed/php-sdk/examples/readme-config/src/Commons/Types/Types/Data.php index 7e78dbc4f09d..3565c94ce984 100644 --- a/seed/php-sdk/examples/readme-config/src/Commons/Types/Types/Data.php +++ b/seed/php-sdk/examples/readme-config/src/Commons/Types/Types/Data.php @@ -40,16 +40,17 @@ class Data extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param string $string * @return Data */ - public static function string(string $string): Data { + public static function string(string $string): Data + { return new Data([ 'type' => 'string', 'value' => $string, @@ -60,7 +61,8 @@ public static function string(string $string): Data { * @param string $base64 * @return Data */ - public static function base64(string $base64): Data { + public static function base64(string $base64): Data + { return new Data([ 'type' => 'base64', 'value' => $base64, @@ -70,61 +72,67 @@ public static function base64(string $base64): Data { /** * @return bool */ - public function isString(): bool { - return is_string($this->value)&& $this->type === 'string'; + public function isString(): bool + { + return is_string($this->value) && $this->type === 'string'; } /** * @return string */ - public function asString(): string { - if (!(is_string($this->value)&& $this->type === 'string')){ + public function asString(): string + { + if (!(is_string($this->value) && $this->type === 'string')) { throw new Exception( "Expected string; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isBase64(): bool { - return is_string($this->value)&& $this->type === 'base64'; + public function isBase64(): bool + { + return is_string($this->value) && $this->type === 'base64'; } /** * @return string */ - public function asBase64(): string { - if (!(is_string($this->value)&& $this->type === 'base64')){ + public function asBase64(): string + { + if (!(is_string($this->value) && $this->type === 'base64')) { throw new Exception( "Expected base64; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'string': $value = $this->value; $result['string'] = $value; @@ -135,26 +143,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -163,38 +172,39 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'string': - if (!array_key_exists('string', $data)){ + if (!array_key_exists('string', $data)) { throw new Exception( "JSON data is missing property 'string'", ); } - + $args['value'] = $data['string']; break; case 'base64': - if (!array_key_exists('base64', $data)){ + if (!array_key_exists('base64', $data)) { throw new Exception( "JSON data is missing property 'base64'", ); } - + $args['value'] = $data['base64']; break; case '_unknown': @@ -202,7 +212,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/examples/readme-config/src/Commons/Types/Types/EventInfo.php b/seed/php-sdk/examples/readme-config/src/Commons/Types/Types/EventInfo.php index 05fa6f69b326..51bb58d0305d 100644 --- a/seed/php-sdk/examples/readme-config/src/Commons/Types/Types/EventInfo.php +++ b/seed/php-sdk/examples/readme-config/src/Commons/Types/Types/EventInfo.php @@ -42,16 +42,17 @@ class EventInfo extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param Metadata $metadata * @return EventInfo */ - public static function metadata(Metadata $metadata): EventInfo { + public static function metadata(Metadata $metadata): EventInfo + { return new EventInfo([ 'type' => 'metadata', 'value' => $metadata, @@ -62,7 +63,8 @@ public static function metadata(Metadata $metadata): EventInfo { * @param string $tag * @return EventInfo */ - public static function tag(string $tag): EventInfo { + public static function tag(string $tag): EventInfo + { return new EventInfo([ 'type' => 'tag', 'value' => $tag, @@ -72,61 +74,67 @@ public static function tag(string $tag): EventInfo { /** * @return bool */ - public function isMetadata(): bool { - return $this->value instanceof Metadata&& $this->type === 'metadata'; + public function isMetadata(): bool + { + return $this->value instanceof Metadata && $this->type === 'metadata'; } /** * @return Metadata */ - public function asMetadata(): Metadata { - if (!($this->value instanceof Metadata&& $this->type === 'metadata')){ + public function asMetadata(): Metadata + { + if (!($this->value instanceof Metadata && $this->type === 'metadata')) { throw new Exception( "Expected metadata; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isTag(): bool { - return is_string($this->value)&& $this->type === 'tag'; + public function isTag(): bool + { + return is_string($this->value) && $this->type === 'tag'; } /** * @return string */ - public function asTag(): string { - if (!(is_string($this->value)&& $this->type === 'tag')){ + public function asTag(): string + { + if (!(is_string($this->value) && $this->type === 'tag')) { throw new Exception( "Expected tag; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'metadata': $value = $this->asMetadata()->jsonSerialize(); $result = array_merge($value, $result); @@ -137,26 +145,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -165,32 +174,33 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'metadata': $args['value'] = Metadata::jsonDeserialize($data); break; case 'tag': - if (!array_key_exists('tag', $data)){ + if (!array_key_exists('tag', $data)) { throw new Exception( "JSON data is missing property 'tag'", ); } - + $args['value'] = $data['tag']; break; case '_unknown': @@ -198,7 +208,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/examples/readme-config/src/Commons/Types/Types/Metadata.php b/seed/php-sdk/examples/readme-config/src/Commons/Types/Types/Metadata.php index 5b01c883d1b7..80b27e6b8775 100644 --- a/seed/php-sdk/examples/readme-config/src/Commons/Types/Types/Metadata.php +++ b/seed/php-sdk/examples/readme-config/src/Commons/Types/Types/Metadata.php @@ -35,15 +35,17 @@ class Metadata extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->data = $values['data'] ?? null;$this->jsonString = $values['jsonString'] ?? null; + ) { + $this->id = $values['id']; + $this->data = $values['data'] ?? null; + $this->jsonString = $values['jsonString'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/examples/readme-config/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/examples/readme-config/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/examples/readme-config/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/examples/readme-config/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/examples/readme-config/src/Core/Client/RawClient.php b/seed/php-sdk/examples/readme-config/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/examples/readme-config/src/Core/Client/RawClient.php +++ b/seed/php-sdk/examples/readme-config/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/examples/readme-config/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/examples/readme-config/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/examples/readme-config/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/examples/readme-config/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/examples/readme-config/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/examples/readme-config/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/examples/readme-config/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/examples/readme-config/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/examples/readme-config/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/examples/readme-config/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/examples/readme-config/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/examples/readme-config/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/examples/readme-config/src/Core/Types/ArrayType.php b/seed/php-sdk/examples/readme-config/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/examples/readme-config/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/examples/readme-config/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/examples/readme-config/src/Core/Types/Constant.php b/seed/php-sdk/examples/readme-config/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/examples/readme-config/src/Core/Types/Constant.php +++ b/seed/php-sdk/examples/readme-config/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/examples/readme-config/src/Core/Types/Union.php b/seed/php-sdk/examples/readme-config/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/examples/readme-config/src/Core/Types/Union.php +++ b/seed/php-sdk/examples/readme-config/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/examples/readme-config/src/Environments.php b/seed/php-sdk/examples/readme-config/src/Environments.php index fd74f6324708..f49ef601cec1 100644 --- a/seed/php-sdk/examples/readme-config/src/Environments.php +++ b/seed/php-sdk/examples/readme-config/src/Environments.php @@ -2,8 +2,8 @@ namespace Seed; -enum Environments - : string { +enum Environments: string +{ case Production = "https://production.com/api"; case Staging = "https://staging.com/api"; } diff --git a/seed/php-sdk/examples/readme-config/src/Exceptions/SeedApiException.php b/seed/php-sdk/examples/readme-config/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/examples/readme-config/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/examples/readme-config/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/examples/readme-config/src/Exceptions/SeedException.php b/seed/php-sdk/examples/readme-config/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/examples/readme-config/src/Exceptions/SeedException.php +++ b/seed/php-sdk/examples/readme-config/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/examples/readme-config/src/File/FileClient.php b/seed/php-sdk/examples/readme-config/src/File/FileClient.php index 45aad0840c9b..6740a72fa4d1 100644 --- a/seed/php-sdk/examples/readme-config/src/File/FileClient.php +++ b/seed/php-sdk/examples/readme-config/src/File/FileClient.php @@ -7,7 +7,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class FileClient +class FileClient { /** * @var NotificationClient $notification @@ -45,11 +45,10 @@ class FileClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; $this->notification = new NotificationClient($this->client, $this->options); diff --git a/seed/php-sdk/examples/readme-config/src/File/Notification/NotificationClient.php b/seed/php-sdk/examples/readme-config/src/File/Notification/NotificationClient.php index 2622321005e3..7d6de8b9fe78 100644 --- a/seed/php-sdk/examples/readme-config/src/File/Notification/NotificationClient.php +++ b/seed/php-sdk/examples/readme-config/src/File/Notification/NotificationClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class NotificationClient +class NotificationClient { /** * @var ServiceClient $service @@ -39,11 +39,10 @@ class NotificationClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; $this->service = new ServiceClient($this->client, $this->options); diff --git a/seed/php-sdk/examples/readme-config/src/File/Notification/Service/ServiceClient.php b/seed/php-sdk/examples/readme-config/src/File/Notification/Service/ServiceClient.php index fe0d50b635c2..41f74acdd82c 100644 --- a/seed/php-sdk/examples/readme-config/src/File/Notification/Service/ServiceClient.php +++ b/seed/php-sdk/examples/readme-config/src/File/Notification/Service/ServiceClient.php @@ -13,7 +13,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class ServiceClient +class ServiceClient { /** * @var array{ @@ -41,11 +41,10 @@ class ServiceClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -64,7 +63,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getException(string $notificationId, ?array $options = null): Exception { + public function getException(string $notificationId, ?array $options = null): Exception + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -76,15 +76,15 @@ public function getException(string $notificationId, ?array $options = null): Ex $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return Exception::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/examples/readme-config/src/File/Service/Requests/GetFileRequest.php b/seed/php-sdk/examples/readme-config/src/File/Service/Requests/GetFileRequest.php index 9278b4039be6..37b5d16c020f 100644 --- a/seed/php-sdk/examples/readme-config/src/File/Service/Requests/GetFileRequest.php +++ b/seed/php-sdk/examples/readme-config/src/File/Service/Requests/GetFileRequest.php @@ -18,8 +18,7 @@ class GetFileRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->xFileApiVersion = $values['xFileApiVersion']; } } diff --git a/seed/php-sdk/examples/readme-config/src/File/Service/ServiceClient.php b/seed/php-sdk/examples/readme-config/src/File/Service/ServiceClient.php index 9c2728f78084..207f666e6d4e 100644 --- a/seed/php-sdk/examples/readme-config/src/File/Service/ServiceClient.php +++ b/seed/php-sdk/examples/readme-config/src/File/Service/ServiceClient.php @@ -14,7 +14,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class ServiceClient +class ServiceClient { /** * @var array{ @@ -42,11 +42,10 @@ class ServiceClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -68,7 +67,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getFile(string $filename, GetFileRequest $request, ?array $options = null): File { + public function getFile(string $filename, GetFileRequest $request, ?array $options = null): File + { $options = array_merge($this->options, $options ?? []); $headers = []; $headers['X-File-API-Version'] = $request->xFileApiVersion; @@ -83,15 +83,15 @@ public function getFile(string $filename, GetFileRequest $request, ?array $optio $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return File::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/examples/readme-config/src/Health/HealthClient.php b/seed/php-sdk/examples/readme-config/src/Health/HealthClient.php index 6e88ae7f3219..1891d8e7debe 100644 --- a/seed/php-sdk/examples/readme-config/src/Health/HealthClient.php +++ b/seed/php-sdk/examples/readme-config/src/Health/HealthClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class HealthClient +class HealthClient { /** * @var ServiceClient $service @@ -39,11 +39,10 @@ class HealthClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; $this->service = new ServiceClient($this->client, $this->options); diff --git a/seed/php-sdk/examples/readme-config/src/Health/Service/ServiceClient.php b/seed/php-sdk/examples/readme-config/src/Health/Service/ServiceClient.php index fffd85e9a51f..68fb11ecc5c8 100644 --- a/seed/php-sdk/examples/readme-config/src/Health/Service/ServiceClient.php +++ b/seed/php-sdk/examples/readme-config/src/Health/Service/ServiceClient.php @@ -13,7 +13,7 @@ use Seed\Core\Json\JsonDecoder; use JsonException; -class ServiceClient +class ServiceClient { /** * @var array{ @@ -41,11 +41,10 @@ class ServiceClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -65,7 +64,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function check(string $id, ?array $options = null): void { + public function check(string $id, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -77,12 +77,12 @@ public function check(string $id, ?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -115,7 +115,8 @@ public function check(string $id, ?array $options = null): void { * @throws SeedException * @throws SeedApiException */ - public function ping(?array $options = null): bool { + public function ping(?array $options = null): bool + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -127,15 +128,15 @@ public function ping(?array $options = null): bool { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeBool($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/examples/readme-config/src/SeedClient.php b/seed/php-sdk/examples/readme-config/src/SeedClient.php index 3ecaa568d991..6946f151d021 100644 --- a/seed/php-sdk/examples/readme-config/src/SeedClient.php +++ b/seed/php-sdk/examples/readme-config/src/SeedClient.php @@ -19,7 +19,7 @@ use Seed\Types\ComplexType; use Seed\Types\Identifier; -class SeedClient +class SeedClient { /** * @var FileClient $file @@ -65,29 +65,28 @@ class SeedClient public function __construct( ?string $token = null, ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - if ($token != null){ + if ($token != null) { $defaultHeaders['Authorization'] = "Bearer $token"; } - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->file = new FileClient($this->client, $this->options); $this->health = new HealthClient($this->client, $this->options); $this->service = new ServiceClient($this->client, $this->options); @@ -107,7 +106,8 @@ public function __construct( * @throws SeedException * @throws SeedApiException */ - public function echo_(string $request, ?array $options = null): string { + public function echo_(string $request, ?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -120,15 +120,15 @@ public function echo_(string $request, ?array $options = null): string { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -163,7 +163,8 @@ public function echo_(string $request, ?array $options = null): string { * @throws SeedException * @throws SeedApiException */ - public function createType(string $request, ?array $options = null): Identifier { + public function createType(string $request, ?array $options = null): Identifier + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -176,15 +177,15 @@ public function createType(string $request, ?array $options = null): Identifier $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return Identifier::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/examples/readme-config/src/Service/Requests/GetMetadataRequest.php b/seed/php-sdk/examples/readme-config/src/Service/Requests/GetMetadataRequest.php index 50642b0ae945..fa997e5d5b6f 100644 --- a/seed/php-sdk/examples/readme-config/src/Service/Requests/GetMetadataRequest.php +++ b/seed/php-sdk/examples/readme-config/src/Service/Requests/GetMetadataRequest.php @@ -30,8 +30,9 @@ class GetMetadataRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->shallow = $values['shallow'] ?? null;$this->tag = $values['tag'] ?? null;$this->xApiVersion = $values['xApiVersion']; + ) { + $this->shallow = $values['shallow'] ?? null; + $this->tag = $values['tag'] ?? null; + $this->xApiVersion = $values['xApiVersion']; } } diff --git a/seed/php-sdk/examples/readme-config/src/Service/ServiceClient.php b/seed/php-sdk/examples/readme-config/src/Service/ServiceClient.php index 5ed7880920c8..24096998ecf3 100644 --- a/seed/php-sdk/examples/readme-config/src/Service/ServiceClient.php +++ b/seed/php-sdk/examples/readme-config/src/Service/ServiceClient.php @@ -19,7 +19,7 @@ use Seed\Types\Types\Response; use Seed\Types\Types\RefreshTokenRequest; -class ServiceClient +class ServiceClient { /** * @var array{ @@ -47,11 +47,10 @@ class ServiceClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -70,7 +69,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getMovie(string $movieId, ?array $options = null): Movie { + public function getMovie(string $movieId, ?array $options = null): Movie + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -82,15 +82,15 @@ public function getMovie(string $movieId, ?array $options = null): Movie { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return Movie::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -122,7 +122,8 @@ public function getMovie(string $movieId, ?array $options = null): Movie { * @throws SeedException * @throws SeedApiException */ - public function createMovie(Movie $request, ?array $options = null): string { + public function createMovie(Movie $request, ?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -135,15 +136,15 @@ public function createMovie(Movie $request, ?array $options = null): string { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -175,13 +176,14 @@ public function createMovie(Movie $request, ?array $options = null): string { * @throws SeedException * @throws SeedApiException */ - public function getMetadata(GetMetadataRequest $request, ?array $options = null): Metadata { + public function getMetadata(GetMetadataRequest $request, ?array $options = null): Metadata + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->shallow != null){ + if ($request->shallow != null) { $query['shallow'] = $request->shallow; } - if ($request->tag != null){ + if ($request->tag != null) { $query['tag'] = $request->tag; } $headers = []; @@ -198,15 +200,15 @@ public function getMetadata(GetMetadataRequest $request, ?array $options = null) $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return Metadata::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -238,7 +240,8 @@ public function getMetadata(GetMetadataRequest $request, ?array $options = null) * @throws SeedException * @throws SeedApiException */ - public function createBigEntity(BigEntity $request, ?array $options = null): Response { + public function createBigEntity(BigEntity $request, ?array $options = null): Response + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -251,15 +254,15 @@ public function createBigEntity(BigEntity $request, ?array $options = null): Res $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return Response::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -290,7 +293,8 @@ public function createBigEntity(BigEntity $request, ?array $options = null): Res * @throws SeedException * @throws SeedApiException */ - public function refreshToken(?RefreshTokenRequest $request = null, ?array $options = null): void { + public function refreshToken(?RefreshTokenRequest $request = null, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -303,12 +307,12 @@ public function refreshToken(?RefreshTokenRequest $request = null, ?array $optio $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/examples/readme-config/src/Types/BasicType.php b/seed/php-sdk/examples/readme-config/src/Types/BasicType.php index 993439fce90c..3c77dac7cada 100644 --- a/seed/php-sdk/examples/readme-config/src/Types/BasicType.php +++ b/seed/php-sdk/examples/readme-config/src/Types/BasicType.php @@ -2,8 +2,8 @@ namespace Seed\Types; -enum BasicType - : string { +enum BasicType: string +{ case Primitive = "primitive"; case Literal = "literal"; } diff --git a/seed/php-sdk/examples/readme-config/src/Types/ComplexType.php b/seed/php-sdk/examples/readme-config/src/Types/ComplexType.php index b223bfddc00e..0063ebb7422b 100644 --- a/seed/php-sdk/examples/readme-config/src/Types/ComplexType.php +++ b/seed/php-sdk/examples/readme-config/src/Types/ComplexType.php @@ -2,8 +2,8 @@ namespace Seed\Types; -enum ComplexType - : string { +enum ComplexType: string +{ case Object = "object"; case Union = "union"; case Unknown = "unknown"; diff --git a/seed/php-sdk/examples/readme-config/src/Types/Identifier.php b/seed/php-sdk/examples/readme-config/src/Types/Identifier.php index 8ea0b83be86b..b1dc23ff17f6 100644 --- a/seed/php-sdk/examples/readme-config/src/Types/Identifier.php +++ b/seed/php-sdk/examples/readme-config/src/Types/Identifier.php @@ -40,15 +40,17 @@ class Identifier extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value'];$this->label = $values['label']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; + $this->label = $values['label']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/examples/readme-config/src/Types/Traits/Movie.php b/seed/php-sdk/examples/readme-config/src/Types/Traits/Movie.php index 59d8a5590cd6..5193090f6b76 100644 --- a/seed/php-sdk/examples/readme-config/src/Types/Traits/Movie.php +++ b/seed/php-sdk/examples/readme-config/src/Types/Traits/Movie.php @@ -17,7 +17,7 @@ * @property array $metadata * @property int $revenue */ -trait Movie +trait Movie { /** * @var string $id diff --git a/seed/php-sdk/examples/readme-config/src/Types/TypeWithSingleCharPropertyEqualToTypeStartingLetter.php b/seed/php-sdk/examples/readme-config/src/Types/TypeWithSingleCharPropertyEqualToTypeStartingLetter.php index e709ec79b0ff..f3b36bb6a39f 100644 --- a/seed/php-sdk/examples/readme-config/src/Types/TypeWithSingleCharPropertyEqualToTypeStartingLetter.php +++ b/seed/php-sdk/examples/readme-config/src/Types/TypeWithSingleCharPropertyEqualToTypeStartingLetter.php @@ -27,15 +27,16 @@ class TypeWithSingleCharPropertyEqualToTypeStartingLetter extends JsonSerializab */ public function __construct( array $values, - ) - { - $this->t = $values['t'];$this->ty = $values['ty']; + ) { + $this->t = $values['t']; + $this->ty = $values['ty']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/examples/readme-config/src/Types/Types/Actor.php b/seed/php-sdk/examples/readme-config/src/Types/Types/Actor.php index 454ec34fbc0d..820c8c16fb4f 100644 --- a/seed/php-sdk/examples/readme-config/src/Types/Types/Actor.php +++ b/seed/php-sdk/examples/readme-config/src/Types/Types/Actor.php @@ -27,15 +27,16 @@ class Actor extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->id = $values['id']; + ) { + $this->name = $values['name']; + $this->id = $values['id']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/examples/readme-config/src/Types/Types/Actress.php b/seed/php-sdk/examples/readme-config/src/Types/Types/Actress.php index 634f66792ece..1b31a539a2ed 100644 --- a/seed/php-sdk/examples/readme-config/src/Types/Types/Actress.php +++ b/seed/php-sdk/examples/readme-config/src/Types/Types/Actress.php @@ -27,15 +27,16 @@ class Actress extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->id = $values['id']; + ) { + $this->name = $values['name']; + $this->id = $values['id']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/examples/readme-config/src/Types/Types/BigEntity.php b/seed/php-sdk/examples/readme-config/src/Types/Types/BigEntity.php index 279e857261ee..5418e315e379 100644 --- a/seed/php-sdk/examples/readme-config/src/Types/Types/BigEntity.php +++ b/seed/php-sdk/examples/readme-config/src/Types/Types/BigEntity.php @@ -17,7 +17,7 @@ class BigEntity extends JsonSerializableType * |StuntDouble * )|null $castMember */ - #[JsonProperty('castMember'), Union(Actor::class,Actress::class,StuntDouble::class,'null')] + #[JsonProperty('castMember'), Union(Actor::class, Actress::class, StuntDouble::class, 'null')] public Actor|Actress|StuntDouble|null $castMember; /** @@ -115,15 +115,27 @@ class BigEntity extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->castMember = $values['castMember'] ?? null;$this->extendedMovie = $values['extendedMovie'] ?? null;$this->entity = $values['entity'] ?? null;$this->metadata = $values['metadata'] ?? null;$this->commonMetadata = $values['commonMetadata'] ?? null;$this->eventInfo = $values['eventInfo'] ?? null;$this->data = $values['data'] ?? null;$this->migration = $values['migration'] ?? null;$this->exception = $values['exception'] ?? null;$this->test = $values['test'] ?? null;$this->node = $values['node'] ?? null;$this->directory = $values['directory'] ?? null;$this->moment = $values['moment'] ?? null; + ) { + $this->castMember = $values['castMember'] ?? null; + $this->extendedMovie = $values['extendedMovie'] ?? null; + $this->entity = $values['entity'] ?? null; + $this->metadata = $values['metadata'] ?? null; + $this->commonMetadata = $values['commonMetadata'] ?? null; + $this->eventInfo = $values['eventInfo'] ?? null; + $this->data = $values['data'] ?? null; + $this->migration = $values['migration'] ?? null; + $this->exception = $values['exception'] ?? null; + $this->test = $values['test'] ?? null; + $this->node = $values['node'] ?? null; + $this->directory = $values['directory'] ?? null; + $this->moment = $values['moment'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/examples/readme-config/src/Types/Types/CronJob.php b/seed/php-sdk/examples/readme-config/src/Types/Types/CronJob.php index 3771a8e185bd..0e42a5401c1e 100644 --- a/seed/php-sdk/examples/readme-config/src/Types/Types/CronJob.php +++ b/seed/php-sdk/examples/readme-config/src/Types/Types/CronJob.php @@ -20,15 +20,15 @@ class CronJob extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->expression = $values['expression']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/examples/readme-config/src/Types/Types/Directory.php b/seed/php-sdk/examples/readme-config/src/Types/Types/Directory.php index 1acc01ad4d89..23d8d3268d63 100644 --- a/seed/php-sdk/examples/readme-config/src/Types/Types/Directory.php +++ b/seed/php-sdk/examples/readme-config/src/Types/Types/Directory.php @@ -35,15 +35,17 @@ class Directory extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->files = $values['files'] ?? null;$this->directories = $values['directories'] ?? null; + ) { + $this->name = $values['name']; + $this->files = $values['files'] ?? null; + $this->directories = $values['directories'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/examples/readme-config/src/Types/Types/Entity.php b/seed/php-sdk/examples/readme-config/src/Types/Types/Entity.php index 11897ec989c3..2bb9c116fdea 100644 --- a/seed/php-sdk/examples/readme-config/src/Types/Types/Entity.php +++ b/seed/php-sdk/examples/readme-config/src/Types/Types/Entity.php @@ -35,15 +35,16 @@ class Entity extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->type = $values['type'];$this->name = $values['name']; + ) { + $this->type = $values['type']; + $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/examples/readme-config/src/Types/Types/ExceptionInfo.php b/seed/php-sdk/examples/readme-config/src/Types/Types/ExceptionInfo.php index 1faa323c71f9..b0718a06ab85 100644 --- a/seed/php-sdk/examples/readme-config/src/Types/Types/ExceptionInfo.php +++ b/seed/php-sdk/examples/readme-config/src/Types/Types/ExceptionInfo.php @@ -34,15 +34,17 @@ class ExceptionInfo extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->exceptionType = $values['exceptionType'];$this->exceptionMessage = $values['exceptionMessage'];$this->exceptionStacktrace = $values['exceptionStacktrace']; + ) { + $this->exceptionType = $values['exceptionType']; + $this->exceptionMessage = $values['exceptionMessage']; + $this->exceptionStacktrace = $values['exceptionStacktrace']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/examples/readme-config/src/Types/Types/ExtendedMovie.php b/seed/php-sdk/examples/readme-config/src/Types/Types/ExtendedMovie.php index de70f53e4d02..d2e823e54c38 100644 --- a/seed/php-sdk/examples/readme-config/src/Types/Types/ExtendedMovie.php +++ b/seed/php-sdk/examples/readme-config/src/Types/Types/ExtendedMovie.php @@ -34,15 +34,25 @@ class ExtendedMovie extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->prequel = $values['prequel'] ?? null;$this->title = $values['title'];$this->from = $values['from'];$this->rating = $values['rating'];$this->type = $values['type'];$this->tag = $values['tag'];$this->book = $values['book'] ?? null;$this->metadata = $values['metadata'];$this->revenue = $values['revenue'];$this->cast = $values['cast']; + ) { + $this->id = $values['id']; + $this->prequel = $values['prequel'] ?? null; + $this->title = $values['title']; + $this->from = $values['from']; + $this->rating = $values['rating']; + $this->type = $values['type']; + $this->tag = $values['tag']; + $this->book = $values['book'] ?? null; + $this->metadata = $values['metadata']; + $this->revenue = $values['revenue']; + $this->cast = $values['cast']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/examples/readme-config/src/Types/Types/File.php b/seed/php-sdk/examples/readme-config/src/Types/Types/File.php index d1e82d62725a..ba6867fd4231 100644 --- a/seed/php-sdk/examples/readme-config/src/Types/Types/File.php +++ b/seed/php-sdk/examples/readme-config/src/Types/Types/File.php @@ -27,15 +27,16 @@ class File extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->contents = $values['contents']; + ) { + $this->name = $values['name']; + $this->contents = $values['contents']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/examples/readme-config/src/Types/Types/Metadata.php b/seed/php-sdk/examples/readme-config/src/Types/Types/Metadata.php index 7f68dbb8a150..7d3d6d838f44 100644 --- a/seed/php-sdk/examples/readme-config/src/Types/Types/Metadata.php +++ b/seed/php-sdk/examples/readme-config/src/Types/Types/Metadata.php @@ -56,9 +56,11 @@ class Metadata extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->extra = $values['extra'];$this->tags = $values['tags'];$this->type = $values['type'];$this->value = $values['value']; + ) { + $this->extra = $values['extra']; + $this->tags = $values['tags']; + $this->type = $values['type']; + $this->value = $values['value']; } /** @@ -67,7 +69,8 @@ private function __construct( * @param string $html * @return Metadata */ - public static function html(array $extra, array $tags, string $html): Metadata { + public static function html(array $extra, array $tags, string $html): Metadata + { return new Metadata([ 'extra' => $extra, 'tags' => $tags, @@ -82,7 +85,8 @@ public static function html(array $extra, array $tags, string $html): Metadata { * @param string $markdown * @return Metadata */ - public static function markdown(array $extra, array $tags, string $markdown): Metadata { + public static function markdown(array $extra, array $tags, string $markdown): Metadata + { return new Metadata([ 'extra' => $extra, 'tags' => $tags, @@ -94,61 +98,67 @@ public static function markdown(array $extra, array $tags, string $markdown): Me /** * @return bool */ - public function isHtml(): bool { - return is_string($this->value)&& $this->type === 'html'; + public function isHtml(): bool + { + return is_string($this->value) && $this->type === 'html'; } /** * @return string */ - public function asHtml(): string { - if (!(is_string($this->value)&& $this->type === 'html')){ + public function asHtml(): string + { + if (!(is_string($this->value) && $this->type === 'html')) { throw new Exception( "Expected html; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isMarkdown(): bool { - return is_string($this->value)&& $this->type === 'markdown'; + public function isMarkdown(): bool + { + return is_string($this->value) && $this->type === 'markdown'; } /** * @return string */ - public function asMarkdown(): string { - if (!(is_string($this->value)&& $this->type === 'markdown')){ + public function asMarkdown(): string + { + if (!(is_string($this->value) && $this->type === 'markdown')) { throw new Exception( "Expected markdown; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'html': $value = $this->value; $result['html'] = $value; @@ -159,26 +169,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -187,62 +198,63 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('extra', $data)){ + if (!array_key_exists('extra', $data)) { throw new Exception( "JSON data is missing property 'extra'", ); } - if (!(is_array($data['extra']))){ + if (!(is_array($data['extra']))) { throw new Exception( "Expected property 'extra' in JSON data to be map, instead received " . get_debug_type($data['extra']), ); } $args['extra'] = $data['extra']; - - if (!array_key_exists('tags', $data)){ + + if (!array_key_exists('tags', $data)) { throw new Exception( "JSON data is missing property 'tags'", ); } - if (!(is_array($data['tags']))){ + if (!(is_array($data['tags']))) { throw new Exception( "Expected property 'tags' in JSON data to be array, instead received " . get_debug_type($data['tags']), ); } $args['tags'] = $data['tags']; - - if (!array_key_exists('type', $data)){ + + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'html': - if (!array_key_exists('html', $data)){ + if (!array_key_exists('html', $data)) { throw new Exception( "JSON data is missing property 'html'", ); } - + $args['value'] = $data['html']; break; case 'markdown': - if (!array_key_exists('markdown', $data)){ + if (!array_key_exists('markdown', $data)) { throw new Exception( "JSON data is missing property 'markdown'", ); } - + $args['value'] = $data['markdown']; break; case '_unknown': @@ -250,7 +262,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/examples/readme-config/src/Types/Types/Migration.php b/seed/php-sdk/examples/readme-config/src/Types/Types/Migration.php index 60dc11864d94..461c76a11b06 100644 --- a/seed/php-sdk/examples/readme-config/src/Types/Types/Migration.php +++ b/seed/php-sdk/examples/readme-config/src/Types/Types/Migration.php @@ -27,15 +27,16 @@ class Migration extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->status = $values['status']; + ) { + $this->name = $values['name']; + $this->status = $values['status']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/examples/readme-config/src/Types/Types/MigrationStatus.php b/seed/php-sdk/examples/readme-config/src/Types/Types/MigrationStatus.php index b1c21bac9a67..ae94c6d619e9 100644 --- a/seed/php-sdk/examples/readme-config/src/Types/Types/MigrationStatus.php +++ b/seed/php-sdk/examples/readme-config/src/Types/Types/MigrationStatus.php @@ -2,8 +2,8 @@ namespace Seed\Types\Types; -enum MigrationStatus - : string { +enum MigrationStatus: string +{ case Running = "RUNNING"; case Failed = "FAILED"; case Finished = "FINISHED"; diff --git a/seed/php-sdk/examples/readme-config/src/Types/Types/Moment.php b/seed/php-sdk/examples/readme-config/src/Types/Types/Moment.php index a3efecdc4cf1..92339bb1d156 100644 --- a/seed/php-sdk/examples/readme-config/src/Types/Types/Moment.php +++ b/seed/php-sdk/examples/readme-config/src/Types/Types/Moment.php @@ -36,15 +36,17 @@ class Moment extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->date = $values['date'];$this->datetime = $values['datetime']; + ) { + $this->id = $values['id']; + $this->date = $values['date']; + $this->datetime = $values['datetime']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/examples/readme-config/src/Types/Types/Movie.php b/seed/php-sdk/examples/readme-config/src/Types/Types/Movie.php index 41a1a4d1925e..83e4dae75f00 100644 --- a/seed/php-sdk/examples/readme-config/src/Types/Types/Movie.php +++ b/seed/php-sdk/examples/readme-config/src/Types/Types/Movie.php @@ -84,15 +84,24 @@ class Movie extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->prequel = $values['prequel'] ?? null;$this->title = $values['title'];$this->from = $values['from'];$this->rating = $values['rating'];$this->type = $values['type'];$this->tag = $values['tag'];$this->book = $values['book'] ?? null;$this->metadata = $values['metadata'];$this->revenue = $values['revenue']; + ) { + $this->id = $values['id']; + $this->prequel = $values['prequel'] ?? null; + $this->title = $values['title']; + $this->from = $values['from']; + $this->rating = $values['rating']; + $this->type = $values['type']; + $this->tag = $values['tag']; + $this->book = $values['book'] ?? null; + $this->metadata = $values['metadata']; + $this->revenue = $values['revenue']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/examples/readme-config/src/Types/Types/Node.php b/seed/php-sdk/examples/readme-config/src/Types/Types/Node.php index c0e7f568e9ae..803293ea94c3 100644 --- a/seed/php-sdk/examples/readme-config/src/Types/Types/Node.php +++ b/seed/php-sdk/examples/readme-config/src/Types/Types/Node.php @@ -35,15 +35,17 @@ class Node extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->nodes = $values['nodes'] ?? null;$this->trees = $values['trees'] ?? null; + ) { + $this->name = $values['name']; + $this->nodes = $values['nodes'] ?? null; + $this->trees = $values['trees'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/examples/readme-config/src/Types/Types/RefreshTokenRequest.php b/seed/php-sdk/examples/readme-config/src/Types/Types/RefreshTokenRequest.php index 36735b55f933..367cbed68df5 100644 --- a/seed/php-sdk/examples/readme-config/src/Types/Types/RefreshTokenRequest.php +++ b/seed/php-sdk/examples/readme-config/src/Types/Types/RefreshTokenRequest.php @@ -20,15 +20,15 @@ class RefreshTokenRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->ttl = $values['ttl']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/examples/readme-config/src/Types/Types/Request.php b/seed/php-sdk/examples/readme-config/src/Types/Types/Request.php index 708473764bc5..0364760e0a2a 100644 --- a/seed/php-sdk/examples/readme-config/src/Types/Types/Request.php +++ b/seed/php-sdk/examples/readme-config/src/Types/Types/Request.php @@ -20,15 +20,15 @@ class Request extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->request = $values['request']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/examples/readme-config/src/Types/Types/Response.php b/seed/php-sdk/examples/readme-config/src/Types/Types/Response.php index 20936570b9a8..c5b6961f8690 100644 --- a/seed/php-sdk/examples/readme-config/src/Types/Types/Response.php +++ b/seed/php-sdk/examples/readme-config/src/Types/Types/Response.php @@ -29,15 +29,16 @@ class Response extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->response = $values['response'];$this->identifiers = $values['identifiers']; + ) { + $this->response = $values['response']; + $this->identifiers = $values['identifiers']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/examples/readme-config/src/Types/Types/ResponseType.php b/seed/php-sdk/examples/readme-config/src/Types/Types/ResponseType.php index c1e6d30ab6d4..c8e85b097d02 100644 --- a/seed/php-sdk/examples/readme-config/src/Types/Types/ResponseType.php +++ b/seed/php-sdk/examples/readme-config/src/Types/Types/ResponseType.php @@ -28,15 +28,15 @@ class ResponseType extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->type = $values['type']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/examples/readme-config/src/Types/Types/StuntDouble.php b/seed/php-sdk/examples/readme-config/src/Types/Types/StuntDouble.php index 0ee52ee72746..3694206a9ce7 100644 --- a/seed/php-sdk/examples/readme-config/src/Types/Types/StuntDouble.php +++ b/seed/php-sdk/examples/readme-config/src/Types/Types/StuntDouble.php @@ -27,15 +27,16 @@ class StuntDouble extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->actorOrActressId = $values['actorOrActressId']; + ) { + $this->name = $values['name']; + $this->actorOrActressId = $values['actorOrActressId']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/examples/readme-config/src/Types/Types/Test.php b/seed/php-sdk/examples/readme-config/src/Types/Types/Test.php index 88df88894c1e..162640567f74 100644 --- a/seed/php-sdk/examples/readme-config/src/Types/Types/Test.php +++ b/seed/php-sdk/examples/readme-config/src/Types/Types/Test.php @@ -40,16 +40,17 @@ class Test extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param bool $and * @return Test */ - public static function and(bool $and): Test { + public static function and(bool $and): Test + { return new Test([ 'type' => 'and', 'value' => $and, @@ -60,7 +61,8 @@ public static function and(bool $and): Test { * @param bool $or * @return Test */ - public static function or(bool $or): Test { + public static function or(bool $or): Test + { return new Test([ 'type' => 'or', 'value' => $or, @@ -70,61 +72,67 @@ public static function or(bool $or): Test { /** * @return bool */ - public function isAnd_(): bool { - return is_bool($this->value)&& $this->type === 'and'; + public function isAnd_(): bool + { + return is_bool($this->value) && $this->type === 'and'; } /** * @return bool */ - public function asAnd_(): bool { - if (!(is_bool($this->value)&& $this->type === 'and')){ + public function asAnd_(): bool + { + if (!(is_bool($this->value) && $this->type === 'and')) { throw new Exception( "Expected and; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isOr_(): bool { - return is_bool($this->value)&& $this->type === 'or'; + public function isOr_(): bool + { + return is_bool($this->value) && $this->type === 'or'; } /** * @return bool */ - public function asOr_(): bool { - if (!(is_bool($this->value)&& $this->type === 'or')){ + public function asOr_(): bool + { + if (!(is_bool($this->value) && $this->type === 'or')) { throw new Exception( "Expected or; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'and': $value = $this->value; $result['and'] = $value; @@ -135,26 +143,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -163,38 +172,39 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'and': - if (!array_key_exists('and', $data)){ + if (!array_key_exists('and', $data)) { throw new Exception( "JSON data is missing property 'and'", ); } - + $args['value'] = $data['and']; break; case 'or': - if (!array_key_exists('or', $data)){ + if (!array_key_exists('or', $data)) { throw new Exception( "JSON data is missing property 'or'", ); } - + $args['value'] = $data['or']; break; case '_unknown': @@ -202,7 +212,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/examples/readme-config/src/Types/Types/Tree.php b/seed/php-sdk/examples/readme-config/src/Types/Types/Tree.php index 4298482fab4c..672753da74d3 100644 --- a/seed/php-sdk/examples/readme-config/src/Types/Types/Tree.php +++ b/seed/php-sdk/examples/readme-config/src/Types/Types/Tree.php @@ -21,15 +21,15 @@ class Tree extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->nodes = $values['nodes'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/examples/readme-config/src/Utils/File.php b/seed/php-sdk/examples/readme-config/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/examples/readme-config/src/Utils/File.php +++ b/seed/php-sdk/examples/readme-config/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/examples/readme-config/tests/Core/Client/RawClientTest.php b/seed/php-sdk/examples/readme-config/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/examples/readme-config/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/examples/readme-config/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/examples/readme-config/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/examples/readme-config/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/examples/readme-config/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/examples/readme-config/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/examples/readme-config/tests/Core/Json/EnumTest.php b/seed/php-sdk/examples/readme-config/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/examples/readme-config/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/examples/readme-config/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/examples/readme-config/tests/Core/Json/TraitTest.php b/seed/php-sdk/examples/readme-config/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/examples/readme-config/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/examples/readme-config/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/examples/readme-config/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/examples/readme-config/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/examples/readme-config/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/examples/readme-config/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/exhaustive/no-custom-config/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/Core/Client/RawClient.php b/seed/php-sdk/exhaustive/no-custom-config/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/Core/Client/RawClient.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/exhaustive/no-custom-config/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/exhaustive/no-custom-config/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/exhaustive/no-custom-config/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/Core/Types/ArrayType.php b/seed/php-sdk/exhaustive/no-custom-config/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/Core/Types/Constant.php b/seed/php-sdk/exhaustive/no-custom-config/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/Core/Types/Constant.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/Core/Types/Union.php b/seed/php-sdk/exhaustive/no-custom-config/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/Core/Types/Union.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Container/ContainerClient.php b/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Container/ContainerClient.php index 3c5d43fa5ed4..a92eb5aef5a5 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Container/ContainerClient.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Container/ContainerClient.php @@ -15,7 +15,7 @@ use Psr\Http\Client\ClientExceptionInterface; use Seed\Types\Object\Types\ObjectWithRequiredField; -class ContainerClient +class ContainerClient { /** * @var array{ @@ -43,11 +43,10 @@ class ContainerClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -66,7 +65,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getAndReturnListOfPrimitives(array $request, ?array $options = null): array { + public function getAndReturnListOfPrimitives(array $request, ?array $options = null): array + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -79,15 +79,15 @@ public function getAndReturnListOfPrimitives(array $request, ?array $options = n $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, ['string']); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -119,7 +119,8 @@ public function getAndReturnListOfPrimitives(array $request, ?array $options = n * @throws SeedException * @throws SeedApiException */ - public function getAndReturnListOfObjects(array $request, ?array $options = null): array { + public function getAndReturnListOfObjects(array $request, ?array $options = null): array + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -132,15 +133,15 @@ public function getAndReturnListOfObjects(array $request, ?array $options = null $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, [ObjectWithRequiredField::class]); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -172,7 +173,8 @@ public function getAndReturnListOfObjects(array $request, ?array $options = null * @throws SeedException * @throws SeedApiException */ - public function getAndReturnSetOfPrimitives(array $request, ?array $options = null): array { + public function getAndReturnSetOfPrimitives(array $request, ?array $options = null): array + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -185,15 +187,15 @@ public function getAndReturnSetOfPrimitives(array $request, ?array $options = nu $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, ['string']); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -225,7 +227,8 @@ public function getAndReturnSetOfPrimitives(array $request, ?array $options = nu * @throws SeedException * @throws SeedApiException */ - public function getAndReturnSetOfObjects(array $request, ?array $options = null): array { + public function getAndReturnSetOfObjects(array $request, ?array $options = null): array + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -238,15 +241,15 @@ public function getAndReturnSetOfObjects(array $request, ?array $options = null) $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, [ObjectWithRequiredField::class]); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -278,7 +281,8 @@ public function getAndReturnSetOfObjects(array $request, ?array $options = null) * @throws SeedException * @throws SeedApiException */ - public function getAndReturnMapPrimToPrim(array $request, ?array $options = null): array { + public function getAndReturnMapPrimToPrim(array $request, ?array $options = null): array + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -291,15 +295,15 @@ public function getAndReturnMapPrimToPrim(array $request, ?array $options = null $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, ['string' => 'string']); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -331,7 +335,8 @@ public function getAndReturnMapPrimToPrim(array $request, ?array $options = null * @throws SeedException * @throws SeedApiException */ - public function getAndReturnMapOfPrimToObject(array $request, ?array $options = null): array { + public function getAndReturnMapOfPrimToObject(array $request, ?array $options = null): array + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -344,15 +349,15 @@ public function getAndReturnMapOfPrimToObject(array $request, ?array $options = $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, ['string' => ObjectWithRequiredField::class]); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -384,7 +389,8 @@ public function getAndReturnMapOfPrimToObject(array $request, ?array $options = * @throws SeedException * @throws SeedApiException */ - public function getAndReturnOptional(?ObjectWithRequiredField $request = null, ?array $options = null): ?ObjectWithRequiredField { + public function getAndReturnOptional(?ObjectWithRequiredField $request = null, ?array $options = null): ?ObjectWithRequiredField + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -397,18 +403,18 @@ public function getAndReturnOptional(?ObjectWithRequiredField $request = null, ? $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); - if (empty($json)){ + if (empty($json)) { return null; } return ObjectWithRequiredField::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/ContentType/ContentTypeClient.php b/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/ContentType/ContentTypeClient.php index 69de06db85be..464542437c9f 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/ContentType/ContentTypeClient.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/ContentType/ContentTypeClient.php @@ -12,7 +12,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class ContentTypeClient +class ContentTypeClient { /** * @var array{ @@ -40,11 +40,10 @@ class ContentTypeClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -62,7 +61,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function postJsonPatchContentType(ObjectWithOptionalField $request, ?array $options = null): void { + public function postJsonPatchContentType(ObjectWithOptionalField $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -75,12 +75,12 @@ public function postJsonPatchContentType(ObjectWithOptionalField $request, ?arra $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -111,7 +111,8 @@ public function postJsonPatchContentType(ObjectWithOptionalField $request, ?arra * @throws SeedException * @throws SeedApiException */ - public function postJsonPatchContentWithCharsetType(ObjectWithOptionalField $request, ?array $options = null): void { + public function postJsonPatchContentWithCharsetType(ObjectWithOptionalField $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -124,12 +125,12 @@ public function postJsonPatchContentWithCharsetType(ObjectWithOptionalField $req $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/EndpointsClient.php b/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/EndpointsClient.php index 2d244967f18f..8fc3e76ab29d 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/EndpointsClient.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/EndpointsClient.php @@ -15,7 +15,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class EndpointsClient +class EndpointsClient { /** * @var ContainerClient $container @@ -93,11 +93,10 @@ class EndpointsClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; $this->container = new ContainerClient($this->client, $this->options); diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Enum/EnumClient.php b/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Enum/EnumClient.php index 244257948b89..be5d18bb5cf0 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Enum/EnumClient.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Enum/EnumClient.php @@ -14,7 +14,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class EnumClient +class EnumClient { /** * @var array{ @@ -42,11 +42,10 @@ class EnumClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -65,7 +64,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getAndReturnEnum(string $request, ?array $options = null): string { + public function getAndReturnEnum(string $request, ?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -78,15 +78,15 @@ public function getAndReturnEnum(string $request, ?array $options = null): strin $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/HttpMethods/HttpMethodsClient.php b/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/HttpMethods/HttpMethodsClient.php index 011ac0d91566..c07112348c2c 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/HttpMethods/HttpMethodsClient.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/HttpMethods/HttpMethodsClient.php @@ -15,7 +15,7 @@ use Seed\Types\Object\Types\ObjectWithRequiredField; use Seed\Types\Object\Types\ObjectWithOptionalField; -class HttpMethodsClient +class HttpMethodsClient { /** * @var array{ @@ -43,11 +43,10 @@ class HttpMethodsClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -66,7 +65,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function testGet(string $id, ?array $options = null): string { + public function testGet(string $id, ?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -78,15 +78,15 @@ public function testGet(string $id, ?array $options = null): string { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -118,7 +118,8 @@ public function testGet(string $id, ?array $options = null): string { * @throws SeedException * @throws SeedApiException */ - public function testPost(ObjectWithRequiredField $request, ?array $options = null): ObjectWithOptionalField { + public function testPost(ObjectWithRequiredField $request, ?array $options = null): ObjectWithOptionalField + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -131,15 +132,15 @@ public function testPost(ObjectWithRequiredField $request, ?array $options = nul $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ObjectWithOptionalField::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -172,7 +173,8 @@ public function testPost(ObjectWithRequiredField $request, ?array $options = nul * @throws SeedException * @throws SeedApiException */ - public function testPut(string $id, ObjectWithRequiredField $request, ?array $options = null): ObjectWithOptionalField { + public function testPut(string $id, ObjectWithRequiredField $request, ?array $options = null): ObjectWithOptionalField + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -185,15 +187,15 @@ public function testPut(string $id, ObjectWithRequiredField $request, ?array $op $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ObjectWithOptionalField::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -226,7 +228,8 @@ public function testPut(string $id, ObjectWithRequiredField $request, ?array $op * @throws SeedException * @throws SeedApiException */ - public function testPatch(string $id, ObjectWithOptionalField $request, ?array $options = null): ObjectWithOptionalField { + public function testPatch(string $id, ObjectWithOptionalField $request, ?array $options = null): ObjectWithOptionalField + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -239,15 +242,15 @@ public function testPatch(string $id, ObjectWithOptionalField $request, ?array $ $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ObjectWithOptionalField::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -279,7 +282,8 @@ public function testPatch(string $id, ObjectWithOptionalField $request, ?array $ * @throws SeedException * @throws SeedApiException */ - public function testDelete(string $id, ?array $options = null): bool { + public function testDelete(string $id, ?array $options = null): bool + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -291,15 +295,15 @@ public function testDelete(string $id, ?array $options = null): bool { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeBool($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Object/ObjectClient.php b/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Object/ObjectClient.php index 0b28c9423134..0bba1bdf6402 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Object/ObjectClient.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Object/ObjectClient.php @@ -18,7 +18,7 @@ use Seed\Types\Object\Types\NestedObjectWithRequiredField; use Seed\Core\Json\JsonSerializer; -class ObjectClient +class ObjectClient { /** * @var array{ @@ -46,11 +46,10 @@ class ObjectClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -69,7 +68,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getAndReturnWithOptionalField(ObjectWithOptionalField $request, ?array $options = null): ObjectWithOptionalField { + public function getAndReturnWithOptionalField(ObjectWithOptionalField $request, ?array $options = null): ObjectWithOptionalField + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -82,15 +82,15 @@ public function getAndReturnWithOptionalField(ObjectWithOptionalField $request, $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ObjectWithOptionalField::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -122,7 +122,8 @@ public function getAndReturnWithOptionalField(ObjectWithOptionalField $request, * @throws SeedException * @throws SeedApiException */ - public function getAndReturnWithRequiredField(ObjectWithRequiredField $request, ?array $options = null): ObjectWithRequiredField { + public function getAndReturnWithRequiredField(ObjectWithRequiredField $request, ?array $options = null): ObjectWithRequiredField + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -135,15 +136,15 @@ public function getAndReturnWithRequiredField(ObjectWithRequiredField $request, $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ObjectWithRequiredField::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -175,7 +176,8 @@ public function getAndReturnWithRequiredField(ObjectWithRequiredField $request, * @throws SeedException * @throws SeedApiException */ - public function getAndReturnWithMapOfMap(ObjectWithMapOfMap $request, ?array $options = null): ObjectWithMapOfMap { + public function getAndReturnWithMapOfMap(ObjectWithMapOfMap $request, ?array $options = null): ObjectWithMapOfMap + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -188,15 +190,15 @@ public function getAndReturnWithMapOfMap(ObjectWithMapOfMap $request, ?array $op $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ObjectWithMapOfMap::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -228,7 +230,8 @@ public function getAndReturnWithMapOfMap(ObjectWithMapOfMap $request, ?array $op * @throws SeedException * @throws SeedApiException */ - public function getAndReturnNestedWithOptionalField(NestedObjectWithOptionalField $request, ?array $options = null): NestedObjectWithOptionalField { + public function getAndReturnNestedWithOptionalField(NestedObjectWithOptionalField $request, ?array $options = null): NestedObjectWithOptionalField + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -241,15 +244,15 @@ public function getAndReturnNestedWithOptionalField(NestedObjectWithOptionalFiel $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return NestedObjectWithOptionalField::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -282,7 +285,8 @@ public function getAndReturnNestedWithOptionalField(NestedObjectWithOptionalFiel * @throws SeedException * @throws SeedApiException */ - public function getAndReturnNestedWithRequiredField(string $string, NestedObjectWithRequiredField $request, ?array $options = null): NestedObjectWithRequiredField { + public function getAndReturnNestedWithRequiredField(string $string, NestedObjectWithRequiredField $request, ?array $options = null): NestedObjectWithRequiredField + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -295,15 +299,15 @@ public function getAndReturnNestedWithRequiredField(string $string, NestedObject $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return NestedObjectWithRequiredField::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -335,7 +339,8 @@ public function getAndReturnNestedWithRequiredField(string $string, NestedObject * @throws SeedException * @throws SeedApiException */ - public function getAndReturnNestedWithRequiredFieldAsList(array $request, ?array $options = null): NestedObjectWithRequiredField { + public function getAndReturnNestedWithRequiredFieldAsList(array $request, ?array $options = null): NestedObjectWithRequiredField + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -348,15 +353,15 @@ public function getAndReturnNestedWithRequiredFieldAsList(array $request, ?array $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return NestedObjectWithRequiredField::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Params/ParamsClient.php b/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Params/ParamsClient.php index ece9be725973..4d7b29f8fdfa 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Params/ParamsClient.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Params/ParamsClient.php @@ -18,7 +18,7 @@ use Seed\Endpoints\Params\Requests\GetWithInlinePathAndQuery; use Seed\Endpoints\Params\Requests\ModifyResourceAtInlinedPath; -class ParamsClient +class ParamsClient { /** * @var array{ @@ -46,11 +46,10 @@ class ParamsClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -71,7 +70,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getWithPath(string $param, ?array $options = null): string { + public function getWithPath(string $param, ?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -83,15 +83,15 @@ public function getWithPath(string $param, ?array $options = null): string { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -125,7 +125,8 @@ public function getWithPath(string $param, ?array $options = null): string { * @throws SeedException * @throws SeedApiException */ - public function getWithInlinePath(string $param, ?array $options = null): string { + public function getWithInlinePath(string $param, ?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -137,15 +138,15 @@ public function getWithInlinePath(string $param, ?array $options = null): string $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -178,7 +179,8 @@ public function getWithInlinePath(string $param, ?array $options = null): string * @throws SeedException * @throws SeedApiException */ - public function getWithQuery(GetWithQuery $request, ?array $options = null): void { + public function getWithQuery(GetWithQuery $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); $query = []; $query['query'] = $request->query; @@ -194,12 +196,12 @@ public function getWithQuery(GetWithQuery $request, ?array $options = null): voi $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -232,7 +234,8 @@ public function getWithQuery(GetWithQuery $request, ?array $options = null): voi * @throws SeedException * @throws SeedApiException */ - public function getWithAllowMultipleQuery(GetWithMultipleQuery $request, ?array $options = null): void { + public function getWithAllowMultipleQuery(GetWithMultipleQuery $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); $query = []; $query['query'] = $request->query; @@ -248,12 +251,12 @@ public function getWithAllowMultipleQuery(GetWithMultipleQuery $request, ?array $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -287,7 +290,8 @@ public function getWithAllowMultipleQuery(GetWithMultipleQuery $request, ?array * @throws SeedException * @throws SeedApiException */ - public function getWithPathAndQuery(string $param, GetWithPathAndQuery $request, ?array $options = null): void { + public function getWithPathAndQuery(string $param, GetWithPathAndQuery $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); $query = []; $query['query'] = $request->query; @@ -302,12 +306,12 @@ public function getWithPathAndQuery(string $param, GetWithPathAndQuery $request, $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -341,7 +345,8 @@ public function getWithPathAndQuery(string $param, GetWithPathAndQuery $request, * @throws SeedException * @throws SeedApiException */ - public function getWithInlinePathAndQuery(string $param, GetWithInlinePathAndQuery $request, ?array $options = null): void { + public function getWithInlinePathAndQuery(string $param, GetWithInlinePathAndQuery $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); $query = []; $query['query'] = $request->query; @@ -356,12 +361,12 @@ public function getWithInlinePathAndQuery(string $param, GetWithInlinePathAndQue $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -396,7 +401,8 @@ public function getWithInlinePathAndQuery(string $param, GetWithInlinePathAndQue * @throws SeedException * @throws SeedApiException */ - public function modifyWithPath(string $param, string $request, ?array $options = null): string { + public function modifyWithPath(string $param, string $request, ?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -409,15 +415,15 @@ public function modifyWithPath(string $param, string $request, ?array $options = $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -452,7 +458,8 @@ public function modifyWithPath(string $param, string $request, ?array $options = * @throws SeedException * @throws SeedApiException */ - public function modifyWithInlinePath(string $param, ModifyResourceAtInlinedPath $request, ?array $options = null): string { + public function modifyWithInlinePath(string $param, ModifyResourceAtInlinedPath $request, ?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -465,15 +472,15 @@ public function modifyWithInlinePath(string $param, ModifyResourceAtInlinedPath $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Params/Requests/GetWithInlinePathAndQuery.php b/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Params/Requests/GetWithInlinePathAndQuery.php index 578cf60b602c..8687a7b5fbfe 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Params/Requests/GetWithInlinePathAndQuery.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Params/Requests/GetWithInlinePathAndQuery.php @@ -18,8 +18,7 @@ class GetWithInlinePathAndQuery extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->query = $values['query']; } } diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Params/Requests/GetWithMultipleQuery.php b/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Params/Requests/GetWithMultipleQuery.php index 2f7aeca93ee1..b59fea5a169a 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Params/Requests/GetWithMultipleQuery.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Params/Requests/GetWithMultipleQuery.php @@ -24,8 +24,8 @@ class GetWithMultipleQuery extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->query = $values['query'];$this->number = $values['number']; + ) { + $this->query = $values['query']; + $this->number = $values['number']; } } diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Params/Requests/GetWithPathAndQuery.php b/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Params/Requests/GetWithPathAndQuery.php index 5ccafdf8693c..26ab6d60f773 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Params/Requests/GetWithPathAndQuery.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Params/Requests/GetWithPathAndQuery.php @@ -18,8 +18,7 @@ class GetWithPathAndQuery extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->query = $values['query']; } } diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Params/Requests/GetWithQuery.php b/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Params/Requests/GetWithQuery.php index 42463f5ea64f..b1537c064d33 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Params/Requests/GetWithQuery.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Params/Requests/GetWithQuery.php @@ -24,8 +24,8 @@ class GetWithQuery extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->query = $values['query'];$this->number = $values['number']; + ) { + $this->query = $values['query']; + $this->number = $values['number']; } } diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Params/Requests/ModifyResourceAtInlinedPath.php b/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Params/Requests/ModifyResourceAtInlinedPath.php index 8c42cce16944..cf1722cb52da 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Params/Requests/ModifyResourceAtInlinedPath.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Params/Requests/ModifyResourceAtInlinedPath.php @@ -18,8 +18,7 @@ class ModifyResourceAtInlinedPath extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->body = $values['body']; } } diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Primitive/PrimitiveClient.php b/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Primitive/PrimitiveClient.php index 7dbbc6e212df..2832d31c8ce5 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Primitive/PrimitiveClient.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Primitive/PrimitiveClient.php @@ -15,7 +15,7 @@ use DateTime; use Seed\Core\Json\JsonSerializer; -class PrimitiveClient +class PrimitiveClient { /** * @var array{ @@ -43,11 +43,10 @@ class PrimitiveClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -66,7 +65,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getAndReturnString(string $request, ?array $options = null): string { + public function getAndReturnString(string $request, ?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -79,15 +79,15 @@ public function getAndReturnString(string $request, ?array $options = null): str $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -119,7 +119,8 @@ public function getAndReturnString(string $request, ?array $options = null): str * @throws SeedException * @throws SeedApiException */ - public function getAndReturnInt(int $request, ?array $options = null): int { + public function getAndReturnInt(int $request, ?array $options = null): int + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -132,15 +133,15 @@ public function getAndReturnInt(int $request, ?array $options = null): int { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeInt($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -172,7 +173,8 @@ public function getAndReturnInt(int $request, ?array $options = null): int { * @throws SeedException * @throws SeedApiException */ - public function getAndReturnLong(int $request, ?array $options = null): int { + public function getAndReturnLong(int $request, ?array $options = null): int + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -185,15 +187,15 @@ public function getAndReturnLong(int $request, ?array $options = null): int { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeInt($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -225,7 +227,8 @@ public function getAndReturnLong(int $request, ?array $options = null): int { * @throws SeedException * @throws SeedApiException */ - public function getAndReturnDouble(float $request, ?array $options = null): float { + public function getAndReturnDouble(float $request, ?array $options = null): float + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -238,15 +241,15 @@ public function getAndReturnDouble(float $request, ?array $options = null): floa $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeFloat($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -278,7 +281,8 @@ public function getAndReturnDouble(float $request, ?array $options = null): floa * @throws SeedException * @throws SeedApiException */ - public function getAndReturnBool(bool $request, ?array $options = null): bool { + public function getAndReturnBool(bool $request, ?array $options = null): bool + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -291,15 +295,15 @@ public function getAndReturnBool(bool $request, ?array $options = null): bool { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeBool($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -331,7 +335,8 @@ public function getAndReturnBool(bool $request, ?array $options = null): bool { * @throws SeedException * @throws SeedApiException */ - public function getAndReturnDatetime(DateTime $request, ?array $options = null): DateTime { + public function getAndReturnDatetime(DateTime $request, ?array $options = null): DateTime + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -344,15 +349,15 @@ public function getAndReturnDatetime(DateTime $request, ?array $options = null): $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeDateTime($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -384,7 +389,8 @@ public function getAndReturnDatetime(DateTime $request, ?array $options = null): * @throws SeedException * @throws SeedApiException */ - public function getAndReturnDate(DateTime $request, ?array $options = null): DateTime { + public function getAndReturnDate(DateTime $request, ?array $options = null): DateTime + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -397,15 +403,15 @@ public function getAndReturnDate(DateTime $request, ?array $options = null): Dat $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeDate($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -437,7 +443,8 @@ public function getAndReturnDate(DateTime $request, ?array $options = null): Dat * @throws SeedException * @throws SeedApiException */ - public function getAndReturnUuid(string $request, ?array $options = null): string { + public function getAndReturnUuid(string $request, ?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -450,15 +457,15 @@ public function getAndReturnUuid(string $request, ?array $options = null): strin $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -490,7 +497,8 @@ public function getAndReturnUuid(string $request, ?array $options = null): strin * @throws SeedException * @throws SeedApiException */ - public function getAndReturnBase64(string $request, ?array $options = null): string { + public function getAndReturnBase64(string $request, ?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -503,15 +511,15 @@ public function getAndReturnBase64(string $request, ?array $options = null): str $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Put/PutClient.php b/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Put/PutClient.php index b7cd3183617f..37808b73fbec 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Put/PutClient.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Put/PutClient.php @@ -13,7 +13,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class PutClient +class PutClient { /** * @var array{ @@ -41,11 +41,10 @@ class PutClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -64,7 +63,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function add(string $id, ?array $options = null): PutResponse { + public function add(string $id, ?array $options = null): PutResponse + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -76,15 +76,15 @@ public function add(string $id, ?array $options = null): PutResponse { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return PutResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Put/Types/Error.php b/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Put/Types/Error.php index 07a42ccada46..23221c853a7e 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Put/Types/Error.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Put/Types/Error.php @@ -41,15 +41,18 @@ class Error extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->category = $values['category'];$this->code = $values['code'];$this->detail = $values['detail'] ?? null;$this->field = $values['field'] ?? null; + ) { + $this->category = $values['category']; + $this->code = $values['code']; + $this->detail = $values['detail'] ?? null; + $this->field = $values['field'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Put/Types/ErrorCategory.php b/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Put/Types/ErrorCategory.php index 5799791e2fe2..d6c5f2d61b63 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Put/Types/ErrorCategory.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Put/Types/ErrorCategory.php @@ -2,8 +2,8 @@ namespace Seed\Endpoints\Put\Types; -enum ErrorCategory - : string { +enum ErrorCategory: string +{ case ApiError = "API_ERROR"; case AuthenticationError = "AUTHENTICATION_ERROR"; case InvalidRequestError = "INVALID_REQUEST_ERROR"; diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Put/Types/ErrorCode.php b/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Put/Types/ErrorCode.php index 3219d4633600..3dbef3c49780 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Put/Types/ErrorCode.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Put/Types/ErrorCode.php @@ -2,8 +2,8 @@ namespace Seed\Endpoints\Put\Types; -enum ErrorCode - : string { +enum ErrorCode: string +{ case InternalServerError = "INTERNAL_SERVER_ERROR"; case Unauthorized = "UNAUTHORIZED"; case Forbidden = "FORBIDDEN"; diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Put/Types/PutResponse.php b/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Put/Types/PutResponse.php index 6310c7abdaa4..ed9c5a781892 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Put/Types/PutResponse.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Put/Types/PutResponse.php @@ -21,15 +21,15 @@ class PutResponse extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->errors = $values['errors'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Union/UnionClient.php b/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Union/UnionClient.php index 72da2f5edbc0..aa4618c749bb 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Union/UnionClient.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Union/UnionClient.php @@ -13,7 +13,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class UnionClient +class UnionClient { /** * @var array{ @@ -41,11 +41,10 @@ class UnionClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -64,7 +63,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getAndReturnUnion(Animal $request, ?array $options = null): Animal { + public function getAndReturnUnion(Animal $request, ?array $options = null): Animal + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -77,15 +77,15 @@ public function getAndReturnUnion(Animal $request, ?array $options = null): Anim $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return Animal::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Urls/UrlsClient.php b/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Urls/UrlsClient.php index 0eb56899777c..c13a3e54cbfa 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Urls/UrlsClient.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Urls/UrlsClient.php @@ -13,7 +13,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class UrlsClient +class UrlsClient { /** * @var array{ @@ -41,11 +41,10 @@ class UrlsClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -63,7 +62,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function withMixedCase(?array $options = null): string { + public function withMixedCase(?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -75,15 +75,15 @@ public function withMixedCase(?array $options = null): string { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -114,7 +114,8 @@ public function withMixedCase(?array $options = null): string { * @throws SeedException * @throws SeedApiException */ - public function noEndingSlash(?array $options = null): string { + public function noEndingSlash(?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -126,15 +127,15 @@ public function noEndingSlash(?array $options = null): string { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -165,7 +166,8 @@ public function noEndingSlash(?array $options = null): string { * @throws SeedException * @throws SeedApiException */ - public function withEndingSlash(?array $options = null): string { + public function withEndingSlash(?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -177,15 +179,15 @@ public function withEndingSlash(?array $options = null): string { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -216,7 +218,8 @@ public function withEndingSlash(?array $options = null): string { * @throws SeedException * @throws SeedApiException */ - public function withUnderscores(?array $options = null): string { + public function withUnderscores(?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -228,15 +231,15 @@ public function withUnderscores(?array $options = null): string { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/Exceptions/SeedApiException.php b/seed/php-sdk/exhaustive/no-custom-config/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/Exceptions/SeedException.php b/seed/php-sdk/exhaustive/no-custom-config/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/Exceptions/SeedException.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/GeneralErrors/Types/BadObjectRequestInfo.php b/seed/php-sdk/exhaustive/no-custom-config/src/GeneralErrors/Types/BadObjectRequestInfo.php index c60b09552de7..4373a7fac7eb 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/GeneralErrors/Types/BadObjectRequestInfo.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/GeneralErrors/Types/BadObjectRequestInfo.php @@ -20,15 +20,15 @@ class BadObjectRequestInfo extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->message = $values['message']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/InlinedRequests/InlinedRequestsClient.php b/seed/php-sdk/exhaustive/no-custom-config/src/InlinedRequests/InlinedRequestsClient.php index e72c303cfde7..a61f90937a9e 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/InlinedRequests/InlinedRequestsClient.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/InlinedRequests/InlinedRequestsClient.php @@ -14,7 +14,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class InlinedRequestsClient +class InlinedRequestsClient { /** * @var array{ @@ -42,11 +42,10 @@ class InlinedRequestsClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -67,7 +66,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function postWithObjectBodyandResponse(PostWithObjectBody $request, ?array $options = null): ObjectWithOptionalField { + public function postWithObjectBodyandResponse(PostWithObjectBody $request, ?array $options = null): ObjectWithOptionalField + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -80,15 +80,15 @@ public function postWithObjectBodyandResponse(PostWithObjectBody $request, ?arra $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ObjectWithOptionalField::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/InlinedRequests/Requests/PostWithObjectBody.php b/seed/php-sdk/exhaustive/no-custom-config/src/InlinedRequests/Requests/PostWithObjectBody.php index c7dc2ee69c0e..263418b8103c 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/InlinedRequests/Requests/PostWithObjectBody.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/InlinedRequests/Requests/PostWithObjectBody.php @@ -35,8 +35,9 @@ class PostWithObjectBody extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->string = $values['string'];$this->integer = $values['integer'];$this->nestedObject = $values['nestedObject']; + ) { + $this->string = $values['string']; + $this->integer = $values['integer']; + $this->nestedObject = $values['nestedObject']; } } diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/NoAuth/NoAuthClient.php b/seed/php-sdk/exhaustive/no-custom-config/src/NoAuth/NoAuthClient.php index 3564574734a1..df2b2b788d62 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/NoAuth/NoAuthClient.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/NoAuth/NoAuthClient.php @@ -13,7 +13,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class NoAuthClient +class NoAuthClient { /** * @var array{ @@ -41,11 +41,10 @@ class NoAuthClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -66,7 +65,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function postWithNoAuth(mixed $request, ?array $options = null): bool { + public function postWithNoAuth(mixed $request, ?array $options = null): bool + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -79,15 +79,15 @@ public function postWithNoAuth(mixed $request, ?array $options = null): bool { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeBool($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/NoReqBody/NoReqBodyClient.php b/seed/php-sdk/exhaustive/no-custom-config/src/NoReqBody/NoReqBodyClient.php index df02fc9bfb9b..9563f94ac678 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/NoReqBody/NoReqBodyClient.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/NoReqBody/NoReqBodyClient.php @@ -14,7 +14,7 @@ use Psr\Http\Client\ClientExceptionInterface; use Seed\Core\Json\JsonDecoder; -class NoReqBodyClient +class NoReqBodyClient { /** * @var array{ @@ -42,11 +42,10 @@ class NoReqBodyClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -64,7 +63,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getWithNoRequestBody(?array $options = null): ObjectWithOptionalField { + public function getWithNoRequestBody(?array $options = null): ObjectWithOptionalField + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -76,15 +76,15 @@ public function getWithNoRequestBody(?array $options = null): ObjectWithOptional $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ObjectWithOptionalField::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -115,7 +115,8 @@ public function getWithNoRequestBody(?array $options = null): ObjectWithOptional * @throws SeedException * @throws SeedApiException */ - public function postWithNoRequestBody(?array $options = null): string { + public function postWithNoRequestBody(?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -127,15 +128,15 @@ public function postWithNoRequestBody(?array $options = null): string { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/ReqWithHeaders/ReqWithHeadersClient.php b/seed/php-sdk/exhaustive/no-custom-config/src/ReqWithHeaders/ReqWithHeadersClient.php index cd2e09a4c3fd..6be34fce7ec2 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/ReqWithHeaders/ReqWithHeadersClient.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/ReqWithHeaders/ReqWithHeadersClient.php @@ -12,7 +12,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class ReqWithHeadersClient +class ReqWithHeadersClient { /** * @var array{ @@ -40,11 +40,10 @@ class ReqWithHeadersClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -62,7 +61,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getWithCustomHeader(ReqWithHeaders $request, ?array $options = null): void { + public function getWithCustomHeader(ReqWithHeaders $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); $headers = []; $headers['X-TEST-SERVICE-HEADER'] = $request->xTestServiceHeader; @@ -79,12 +79,12 @@ public function getWithCustomHeader(ReqWithHeaders $request, ?array $options = n $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/ReqWithHeaders/Requests/ReqWithHeaders.php b/seed/php-sdk/exhaustive/no-custom-config/src/ReqWithHeaders/Requests/ReqWithHeaders.php index a8d88e16fafd..c55d0c272eb7 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/ReqWithHeaders/Requests/ReqWithHeaders.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/ReqWithHeaders/Requests/ReqWithHeaders.php @@ -30,8 +30,9 @@ class ReqWithHeaders extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->xTestServiceHeader = $values['xTestServiceHeader'];$this->xTestEndpointHeader = $values['xTestEndpointHeader'];$this->body = $values['body']; + ) { + $this->xTestServiceHeader = $values['xTestServiceHeader']; + $this->xTestEndpointHeader = $values['xTestEndpointHeader']; + $this->body = $values['body']; } } diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/SeedClient.php b/seed/php-sdk/exhaustive/no-custom-config/src/SeedClient.php index 36207d35c0b7..1e3444340c3c 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/SeedClient.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/SeedClient.php @@ -10,7 +10,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var EndpointsClient $endpoints @@ -66,29 +66,28 @@ class SeedClient public function __construct( ?string $token = null, ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - if ($token != null){ + if ($token != null) { $defaultHeaders['Authorization'] = "Bearer $token"; } - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->endpoints = new EndpointsClient($this->client, $this->options); $this->inlinedRequests = new InlinedRequestsClient($this->client, $this->options); $this->noAuth = new NoAuthClient($this->client, $this->options); diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/Types/Docs/Types/ObjectWithDocs.php b/seed/php-sdk/exhaustive/no-custom-config/src/Types/Docs/Types/ObjectWithDocs.php index 1d4fe1f0e04b..db40e85452f9 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/Types/Docs/Types/ObjectWithDocs.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/Types/Docs/Types/ObjectWithDocs.php @@ -9,11 +9,11 @@ class ObjectWithDocs extends JsonSerializableType { /** * Characters that could lead to broken generated SDKs: - * + * * Markdown Escapes: * - \_: Escaped underscore (e.g., FOO\_BAR) * - \*: Escaped asterisk - * + * * JSDoc (JavaScript/TypeScript): * - @: Used for JSDoc tags * - {: }: Used for type definitions @@ -22,7 +22,7 @@ class ObjectWithDocs extends JsonSerializableType * - /**: JSDoc comment start * - ** /: JSDoc comment end * - &: HTML entities - * + * * XMLDoc (C#): * - <: >: XML tags * - &: ': ": <: >: XML special characters @@ -30,7 +30,7 @@ class ObjectWithDocs extends JsonSerializableType * - ///: Comment marker * - /**: Block comment start * - ** /: Block comment end - * + * * Javadoc (Java): * - @: Used for Javadoc tags * - <: >: HTML tags @@ -38,7 +38,7 @@ class ObjectWithDocs extends JsonSerializableType * - *: Can interfere with comment blocks * - /**: Javadoc comment start * - ** /: Javadoc comment end - * + * * Doxygen (C++): * - \: Used for Doxygen commands * - @: Alternative command prefix @@ -46,7 +46,7 @@ class ObjectWithDocs extends JsonSerializableType * - &: HTML entities * - /**: C-style comment start * - ** /: C-style comment end - * + * * RDoc (Ruby): * - :: Used in symbol notation * - =: Section markers @@ -58,7 +58,7 @@ class ObjectWithDocs extends JsonSerializableType * - %: String literal delimiter * - #{: String interpolation start * - }: String interpolation end - * + * * PHPDoc (PHP): * - @: Used for PHPDoc tags * - {: }: Used for type definitions @@ -80,15 +80,15 @@ class ObjectWithDocs extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->string = $values['string']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/Types/Enum/Types/WeatherReport.php b/seed/php-sdk/exhaustive/no-custom-config/src/Types/Enum/Types/WeatherReport.php index a8246d8ef1d7..a363d5e7f22b 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/Types/Enum/Types/WeatherReport.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/Types/Enum/Types/WeatherReport.php @@ -2,8 +2,8 @@ namespace Seed\Types\Enum\Types; -enum WeatherReport - : string { +enum WeatherReport: string +{ case Sunny = "SUNNY"; case Cloudy = "CLOUDY"; case Raining = "RAINING"; diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/Types/Object/Types/DoubleOptional.php b/seed/php-sdk/exhaustive/no-custom-config/src/Types/Object/Types/DoubleOptional.php index 6c0aa495d527..87087ca7c274 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/Types/Object/Types/DoubleOptional.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/Types/Object/Types/DoubleOptional.php @@ -20,15 +20,15 @@ class DoubleOptional extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->optionalAlias = $values['optionalAlias'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/Types/Object/Types/NestedObjectWithOptionalField.php b/seed/php-sdk/exhaustive/no-custom-config/src/Types/Object/Types/NestedObjectWithOptionalField.php index 56c784e8f949..ed0fca4f6e14 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/Types/Object/Types/NestedObjectWithOptionalField.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/Types/Object/Types/NestedObjectWithOptionalField.php @@ -27,15 +27,16 @@ class NestedObjectWithOptionalField extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->string = $values['string'] ?? null;$this->nestedObject = $values['nestedObject'] ?? null; + ) { + $this->string = $values['string'] ?? null; + $this->nestedObject = $values['nestedObject'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/Types/Object/Types/NestedObjectWithRequiredField.php b/seed/php-sdk/exhaustive/no-custom-config/src/Types/Object/Types/NestedObjectWithRequiredField.php index 15d9bb7bea6c..e4fc08d3520f 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/Types/Object/Types/NestedObjectWithRequiredField.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/Types/Object/Types/NestedObjectWithRequiredField.php @@ -27,15 +27,16 @@ class NestedObjectWithRequiredField extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->string = $values['string'];$this->nestedObject = $values['nestedObject']; + ) { + $this->string = $values['string']; + $this->nestedObject = $values['nestedObject']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/Types/Object/Types/ObjectWithMapOfMap.php b/seed/php-sdk/exhaustive/no-custom-config/src/Types/Object/Types/ObjectWithMapOfMap.php index 87497c99940a..20976a704c8e 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/Types/Object/Types/ObjectWithMapOfMap.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/Types/Object/Types/ObjectWithMapOfMap.php @@ -21,15 +21,15 @@ class ObjectWithMapOfMap extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->map = $values['map']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/Types/Object/Types/ObjectWithOptionalField.php b/seed/php-sdk/exhaustive/no-custom-config/src/Types/Object/Types/ObjectWithOptionalField.php index a79d31e065e3..af6fe1b400c6 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/Types/Object/Types/ObjectWithOptionalField.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/Types/Object/Types/ObjectWithOptionalField.php @@ -107,15 +107,27 @@ class ObjectWithOptionalField extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->string = $values['string'] ?? null;$this->integer = $values['integer'] ?? null;$this->long = $values['long'] ?? null;$this->double = $values['double'] ?? null;$this->bool = $values['bool'] ?? null;$this->datetime = $values['datetime'] ?? null;$this->date = $values['date'] ?? null;$this->uuid = $values['uuid'] ?? null;$this->base64 = $values['base64'] ?? null;$this->list = $values['list'] ?? null;$this->set = $values['set'] ?? null;$this->map = $values['map'] ?? null;$this->bigint = $values['bigint'] ?? null; + ) { + $this->string = $values['string'] ?? null; + $this->integer = $values['integer'] ?? null; + $this->long = $values['long'] ?? null; + $this->double = $values['double'] ?? null; + $this->bool = $values['bool'] ?? null; + $this->datetime = $values['datetime'] ?? null; + $this->date = $values['date'] ?? null; + $this->uuid = $values['uuid'] ?? null; + $this->base64 = $values['base64'] ?? null; + $this->list = $values['list'] ?? null; + $this->set = $values['set'] ?? null; + $this->map = $values['map'] ?? null; + $this->bigint = $values['bigint'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/Types/Object/Types/ObjectWithRequiredField.php b/seed/php-sdk/exhaustive/no-custom-config/src/Types/Object/Types/ObjectWithRequiredField.php index d17970deb278..22fe0c58df60 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/Types/Object/Types/ObjectWithRequiredField.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/Types/Object/Types/ObjectWithRequiredField.php @@ -20,15 +20,15 @@ class ObjectWithRequiredField extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->string = $values['string']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/Types/Union/Types/Animal.php b/seed/php-sdk/exhaustive/no-custom-config/src/Types/Union/Types/Animal.php index 40be624b51da..8138ea53a41e 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/Types/Union/Types/Animal.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/Types/Union/Types/Animal.php @@ -42,16 +42,17 @@ class Animal extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->animal = $values['animal'];$this->value = $values['value']; + ) { + $this->animal = $values['animal']; + $this->value = $values['value']; } /** * @param Dog $dog * @return Animal */ - public static function dog(Dog $dog): Animal { + public static function dog(Dog $dog): Animal + { return new Animal([ 'animal' => 'dog', 'value' => $dog, @@ -62,7 +63,8 @@ public static function dog(Dog $dog): Animal { * @param Cat $cat * @return Animal */ - public static function cat(Cat $cat): Animal { + public static function cat(Cat $cat): Animal + { return new Animal([ 'animal' => 'cat', 'value' => $cat, @@ -72,61 +74,67 @@ public static function cat(Cat $cat): Animal { /** * @return bool */ - public function isDog(): bool { - return $this->value instanceof Dog&& $this->animal === 'dog'; + public function isDog(): bool + { + return $this->value instanceof Dog && $this->animal === 'dog'; } /** * @return Dog */ - public function asDog(): Dog { - if (!($this->value instanceof Dog&& $this->animal === 'dog')){ + public function asDog(): Dog + { + if (!($this->value instanceof Dog && $this->animal === 'dog')) { throw new Exception( "Expected dog; got " . $this->animal . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isCat(): bool { - return $this->value instanceof Cat&& $this->animal === 'cat'; + public function isCat(): bool + { + return $this->value instanceof Cat && $this->animal === 'cat'; } /** * @return Cat */ - public function asCat(): Cat { - if (!($this->value instanceof Cat&& $this->animal === 'cat')){ + public function asCat(): Cat + { + if (!($this->value instanceof Cat && $this->animal === 'cat')) { throw new Exception( "Expected cat; got " . $this->animal . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['animal'] = $this->animal; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->animal){ + + switch ($this->animal) { case 'dog': $value = $this->asDog()->jsonSerialize(); $result = array_merge($value, $result); @@ -137,26 +145,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -165,22 +174,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('animal', $data)){ + if (!array_key_exists('animal', $data)) { throw new Exception( "JSON data is missing property 'animal'", ); } $animal = $data['animal']; - if (!(is_string($animal))){ + if (!(is_string($animal))) { throw new Exception( "Expected property 'animal' in JSON data to be string, instead received " . get_debug_type($data['animal']), ); } - + $args['animal'] = $animal; - switch ($animal){ + switch ($animal) { case 'dog': $args['value'] = Dog::jsonDeserialize($data); break; @@ -192,7 +202,7 @@ public static function jsonDeserialize(array $data): static { $args['animal'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/Types/Union/Types/Cat.php b/seed/php-sdk/exhaustive/no-custom-config/src/Types/Union/Types/Cat.php index 328ffbeae41b..cd2369d2fee5 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/Types/Union/Types/Cat.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/Types/Union/Types/Cat.php @@ -27,15 +27,16 @@ class Cat extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->likesToMeow = $values['likesToMeow']; + ) { + $this->name = $values['name']; + $this->likesToMeow = $values['likesToMeow']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/Types/Union/Types/Dog.php b/seed/php-sdk/exhaustive/no-custom-config/src/Types/Union/Types/Dog.php index a09bdf94cca2..dad76628a799 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/Types/Union/Types/Dog.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/Types/Union/Types/Dog.php @@ -27,15 +27,16 @@ class Dog extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->likesToWoof = $values['likesToWoof']; + ) { + $this->name = $values['name']; + $this->likesToWoof = $values['likesToWoof']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/Utils/File.php b/seed/php-sdk/exhaustive/no-custom-config/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/Utils/File.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/exhaustive/no-custom-config/tests/Core/Client/RawClientTest.php b/seed/php-sdk/exhaustive/no-custom-config/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/exhaustive/no-custom-config/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/exhaustive/no-custom-config/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/exhaustive/no-custom-config/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/exhaustive/no-custom-config/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/exhaustive/no-custom-config/tests/Core/Json/EnumTest.php b/seed/php-sdk/exhaustive/no-custom-config/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/exhaustive/no-custom-config/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/exhaustive/no-custom-config/tests/Core/Json/TraitTest.php b/seed/php-sdk/exhaustive/no-custom-config/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/exhaustive/no-custom-config/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/exhaustive/no-custom-config/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/exhaustive/no-custom-config/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/exhaustive/no-custom-config/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/exhaustive/wire-tests/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/exhaustive/wire-tests/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/exhaustive/wire-tests/src/Core/Client/RawClient.php b/seed/php-sdk/exhaustive/wire-tests/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/Core/Client/RawClient.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/exhaustive/wire-tests/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/exhaustive/wire-tests/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/exhaustive/wire-tests/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/exhaustive/wire-tests/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/exhaustive/wire-tests/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/exhaustive/wire-tests/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/exhaustive/wire-tests/src/Core/Types/ArrayType.php b/seed/php-sdk/exhaustive/wire-tests/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/exhaustive/wire-tests/src/Core/Types/Constant.php b/seed/php-sdk/exhaustive/wire-tests/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/Core/Types/Constant.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/exhaustive/wire-tests/src/Core/Types/Union.php b/seed/php-sdk/exhaustive/wire-tests/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/Core/Types/Union.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Container/ContainerClient.php b/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Container/ContainerClient.php index 3c5d43fa5ed4..a92eb5aef5a5 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Container/ContainerClient.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Container/ContainerClient.php @@ -15,7 +15,7 @@ use Psr\Http\Client\ClientExceptionInterface; use Seed\Types\Object\Types\ObjectWithRequiredField; -class ContainerClient +class ContainerClient { /** * @var array{ @@ -43,11 +43,10 @@ class ContainerClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -66,7 +65,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getAndReturnListOfPrimitives(array $request, ?array $options = null): array { + public function getAndReturnListOfPrimitives(array $request, ?array $options = null): array + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -79,15 +79,15 @@ public function getAndReturnListOfPrimitives(array $request, ?array $options = n $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, ['string']); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -119,7 +119,8 @@ public function getAndReturnListOfPrimitives(array $request, ?array $options = n * @throws SeedException * @throws SeedApiException */ - public function getAndReturnListOfObjects(array $request, ?array $options = null): array { + public function getAndReturnListOfObjects(array $request, ?array $options = null): array + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -132,15 +133,15 @@ public function getAndReturnListOfObjects(array $request, ?array $options = null $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, [ObjectWithRequiredField::class]); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -172,7 +173,8 @@ public function getAndReturnListOfObjects(array $request, ?array $options = null * @throws SeedException * @throws SeedApiException */ - public function getAndReturnSetOfPrimitives(array $request, ?array $options = null): array { + public function getAndReturnSetOfPrimitives(array $request, ?array $options = null): array + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -185,15 +187,15 @@ public function getAndReturnSetOfPrimitives(array $request, ?array $options = nu $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, ['string']); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -225,7 +227,8 @@ public function getAndReturnSetOfPrimitives(array $request, ?array $options = nu * @throws SeedException * @throws SeedApiException */ - public function getAndReturnSetOfObjects(array $request, ?array $options = null): array { + public function getAndReturnSetOfObjects(array $request, ?array $options = null): array + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -238,15 +241,15 @@ public function getAndReturnSetOfObjects(array $request, ?array $options = null) $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, [ObjectWithRequiredField::class]); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -278,7 +281,8 @@ public function getAndReturnSetOfObjects(array $request, ?array $options = null) * @throws SeedException * @throws SeedApiException */ - public function getAndReturnMapPrimToPrim(array $request, ?array $options = null): array { + public function getAndReturnMapPrimToPrim(array $request, ?array $options = null): array + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -291,15 +295,15 @@ public function getAndReturnMapPrimToPrim(array $request, ?array $options = null $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, ['string' => 'string']); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -331,7 +335,8 @@ public function getAndReturnMapPrimToPrim(array $request, ?array $options = null * @throws SeedException * @throws SeedApiException */ - public function getAndReturnMapOfPrimToObject(array $request, ?array $options = null): array { + public function getAndReturnMapOfPrimToObject(array $request, ?array $options = null): array + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -344,15 +349,15 @@ public function getAndReturnMapOfPrimToObject(array $request, ?array $options = $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, ['string' => ObjectWithRequiredField::class]); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -384,7 +389,8 @@ public function getAndReturnMapOfPrimToObject(array $request, ?array $options = * @throws SeedException * @throws SeedApiException */ - public function getAndReturnOptional(?ObjectWithRequiredField $request = null, ?array $options = null): ?ObjectWithRequiredField { + public function getAndReturnOptional(?ObjectWithRequiredField $request = null, ?array $options = null): ?ObjectWithRequiredField + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -397,18 +403,18 @@ public function getAndReturnOptional(?ObjectWithRequiredField $request = null, ? $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); - if (empty($json)){ + if (empty($json)) { return null; } return ObjectWithRequiredField::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/ContentType/ContentTypeClient.php b/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/ContentType/ContentTypeClient.php index 69de06db85be..464542437c9f 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/ContentType/ContentTypeClient.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/ContentType/ContentTypeClient.php @@ -12,7 +12,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class ContentTypeClient +class ContentTypeClient { /** * @var array{ @@ -40,11 +40,10 @@ class ContentTypeClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -62,7 +61,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function postJsonPatchContentType(ObjectWithOptionalField $request, ?array $options = null): void { + public function postJsonPatchContentType(ObjectWithOptionalField $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -75,12 +75,12 @@ public function postJsonPatchContentType(ObjectWithOptionalField $request, ?arra $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -111,7 +111,8 @@ public function postJsonPatchContentType(ObjectWithOptionalField $request, ?arra * @throws SeedException * @throws SeedApiException */ - public function postJsonPatchContentWithCharsetType(ObjectWithOptionalField $request, ?array $options = null): void { + public function postJsonPatchContentWithCharsetType(ObjectWithOptionalField $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -124,12 +125,12 @@ public function postJsonPatchContentWithCharsetType(ObjectWithOptionalField $req $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/EndpointsClient.php b/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/EndpointsClient.php index 2d244967f18f..8fc3e76ab29d 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/EndpointsClient.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/EndpointsClient.php @@ -15,7 +15,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class EndpointsClient +class EndpointsClient { /** * @var ContainerClient $container @@ -93,11 +93,10 @@ class EndpointsClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; $this->container = new ContainerClient($this->client, $this->options); diff --git a/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Enum/EnumClient.php b/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Enum/EnumClient.php index 244257948b89..be5d18bb5cf0 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Enum/EnumClient.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Enum/EnumClient.php @@ -14,7 +14,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class EnumClient +class EnumClient { /** * @var array{ @@ -42,11 +42,10 @@ class EnumClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -65,7 +64,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getAndReturnEnum(string $request, ?array $options = null): string { + public function getAndReturnEnum(string $request, ?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -78,15 +78,15 @@ public function getAndReturnEnum(string $request, ?array $options = null): strin $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/HttpMethods/HttpMethodsClient.php b/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/HttpMethods/HttpMethodsClient.php index 011ac0d91566..c07112348c2c 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/HttpMethods/HttpMethodsClient.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/HttpMethods/HttpMethodsClient.php @@ -15,7 +15,7 @@ use Seed\Types\Object\Types\ObjectWithRequiredField; use Seed\Types\Object\Types\ObjectWithOptionalField; -class HttpMethodsClient +class HttpMethodsClient { /** * @var array{ @@ -43,11 +43,10 @@ class HttpMethodsClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -66,7 +65,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function testGet(string $id, ?array $options = null): string { + public function testGet(string $id, ?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -78,15 +78,15 @@ public function testGet(string $id, ?array $options = null): string { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -118,7 +118,8 @@ public function testGet(string $id, ?array $options = null): string { * @throws SeedException * @throws SeedApiException */ - public function testPost(ObjectWithRequiredField $request, ?array $options = null): ObjectWithOptionalField { + public function testPost(ObjectWithRequiredField $request, ?array $options = null): ObjectWithOptionalField + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -131,15 +132,15 @@ public function testPost(ObjectWithRequiredField $request, ?array $options = nul $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ObjectWithOptionalField::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -172,7 +173,8 @@ public function testPost(ObjectWithRequiredField $request, ?array $options = nul * @throws SeedException * @throws SeedApiException */ - public function testPut(string $id, ObjectWithRequiredField $request, ?array $options = null): ObjectWithOptionalField { + public function testPut(string $id, ObjectWithRequiredField $request, ?array $options = null): ObjectWithOptionalField + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -185,15 +187,15 @@ public function testPut(string $id, ObjectWithRequiredField $request, ?array $op $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ObjectWithOptionalField::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -226,7 +228,8 @@ public function testPut(string $id, ObjectWithRequiredField $request, ?array $op * @throws SeedException * @throws SeedApiException */ - public function testPatch(string $id, ObjectWithOptionalField $request, ?array $options = null): ObjectWithOptionalField { + public function testPatch(string $id, ObjectWithOptionalField $request, ?array $options = null): ObjectWithOptionalField + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -239,15 +242,15 @@ public function testPatch(string $id, ObjectWithOptionalField $request, ?array $ $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ObjectWithOptionalField::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -279,7 +282,8 @@ public function testPatch(string $id, ObjectWithOptionalField $request, ?array $ * @throws SeedException * @throws SeedApiException */ - public function testDelete(string $id, ?array $options = null): bool { + public function testDelete(string $id, ?array $options = null): bool + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -291,15 +295,15 @@ public function testDelete(string $id, ?array $options = null): bool { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeBool($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Object/ObjectClient.php b/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Object/ObjectClient.php index 0b28c9423134..0bba1bdf6402 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Object/ObjectClient.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Object/ObjectClient.php @@ -18,7 +18,7 @@ use Seed\Types\Object\Types\NestedObjectWithRequiredField; use Seed\Core\Json\JsonSerializer; -class ObjectClient +class ObjectClient { /** * @var array{ @@ -46,11 +46,10 @@ class ObjectClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -69,7 +68,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getAndReturnWithOptionalField(ObjectWithOptionalField $request, ?array $options = null): ObjectWithOptionalField { + public function getAndReturnWithOptionalField(ObjectWithOptionalField $request, ?array $options = null): ObjectWithOptionalField + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -82,15 +82,15 @@ public function getAndReturnWithOptionalField(ObjectWithOptionalField $request, $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ObjectWithOptionalField::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -122,7 +122,8 @@ public function getAndReturnWithOptionalField(ObjectWithOptionalField $request, * @throws SeedException * @throws SeedApiException */ - public function getAndReturnWithRequiredField(ObjectWithRequiredField $request, ?array $options = null): ObjectWithRequiredField { + public function getAndReturnWithRequiredField(ObjectWithRequiredField $request, ?array $options = null): ObjectWithRequiredField + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -135,15 +136,15 @@ public function getAndReturnWithRequiredField(ObjectWithRequiredField $request, $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ObjectWithRequiredField::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -175,7 +176,8 @@ public function getAndReturnWithRequiredField(ObjectWithRequiredField $request, * @throws SeedException * @throws SeedApiException */ - public function getAndReturnWithMapOfMap(ObjectWithMapOfMap $request, ?array $options = null): ObjectWithMapOfMap { + public function getAndReturnWithMapOfMap(ObjectWithMapOfMap $request, ?array $options = null): ObjectWithMapOfMap + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -188,15 +190,15 @@ public function getAndReturnWithMapOfMap(ObjectWithMapOfMap $request, ?array $op $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ObjectWithMapOfMap::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -228,7 +230,8 @@ public function getAndReturnWithMapOfMap(ObjectWithMapOfMap $request, ?array $op * @throws SeedException * @throws SeedApiException */ - public function getAndReturnNestedWithOptionalField(NestedObjectWithOptionalField $request, ?array $options = null): NestedObjectWithOptionalField { + public function getAndReturnNestedWithOptionalField(NestedObjectWithOptionalField $request, ?array $options = null): NestedObjectWithOptionalField + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -241,15 +244,15 @@ public function getAndReturnNestedWithOptionalField(NestedObjectWithOptionalFiel $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return NestedObjectWithOptionalField::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -282,7 +285,8 @@ public function getAndReturnNestedWithOptionalField(NestedObjectWithOptionalFiel * @throws SeedException * @throws SeedApiException */ - public function getAndReturnNestedWithRequiredField(string $string, NestedObjectWithRequiredField $request, ?array $options = null): NestedObjectWithRequiredField { + public function getAndReturnNestedWithRequiredField(string $string, NestedObjectWithRequiredField $request, ?array $options = null): NestedObjectWithRequiredField + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -295,15 +299,15 @@ public function getAndReturnNestedWithRequiredField(string $string, NestedObject $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return NestedObjectWithRequiredField::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -335,7 +339,8 @@ public function getAndReturnNestedWithRequiredField(string $string, NestedObject * @throws SeedException * @throws SeedApiException */ - public function getAndReturnNestedWithRequiredFieldAsList(array $request, ?array $options = null): NestedObjectWithRequiredField { + public function getAndReturnNestedWithRequiredFieldAsList(array $request, ?array $options = null): NestedObjectWithRequiredField + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -348,15 +353,15 @@ public function getAndReturnNestedWithRequiredFieldAsList(array $request, ?array $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return NestedObjectWithRequiredField::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Params/ParamsClient.php b/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Params/ParamsClient.php index ece9be725973..4d7b29f8fdfa 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Params/ParamsClient.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Params/ParamsClient.php @@ -18,7 +18,7 @@ use Seed\Endpoints\Params\Requests\GetWithInlinePathAndQuery; use Seed\Endpoints\Params\Requests\ModifyResourceAtInlinedPath; -class ParamsClient +class ParamsClient { /** * @var array{ @@ -46,11 +46,10 @@ class ParamsClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -71,7 +70,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getWithPath(string $param, ?array $options = null): string { + public function getWithPath(string $param, ?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -83,15 +83,15 @@ public function getWithPath(string $param, ?array $options = null): string { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -125,7 +125,8 @@ public function getWithPath(string $param, ?array $options = null): string { * @throws SeedException * @throws SeedApiException */ - public function getWithInlinePath(string $param, ?array $options = null): string { + public function getWithInlinePath(string $param, ?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -137,15 +138,15 @@ public function getWithInlinePath(string $param, ?array $options = null): string $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -178,7 +179,8 @@ public function getWithInlinePath(string $param, ?array $options = null): string * @throws SeedException * @throws SeedApiException */ - public function getWithQuery(GetWithQuery $request, ?array $options = null): void { + public function getWithQuery(GetWithQuery $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); $query = []; $query['query'] = $request->query; @@ -194,12 +196,12 @@ public function getWithQuery(GetWithQuery $request, ?array $options = null): voi $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -232,7 +234,8 @@ public function getWithQuery(GetWithQuery $request, ?array $options = null): voi * @throws SeedException * @throws SeedApiException */ - public function getWithAllowMultipleQuery(GetWithMultipleQuery $request, ?array $options = null): void { + public function getWithAllowMultipleQuery(GetWithMultipleQuery $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); $query = []; $query['query'] = $request->query; @@ -248,12 +251,12 @@ public function getWithAllowMultipleQuery(GetWithMultipleQuery $request, ?array $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -287,7 +290,8 @@ public function getWithAllowMultipleQuery(GetWithMultipleQuery $request, ?array * @throws SeedException * @throws SeedApiException */ - public function getWithPathAndQuery(string $param, GetWithPathAndQuery $request, ?array $options = null): void { + public function getWithPathAndQuery(string $param, GetWithPathAndQuery $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); $query = []; $query['query'] = $request->query; @@ -302,12 +306,12 @@ public function getWithPathAndQuery(string $param, GetWithPathAndQuery $request, $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -341,7 +345,8 @@ public function getWithPathAndQuery(string $param, GetWithPathAndQuery $request, * @throws SeedException * @throws SeedApiException */ - public function getWithInlinePathAndQuery(string $param, GetWithInlinePathAndQuery $request, ?array $options = null): void { + public function getWithInlinePathAndQuery(string $param, GetWithInlinePathAndQuery $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); $query = []; $query['query'] = $request->query; @@ -356,12 +361,12 @@ public function getWithInlinePathAndQuery(string $param, GetWithInlinePathAndQue $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -396,7 +401,8 @@ public function getWithInlinePathAndQuery(string $param, GetWithInlinePathAndQue * @throws SeedException * @throws SeedApiException */ - public function modifyWithPath(string $param, string $request, ?array $options = null): string { + public function modifyWithPath(string $param, string $request, ?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -409,15 +415,15 @@ public function modifyWithPath(string $param, string $request, ?array $options = $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -452,7 +458,8 @@ public function modifyWithPath(string $param, string $request, ?array $options = * @throws SeedException * @throws SeedApiException */ - public function modifyWithInlinePath(string $param, ModifyResourceAtInlinedPath $request, ?array $options = null): string { + public function modifyWithInlinePath(string $param, ModifyResourceAtInlinedPath $request, ?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -465,15 +472,15 @@ public function modifyWithInlinePath(string $param, ModifyResourceAtInlinedPath $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Params/Requests/GetWithInlinePathAndQuery.php b/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Params/Requests/GetWithInlinePathAndQuery.php index 578cf60b602c..8687a7b5fbfe 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Params/Requests/GetWithInlinePathAndQuery.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Params/Requests/GetWithInlinePathAndQuery.php @@ -18,8 +18,7 @@ class GetWithInlinePathAndQuery extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->query = $values['query']; } } diff --git a/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Params/Requests/GetWithMultipleQuery.php b/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Params/Requests/GetWithMultipleQuery.php index 2f7aeca93ee1..b59fea5a169a 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Params/Requests/GetWithMultipleQuery.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Params/Requests/GetWithMultipleQuery.php @@ -24,8 +24,8 @@ class GetWithMultipleQuery extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->query = $values['query'];$this->number = $values['number']; + ) { + $this->query = $values['query']; + $this->number = $values['number']; } } diff --git a/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Params/Requests/GetWithPathAndQuery.php b/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Params/Requests/GetWithPathAndQuery.php index 5ccafdf8693c..26ab6d60f773 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Params/Requests/GetWithPathAndQuery.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Params/Requests/GetWithPathAndQuery.php @@ -18,8 +18,7 @@ class GetWithPathAndQuery extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->query = $values['query']; } } diff --git a/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Params/Requests/GetWithQuery.php b/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Params/Requests/GetWithQuery.php index 42463f5ea64f..b1537c064d33 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Params/Requests/GetWithQuery.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Params/Requests/GetWithQuery.php @@ -24,8 +24,8 @@ class GetWithQuery extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->query = $values['query'];$this->number = $values['number']; + ) { + $this->query = $values['query']; + $this->number = $values['number']; } } diff --git a/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Params/Requests/ModifyResourceAtInlinedPath.php b/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Params/Requests/ModifyResourceAtInlinedPath.php index 8c42cce16944..cf1722cb52da 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Params/Requests/ModifyResourceAtInlinedPath.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Params/Requests/ModifyResourceAtInlinedPath.php @@ -18,8 +18,7 @@ class ModifyResourceAtInlinedPath extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->body = $values['body']; } } diff --git a/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Primitive/PrimitiveClient.php b/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Primitive/PrimitiveClient.php index 7dbbc6e212df..2832d31c8ce5 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Primitive/PrimitiveClient.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Primitive/PrimitiveClient.php @@ -15,7 +15,7 @@ use DateTime; use Seed\Core\Json\JsonSerializer; -class PrimitiveClient +class PrimitiveClient { /** * @var array{ @@ -43,11 +43,10 @@ class PrimitiveClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -66,7 +65,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getAndReturnString(string $request, ?array $options = null): string { + public function getAndReturnString(string $request, ?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -79,15 +79,15 @@ public function getAndReturnString(string $request, ?array $options = null): str $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -119,7 +119,8 @@ public function getAndReturnString(string $request, ?array $options = null): str * @throws SeedException * @throws SeedApiException */ - public function getAndReturnInt(int $request, ?array $options = null): int { + public function getAndReturnInt(int $request, ?array $options = null): int + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -132,15 +133,15 @@ public function getAndReturnInt(int $request, ?array $options = null): int { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeInt($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -172,7 +173,8 @@ public function getAndReturnInt(int $request, ?array $options = null): int { * @throws SeedException * @throws SeedApiException */ - public function getAndReturnLong(int $request, ?array $options = null): int { + public function getAndReturnLong(int $request, ?array $options = null): int + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -185,15 +187,15 @@ public function getAndReturnLong(int $request, ?array $options = null): int { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeInt($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -225,7 +227,8 @@ public function getAndReturnLong(int $request, ?array $options = null): int { * @throws SeedException * @throws SeedApiException */ - public function getAndReturnDouble(float $request, ?array $options = null): float { + public function getAndReturnDouble(float $request, ?array $options = null): float + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -238,15 +241,15 @@ public function getAndReturnDouble(float $request, ?array $options = null): floa $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeFloat($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -278,7 +281,8 @@ public function getAndReturnDouble(float $request, ?array $options = null): floa * @throws SeedException * @throws SeedApiException */ - public function getAndReturnBool(bool $request, ?array $options = null): bool { + public function getAndReturnBool(bool $request, ?array $options = null): bool + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -291,15 +295,15 @@ public function getAndReturnBool(bool $request, ?array $options = null): bool { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeBool($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -331,7 +335,8 @@ public function getAndReturnBool(bool $request, ?array $options = null): bool { * @throws SeedException * @throws SeedApiException */ - public function getAndReturnDatetime(DateTime $request, ?array $options = null): DateTime { + public function getAndReturnDatetime(DateTime $request, ?array $options = null): DateTime + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -344,15 +349,15 @@ public function getAndReturnDatetime(DateTime $request, ?array $options = null): $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeDateTime($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -384,7 +389,8 @@ public function getAndReturnDatetime(DateTime $request, ?array $options = null): * @throws SeedException * @throws SeedApiException */ - public function getAndReturnDate(DateTime $request, ?array $options = null): DateTime { + public function getAndReturnDate(DateTime $request, ?array $options = null): DateTime + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -397,15 +403,15 @@ public function getAndReturnDate(DateTime $request, ?array $options = null): Dat $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeDate($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -437,7 +443,8 @@ public function getAndReturnDate(DateTime $request, ?array $options = null): Dat * @throws SeedException * @throws SeedApiException */ - public function getAndReturnUuid(string $request, ?array $options = null): string { + public function getAndReturnUuid(string $request, ?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -450,15 +457,15 @@ public function getAndReturnUuid(string $request, ?array $options = null): strin $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -490,7 +497,8 @@ public function getAndReturnUuid(string $request, ?array $options = null): strin * @throws SeedException * @throws SeedApiException */ - public function getAndReturnBase64(string $request, ?array $options = null): string { + public function getAndReturnBase64(string $request, ?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -503,15 +511,15 @@ public function getAndReturnBase64(string $request, ?array $options = null): str $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Put/PutClient.php b/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Put/PutClient.php index b7cd3183617f..37808b73fbec 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Put/PutClient.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Put/PutClient.php @@ -13,7 +13,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class PutClient +class PutClient { /** * @var array{ @@ -41,11 +41,10 @@ class PutClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -64,7 +63,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function add(string $id, ?array $options = null): PutResponse { + public function add(string $id, ?array $options = null): PutResponse + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -76,15 +76,15 @@ public function add(string $id, ?array $options = null): PutResponse { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return PutResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Put/Types/Error.php b/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Put/Types/Error.php index 07a42ccada46..23221c853a7e 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Put/Types/Error.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Put/Types/Error.php @@ -41,15 +41,18 @@ class Error extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->category = $values['category'];$this->code = $values['code'];$this->detail = $values['detail'] ?? null;$this->field = $values['field'] ?? null; + ) { + $this->category = $values['category']; + $this->code = $values['code']; + $this->detail = $values['detail'] ?? null; + $this->field = $values['field'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Put/Types/ErrorCategory.php b/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Put/Types/ErrorCategory.php index 5799791e2fe2..d6c5f2d61b63 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Put/Types/ErrorCategory.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Put/Types/ErrorCategory.php @@ -2,8 +2,8 @@ namespace Seed\Endpoints\Put\Types; -enum ErrorCategory - : string { +enum ErrorCategory: string +{ case ApiError = "API_ERROR"; case AuthenticationError = "AUTHENTICATION_ERROR"; case InvalidRequestError = "INVALID_REQUEST_ERROR"; diff --git a/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Put/Types/ErrorCode.php b/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Put/Types/ErrorCode.php index 3219d4633600..3dbef3c49780 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Put/Types/ErrorCode.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Put/Types/ErrorCode.php @@ -2,8 +2,8 @@ namespace Seed\Endpoints\Put\Types; -enum ErrorCode - : string { +enum ErrorCode: string +{ case InternalServerError = "INTERNAL_SERVER_ERROR"; case Unauthorized = "UNAUTHORIZED"; case Forbidden = "FORBIDDEN"; diff --git a/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Put/Types/PutResponse.php b/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Put/Types/PutResponse.php index 6310c7abdaa4..ed9c5a781892 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Put/Types/PutResponse.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Put/Types/PutResponse.php @@ -21,15 +21,15 @@ class PutResponse extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->errors = $values['errors'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Union/UnionClient.php b/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Union/UnionClient.php index 72da2f5edbc0..aa4618c749bb 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Union/UnionClient.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Union/UnionClient.php @@ -13,7 +13,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class UnionClient +class UnionClient { /** * @var array{ @@ -41,11 +41,10 @@ class UnionClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -64,7 +63,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getAndReturnUnion(Animal $request, ?array $options = null): Animal { + public function getAndReturnUnion(Animal $request, ?array $options = null): Animal + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -77,15 +77,15 @@ public function getAndReturnUnion(Animal $request, ?array $options = null): Anim $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return Animal::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Urls/UrlsClient.php b/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Urls/UrlsClient.php index 0eb56899777c..c13a3e54cbfa 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Urls/UrlsClient.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Urls/UrlsClient.php @@ -13,7 +13,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class UrlsClient +class UrlsClient { /** * @var array{ @@ -41,11 +41,10 @@ class UrlsClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -63,7 +62,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function withMixedCase(?array $options = null): string { + public function withMixedCase(?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -75,15 +75,15 @@ public function withMixedCase(?array $options = null): string { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -114,7 +114,8 @@ public function withMixedCase(?array $options = null): string { * @throws SeedException * @throws SeedApiException */ - public function noEndingSlash(?array $options = null): string { + public function noEndingSlash(?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -126,15 +127,15 @@ public function noEndingSlash(?array $options = null): string { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -165,7 +166,8 @@ public function noEndingSlash(?array $options = null): string { * @throws SeedException * @throws SeedApiException */ - public function withEndingSlash(?array $options = null): string { + public function withEndingSlash(?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -177,15 +179,15 @@ public function withEndingSlash(?array $options = null): string { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -216,7 +218,8 @@ public function withEndingSlash(?array $options = null): string { * @throws SeedException * @throws SeedApiException */ - public function withUnderscores(?array $options = null): string { + public function withUnderscores(?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -228,15 +231,15 @@ public function withUnderscores(?array $options = null): string { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/exhaustive/wire-tests/src/Exceptions/SeedApiException.php b/seed/php-sdk/exhaustive/wire-tests/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/exhaustive/wire-tests/src/Exceptions/SeedException.php b/seed/php-sdk/exhaustive/wire-tests/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/Exceptions/SeedException.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/exhaustive/wire-tests/src/GeneralErrors/Types/BadObjectRequestInfo.php b/seed/php-sdk/exhaustive/wire-tests/src/GeneralErrors/Types/BadObjectRequestInfo.php index c60b09552de7..4373a7fac7eb 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/GeneralErrors/Types/BadObjectRequestInfo.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/GeneralErrors/Types/BadObjectRequestInfo.php @@ -20,15 +20,15 @@ class BadObjectRequestInfo extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->message = $values['message']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/exhaustive/wire-tests/src/InlinedRequests/InlinedRequestsClient.php b/seed/php-sdk/exhaustive/wire-tests/src/InlinedRequests/InlinedRequestsClient.php index e72c303cfde7..a61f90937a9e 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/InlinedRequests/InlinedRequestsClient.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/InlinedRequests/InlinedRequestsClient.php @@ -14,7 +14,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class InlinedRequestsClient +class InlinedRequestsClient { /** * @var array{ @@ -42,11 +42,10 @@ class InlinedRequestsClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -67,7 +66,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function postWithObjectBodyandResponse(PostWithObjectBody $request, ?array $options = null): ObjectWithOptionalField { + public function postWithObjectBodyandResponse(PostWithObjectBody $request, ?array $options = null): ObjectWithOptionalField + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -80,15 +80,15 @@ public function postWithObjectBodyandResponse(PostWithObjectBody $request, ?arra $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ObjectWithOptionalField::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/exhaustive/wire-tests/src/InlinedRequests/Requests/PostWithObjectBody.php b/seed/php-sdk/exhaustive/wire-tests/src/InlinedRequests/Requests/PostWithObjectBody.php index c7dc2ee69c0e..263418b8103c 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/InlinedRequests/Requests/PostWithObjectBody.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/InlinedRequests/Requests/PostWithObjectBody.php @@ -35,8 +35,9 @@ class PostWithObjectBody extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->string = $values['string'];$this->integer = $values['integer'];$this->nestedObject = $values['nestedObject']; + ) { + $this->string = $values['string']; + $this->integer = $values['integer']; + $this->nestedObject = $values['nestedObject']; } } diff --git a/seed/php-sdk/exhaustive/wire-tests/src/NoAuth/NoAuthClient.php b/seed/php-sdk/exhaustive/wire-tests/src/NoAuth/NoAuthClient.php index 3564574734a1..df2b2b788d62 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/NoAuth/NoAuthClient.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/NoAuth/NoAuthClient.php @@ -13,7 +13,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class NoAuthClient +class NoAuthClient { /** * @var array{ @@ -41,11 +41,10 @@ class NoAuthClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -66,7 +65,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function postWithNoAuth(mixed $request, ?array $options = null): bool { + public function postWithNoAuth(mixed $request, ?array $options = null): bool + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -79,15 +79,15 @@ public function postWithNoAuth(mixed $request, ?array $options = null): bool { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeBool($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/exhaustive/wire-tests/src/NoReqBody/NoReqBodyClient.php b/seed/php-sdk/exhaustive/wire-tests/src/NoReqBody/NoReqBodyClient.php index df02fc9bfb9b..9563f94ac678 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/NoReqBody/NoReqBodyClient.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/NoReqBody/NoReqBodyClient.php @@ -14,7 +14,7 @@ use Psr\Http\Client\ClientExceptionInterface; use Seed\Core\Json\JsonDecoder; -class NoReqBodyClient +class NoReqBodyClient { /** * @var array{ @@ -42,11 +42,10 @@ class NoReqBodyClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -64,7 +63,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getWithNoRequestBody(?array $options = null): ObjectWithOptionalField { + public function getWithNoRequestBody(?array $options = null): ObjectWithOptionalField + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -76,15 +76,15 @@ public function getWithNoRequestBody(?array $options = null): ObjectWithOptional $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ObjectWithOptionalField::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -115,7 +115,8 @@ public function getWithNoRequestBody(?array $options = null): ObjectWithOptional * @throws SeedException * @throws SeedApiException */ - public function postWithNoRequestBody(?array $options = null): string { + public function postWithNoRequestBody(?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -127,15 +128,15 @@ public function postWithNoRequestBody(?array $options = null): string { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/exhaustive/wire-tests/src/ReqWithHeaders/ReqWithHeadersClient.php b/seed/php-sdk/exhaustive/wire-tests/src/ReqWithHeaders/ReqWithHeadersClient.php index cd2e09a4c3fd..6be34fce7ec2 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/ReqWithHeaders/ReqWithHeadersClient.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/ReqWithHeaders/ReqWithHeadersClient.php @@ -12,7 +12,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class ReqWithHeadersClient +class ReqWithHeadersClient { /** * @var array{ @@ -40,11 +40,10 @@ class ReqWithHeadersClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -62,7 +61,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getWithCustomHeader(ReqWithHeaders $request, ?array $options = null): void { + public function getWithCustomHeader(ReqWithHeaders $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); $headers = []; $headers['X-TEST-SERVICE-HEADER'] = $request->xTestServiceHeader; @@ -79,12 +79,12 @@ public function getWithCustomHeader(ReqWithHeaders $request, ?array $options = n $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/exhaustive/wire-tests/src/ReqWithHeaders/Requests/ReqWithHeaders.php b/seed/php-sdk/exhaustive/wire-tests/src/ReqWithHeaders/Requests/ReqWithHeaders.php index a8d88e16fafd..c55d0c272eb7 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/ReqWithHeaders/Requests/ReqWithHeaders.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/ReqWithHeaders/Requests/ReqWithHeaders.php @@ -30,8 +30,9 @@ class ReqWithHeaders extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->xTestServiceHeader = $values['xTestServiceHeader'];$this->xTestEndpointHeader = $values['xTestEndpointHeader'];$this->body = $values['body']; + ) { + $this->xTestServiceHeader = $values['xTestServiceHeader']; + $this->xTestEndpointHeader = $values['xTestEndpointHeader']; + $this->body = $values['body']; } } diff --git a/seed/php-sdk/exhaustive/wire-tests/src/SeedClient.php b/seed/php-sdk/exhaustive/wire-tests/src/SeedClient.php index 36207d35c0b7..1e3444340c3c 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/SeedClient.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/SeedClient.php @@ -10,7 +10,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var EndpointsClient $endpoints @@ -66,29 +66,28 @@ class SeedClient public function __construct( ?string $token = null, ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - if ($token != null){ + if ($token != null) { $defaultHeaders['Authorization'] = "Bearer $token"; } - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->endpoints = new EndpointsClient($this->client, $this->options); $this->inlinedRequests = new InlinedRequestsClient($this->client, $this->options); $this->noAuth = new NoAuthClient($this->client, $this->options); diff --git a/seed/php-sdk/exhaustive/wire-tests/src/Types/Docs/Types/ObjectWithDocs.php b/seed/php-sdk/exhaustive/wire-tests/src/Types/Docs/Types/ObjectWithDocs.php index 1d4fe1f0e04b..db40e85452f9 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/Types/Docs/Types/ObjectWithDocs.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/Types/Docs/Types/ObjectWithDocs.php @@ -9,11 +9,11 @@ class ObjectWithDocs extends JsonSerializableType { /** * Characters that could lead to broken generated SDKs: - * + * * Markdown Escapes: * - \_: Escaped underscore (e.g., FOO\_BAR) * - \*: Escaped asterisk - * + * * JSDoc (JavaScript/TypeScript): * - @: Used for JSDoc tags * - {: }: Used for type definitions @@ -22,7 +22,7 @@ class ObjectWithDocs extends JsonSerializableType * - /**: JSDoc comment start * - ** /: JSDoc comment end * - &: HTML entities - * + * * XMLDoc (C#): * - <: >: XML tags * - &: ': ": <: >: XML special characters @@ -30,7 +30,7 @@ class ObjectWithDocs extends JsonSerializableType * - ///: Comment marker * - /**: Block comment start * - ** /: Block comment end - * + * * Javadoc (Java): * - @: Used for Javadoc tags * - <: >: HTML tags @@ -38,7 +38,7 @@ class ObjectWithDocs extends JsonSerializableType * - *: Can interfere with comment blocks * - /**: Javadoc comment start * - ** /: Javadoc comment end - * + * * Doxygen (C++): * - \: Used for Doxygen commands * - @: Alternative command prefix @@ -46,7 +46,7 @@ class ObjectWithDocs extends JsonSerializableType * - &: HTML entities * - /**: C-style comment start * - ** /: C-style comment end - * + * * RDoc (Ruby): * - :: Used in symbol notation * - =: Section markers @@ -58,7 +58,7 @@ class ObjectWithDocs extends JsonSerializableType * - %: String literal delimiter * - #{: String interpolation start * - }: String interpolation end - * + * * PHPDoc (PHP): * - @: Used for PHPDoc tags * - {: }: Used for type definitions @@ -80,15 +80,15 @@ class ObjectWithDocs extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->string = $values['string']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/exhaustive/wire-tests/src/Types/Enum/Types/WeatherReport.php b/seed/php-sdk/exhaustive/wire-tests/src/Types/Enum/Types/WeatherReport.php index a8246d8ef1d7..a363d5e7f22b 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/Types/Enum/Types/WeatherReport.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/Types/Enum/Types/WeatherReport.php @@ -2,8 +2,8 @@ namespace Seed\Types\Enum\Types; -enum WeatherReport - : string { +enum WeatherReport: string +{ case Sunny = "SUNNY"; case Cloudy = "CLOUDY"; case Raining = "RAINING"; diff --git a/seed/php-sdk/exhaustive/wire-tests/src/Types/Object/Types/DoubleOptional.php b/seed/php-sdk/exhaustive/wire-tests/src/Types/Object/Types/DoubleOptional.php index 6c0aa495d527..87087ca7c274 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/Types/Object/Types/DoubleOptional.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/Types/Object/Types/DoubleOptional.php @@ -20,15 +20,15 @@ class DoubleOptional extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->optionalAlias = $values['optionalAlias'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/exhaustive/wire-tests/src/Types/Object/Types/NestedObjectWithOptionalField.php b/seed/php-sdk/exhaustive/wire-tests/src/Types/Object/Types/NestedObjectWithOptionalField.php index 56c784e8f949..ed0fca4f6e14 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/Types/Object/Types/NestedObjectWithOptionalField.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/Types/Object/Types/NestedObjectWithOptionalField.php @@ -27,15 +27,16 @@ class NestedObjectWithOptionalField extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->string = $values['string'] ?? null;$this->nestedObject = $values['nestedObject'] ?? null; + ) { + $this->string = $values['string'] ?? null; + $this->nestedObject = $values['nestedObject'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/exhaustive/wire-tests/src/Types/Object/Types/NestedObjectWithRequiredField.php b/seed/php-sdk/exhaustive/wire-tests/src/Types/Object/Types/NestedObjectWithRequiredField.php index 15d9bb7bea6c..e4fc08d3520f 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/Types/Object/Types/NestedObjectWithRequiredField.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/Types/Object/Types/NestedObjectWithRequiredField.php @@ -27,15 +27,16 @@ class NestedObjectWithRequiredField extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->string = $values['string'];$this->nestedObject = $values['nestedObject']; + ) { + $this->string = $values['string']; + $this->nestedObject = $values['nestedObject']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/exhaustive/wire-tests/src/Types/Object/Types/ObjectWithMapOfMap.php b/seed/php-sdk/exhaustive/wire-tests/src/Types/Object/Types/ObjectWithMapOfMap.php index 87497c99940a..20976a704c8e 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/Types/Object/Types/ObjectWithMapOfMap.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/Types/Object/Types/ObjectWithMapOfMap.php @@ -21,15 +21,15 @@ class ObjectWithMapOfMap extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->map = $values['map']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/exhaustive/wire-tests/src/Types/Object/Types/ObjectWithOptionalField.php b/seed/php-sdk/exhaustive/wire-tests/src/Types/Object/Types/ObjectWithOptionalField.php index a79d31e065e3..af6fe1b400c6 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/Types/Object/Types/ObjectWithOptionalField.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/Types/Object/Types/ObjectWithOptionalField.php @@ -107,15 +107,27 @@ class ObjectWithOptionalField extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->string = $values['string'] ?? null;$this->integer = $values['integer'] ?? null;$this->long = $values['long'] ?? null;$this->double = $values['double'] ?? null;$this->bool = $values['bool'] ?? null;$this->datetime = $values['datetime'] ?? null;$this->date = $values['date'] ?? null;$this->uuid = $values['uuid'] ?? null;$this->base64 = $values['base64'] ?? null;$this->list = $values['list'] ?? null;$this->set = $values['set'] ?? null;$this->map = $values['map'] ?? null;$this->bigint = $values['bigint'] ?? null; + ) { + $this->string = $values['string'] ?? null; + $this->integer = $values['integer'] ?? null; + $this->long = $values['long'] ?? null; + $this->double = $values['double'] ?? null; + $this->bool = $values['bool'] ?? null; + $this->datetime = $values['datetime'] ?? null; + $this->date = $values['date'] ?? null; + $this->uuid = $values['uuid'] ?? null; + $this->base64 = $values['base64'] ?? null; + $this->list = $values['list'] ?? null; + $this->set = $values['set'] ?? null; + $this->map = $values['map'] ?? null; + $this->bigint = $values['bigint'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/exhaustive/wire-tests/src/Types/Object/Types/ObjectWithRequiredField.php b/seed/php-sdk/exhaustive/wire-tests/src/Types/Object/Types/ObjectWithRequiredField.php index d17970deb278..22fe0c58df60 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/Types/Object/Types/ObjectWithRequiredField.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/Types/Object/Types/ObjectWithRequiredField.php @@ -20,15 +20,15 @@ class ObjectWithRequiredField extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->string = $values['string']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/exhaustive/wire-tests/src/Types/Union/Types/Animal.php b/seed/php-sdk/exhaustive/wire-tests/src/Types/Union/Types/Animal.php index 40be624b51da..8138ea53a41e 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/Types/Union/Types/Animal.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/Types/Union/Types/Animal.php @@ -42,16 +42,17 @@ class Animal extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->animal = $values['animal'];$this->value = $values['value']; + ) { + $this->animal = $values['animal']; + $this->value = $values['value']; } /** * @param Dog $dog * @return Animal */ - public static function dog(Dog $dog): Animal { + public static function dog(Dog $dog): Animal + { return new Animal([ 'animal' => 'dog', 'value' => $dog, @@ -62,7 +63,8 @@ public static function dog(Dog $dog): Animal { * @param Cat $cat * @return Animal */ - public static function cat(Cat $cat): Animal { + public static function cat(Cat $cat): Animal + { return new Animal([ 'animal' => 'cat', 'value' => $cat, @@ -72,61 +74,67 @@ public static function cat(Cat $cat): Animal { /** * @return bool */ - public function isDog(): bool { - return $this->value instanceof Dog&& $this->animal === 'dog'; + public function isDog(): bool + { + return $this->value instanceof Dog && $this->animal === 'dog'; } /** * @return Dog */ - public function asDog(): Dog { - if (!($this->value instanceof Dog&& $this->animal === 'dog')){ + public function asDog(): Dog + { + if (!($this->value instanceof Dog && $this->animal === 'dog')) { throw new Exception( "Expected dog; got " . $this->animal . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isCat(): bool { - return $this->value instanceof Cat&& $this->animal === 'cat'; + public function isCat(): bool + { + return $this->value instanceof Cat && $this->animal === 'cat'; } /** * @return Cat */ - public function asCat(): Cat { - if (!($this->value instanceof Cat&& $this->animal === 'cat')){ + public function asCat(): Cat + { + if (!($this->value instanceof Cat && $this->animal === 'cat')) { throw new Exception( "Expected cat; got " . $this->animal . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['animal'] = $this->animal; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->animal){ + + switch ($this->animal) { case 'dog': $value = $this->asDog()->jsonSerialize(); $result = array_merge($value, $result); @@ -137,26 +145,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -165,22 +174,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('animal', $data)){ + if (!array_key_exists('animal', $data)) { throw new Exception( "JSON data is missing property 'animal'", ); } $animal = $data['animal']; - if (!(is_string($animal))){ + if (!(is_string($animal))) { throw new Exception( "Expected property 'animal' in JSON data to be string, instead received " . get_debug_type($data['animal']), ); } - + $args['animal'] = $animal; - switch ($animal){ + switch ($animal) { case 'dog': $args['value'] = Dog::jsonDeserialize($data); break; @@ -192,7 +202,7 @@ public static function jsonDeserialize(array $data): static { $args['animal'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/exhaustive/wire-tests/src/Types/Union/Types/Cat.php b/seed/php-sdk/exhaustive/wire-tests/src/Types/Union/Types/Cat.php index 328ffbeae41b..cd2369d2fee5 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/Types/Union/Types/Cat.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/Types/Union/Types/Cat.php @@ -27,15 +27,16 @@ class Cat extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->likesToMeow = $values['likesToMeow']; + ) { + $this->name = $values['name']; + $this->likesToMeow = $values['likesToMeow']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/exhaustive/wire-tests/src/Types/Union/Types/Dog.php b/seed/php-sdk/exhaustive/wire-tests/src/Types/Union/Types/Dog.php index a09bdf94cca2..dad76628a799 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/Types/Union/Types/Dog.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/Types/Union/Types/Dog.php @@ -27,15 +27,16 @@ class Dog extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->likesToWoof = $values['likesToWoof']; + ) { + $this->name = $values['name']; + $this->likesToWoof = $values['likesToWoof']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/exhaustive/wire-tests/src/Utils/File.php b/seed/php-sdk/exhaustive/wire-tests/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/Utils/File.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/exhaustive/wire-tests/tests/Core/Client/RawClientTest.php b/seed/php-sdk/exhaustive/wire-tests/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/exhaustive/wire-tests/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/exhaustive/wire-tests/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/exhaustive/wire-tests/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/exhaustive/wire-tests/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/exhaustive/wire-tests/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/exhaustive/wire-tests/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/exhaustive/wire-tests/tests/Core/Json/EnumTest.php b/seed/php-sdk/exhaustive/wire-tests/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/exhaustive/wire-tests/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/exhaustive/wire-tests/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/exhaustive/wire-tests/tests/Core/Json/TraitTest.php b/seed/php-sdk/exhaustive/wire-tests/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/exhaustive/wire-tests/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/exhaustive/wire-tests/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/exhaustive/wire-tests/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/exhaustive/wire-tests/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/exhaustive/wire-tests/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/exhaustive/wire-tests/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/extends/no-custom-config/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/extends/no-custom-config/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/extends/no-custom-config/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/extends/no-custom-config/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/extends/no-custom-config/src/Core/Client/RawClient.php b/seed/php-sdk/extends/no-custom-config/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/extends/no-custom-config/src/Core/Client/RawClient.php +++ b/seed/php-sdk/extends/no-custom-config/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/extends/no-custom-config/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/extends/no-custom-config/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/extends/no-custom-config/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/extends/no-custom-config/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/extends/no-custom-config/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/extends/no-custom-config/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/extends/no-custom-config/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/extends/no-custom-config/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/extends/no-custom-config/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/extends/no-custom-config/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/extends/no-custom-config/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/extends/no-custom-config/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/extends/no-custom-config/src/Core/Types/ArrayType.php b/seed/php-sdk/extends/no-custom-config/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/extends/no-custom-config/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/extends/no-custom-config/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/extends/no-custom-config/src/Core/Types/Constant.php b/seed/php-sdk/extends/no-custom-config/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/extends/no-custom-config/src/Core/Types/Constant.php +++ b/seed/php-sdk/extends/no-custom-config/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/extends/no-custom-config/src/Core/Types/Union.php b/seed/php-sdk/extends/no-custom-config/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/extends/no-custom-config/src/Core/Types/Union.php +++ b/seed/php-sdk/extends/no-custom-config/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/extends/no-custom-config/src/Exceptions/SeedApiException.php b/seed/php-sdk/extends/no-custom-config/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/extends/no-custom-config/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/extends/no-custom-config/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/extends/no-custom-config/src/Exceptions/SeedException.php b/seed/php-sdk/extends/no-custom-config/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/extends/no-custom-config/src/Exceptions/SeedException.php +++ b/seed/php-sdk/extends/no-custom-config/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/extends/no-custom-config/src/Requests/Inlined.php b/seed/php-sdk/extends/no-custom-config/src/Requests/Inlined.php index 080cc0c3d290..d47180ba84b4 100644 --- a/seed/php-sdk/extends/no-custom-config/src/Requests/Inlined.php +++ b/seed/php-sdk/extends/no-custom-config/src/Requests/Inlined.php @@ -25,8 +25,9 @@ class Inlined extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->unique = $values['unique'];$this->name = $values['name'];$this->docs = $values['docs']; + ) { + $this->unique = $values['unique']; + $this->name = $values['name']; + $this->docs = $values['docs']; } } diff --git a/seed/php-sdk/extends/no-custom-config/src/SeedClient.php b/seed/php-sdk/extends/no-custom-config/src/SeedClient.php index 58e0c0bdfe3e..a6048209cf50 100644 --- a/seed/php-sdk/extends/no-custom-config/src/SeedClient.php +++ b/seed/php-sdk/extends/no-custom-config/src/SeedClient.php @@ -12,7 +12,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class SeedClient +class SeedClient { /** * @var array{ @@ -41,22 +41,21 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); @@ -75,7 +74,8 @@ public function __construct( * @throws SeedException * @throws SeedApiException */ - public function extendedInlineRequestBody(Inlined $request, ?array $options = null): void { + public function extendedInlineRequestBody(Inlined $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -88,12 +88,12 @@ public function extendedInlineRequestBody(Inlined $request, ?array $options = nu $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/extends/no-custom-config/src/Traits/Docs.php b/seed/php-sdk/extends/no-custom-config/src/Traits/Docs.php index 31549c3f7ad9..6e49e98814be 100644 --- a/seed/php-sdk/extends/no-custom-config/src/Traits/Docs.php +++ b/seed/php-sdk/extends/no-custom-config/src/Traits/Docs.php @@ -7,7 +7,7 @@ /** * @property string $docs */ -trait Docs +trait Docs { /** * @var string $docs diff --git a/seed/php-sdk/extends/no-custom-config/src/Traits/ExampleType.php b/seed/php-sdk/extends/no-custom-config/src/Traits/ExampleType.php index bbccad420453..503829fdc3be 100644 --- a/seed/php-sdk/extends/no-custom-config/src/Traits/ExampleType.php +++ b/seed/php-sdk/extends/no-custom-config/src/Traits/ExampleType.php @@ -7,7 +7,7 @@ /** * @property string $name */ -trait ExampleType +trait ExampleType { use Docs; diff --git a/seed/php-sdk/extends/no-custom-config/src/Traits/Json.php b/seed/php-sdk/extends/no-custom-config/src/Traits/Json.php index 5c287d4cff59..4380468c2af0 100644 --- a/seed/php-sdk/extends/no-custom-config/src/Traits/Json.php +++ b/seed/php-sdk/extends/no-custom-config/src/Traits/Json.php @@ -7,7 +7,7 @@ /** * @property string $raw */ -trait Json +trait Json { use Docs; diff --git a/seed/php-sdk/extends/no-custom-config/src/Types/Docs.php b/seed/php-sdk/extends/no-custom-config/src/Types/Docs.php index 9bc1896c53c1..bd67650a8fdc 100644 --- a/seed/php-sdk/extends/no-custom-config/src/Types/Docs.php +++ b/seed/php-sdk/extends/no-custom-config/src/Types/Docs.php @@ -20,15 +20,15 @@ class Docs extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->docs = $values['docs']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/extends/no-custom-config/src/Types/ExampleType.php b/seed/php-sdk/extends/no-custom-config/src/Types/ExampleType.php index 55793792bd8b..3fd1c875a36c 100644 --- a/seed/php-sdk/extends/no-custom-config/src/Types/ExampleType.php +++ b/seed/php-sdk/extends/no-custom-config/src/Types/ExampleType.php @@ -24,15 +24,16 @@ class ExampleType extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->docs = $values['docs'];$this->name = $values['name']; + ) { + $this->docs = $values['docs']; + $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/extends/no-custom-config/src/Types/Json.php b/seed/php-sdk/extends/no-custom-config/src/Types/Json.php index 3c99b3483864..3f6be188101d 100644 --- a/seed/php-sdk/extends/no-custom-config/src/Types/Json.php +++ b/seed/php-sdk/extends/no-custom-config/src/Types/Json.php @@ -24,15 +24,16 @@ class Json extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->docs = $values['docs'];$this->raw = $values['raw']; + ) { + $this->docs = $values['docs']; + $this->raw = $values['raw']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/extends/no-custom-config/src/Types/NestedType.php b/seed/php-sdk/extends/no-custom-config/src/Types/NestedType.php index ed89b4247b2a..63b7b9844cd5 100644 --- a/seed/php-sdk/extends/no-custom-config/src/Types/NestedType.php +++ b/seed/php-sdk/extends/no-custom-config/src/Types/NestedType.php @@ -25,15 +25,17 @@ class NestedType extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->raw = $values['raw'];$this->docs = $values['docs'];$this->name = $values['name']; + ) { + $this->raw = $values['raw']; + $this->docs = $values['docs']; + $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/extends/no-custom-config/src/Utils/File.php b/seed/php-sdk/extends/no-custom-config/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/extends/no-custom-config/src/Utils/File.php +++ b/seed/php-sdk/extends/no-custom-config/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/extends/no-custom-config/tests/Core/Client/RawClientTest.php b/seed/php-sdk/extends/no-custom-config/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/extends/no-custom-config/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/extends/no-custom-config/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/extends/no-custom-config/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/extends/no-custom-config/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/extends/no-custom-config/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/extends/no-custom-config/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/extends/no-custom-config/tests/Core/Json/EnumTest.php b/seed/php-sdk/extends/no-custom-config/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/extends/no-custom-config/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/extends/no-custom-config/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/extends/no-custom-config/tests/Core/Json/TraitTest.php b/seed/php-sdk/extends/no-custom-config/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/extends/no-custom-config/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/extends/no-custom-config/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/extends/no-custom-config/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/extends/no-custom-config/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/extends/no-custom-config/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/extends/no-custom-config/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/extends/private/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/extends/private/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/extends/private/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/extends/private/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/extends/private/src/Core/Client/RawClient.php b/seed/php-sdk/extends/private/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/extends/private/src/Core/Client/RawClient.php +++ b/seed/php-sdk/extends/private/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/extends/private/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/extends/private/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/extends/private/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/extends/private/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/extends/private/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/extends/private/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/extends/private/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/extends/private/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/extends/private/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/extends/private/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/extends/private/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/extends/private/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/extends/private/src/Core/Types/ArrayType.php b/seed/php-sdk/extends/private/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/extends/private/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/extends/private/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/extends/private/src/Core/Types/Constant.php b/seed/php-sdk/extends/private/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/extends/private/src/Core/Types/Constant.php +++ b/seed/php-sdk/extends/private/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/extends/private/src/Core/Types/Union.php b/seed/php-sdk/extends/private/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/extends/private/src/Core/Types/Union.php +++ b/seed/php-sdk/extends/private/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/extends/private/src/Exceptions/SeedApiException.php b/seed/php-sdk/extends/private/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/extends/private/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/extends/private/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/extends/private/src/Exceptions/SeedException.php b/seed/php-sdk/extends/private/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/extends/private/src/Exceptions/SeedException.php +++ b/seed/php-sdk/extends/private/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/extends/private/src/Requests/Inlined.php b/seed/php-sdk/extends/private/src/Requests/Inlined.php index 911db4f4a44d..cce832323f75 100644 --- a/seed/php-sdk/extends/private/src/Requests/Inlined.php +++ b/seed/php-sdk/extends/private/src/Requests/Inlined.php @@ -25,20 +25,26 @@ class Inlined extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->unique = $values['unique'];$this->name = $values['name'];$this->docs = $values['docs']; + ) { + $this->unique = $values['unique']; + $this->name = $values['name']; + $this->docs = $values['docs']; } /** * @return string */ - public function getUnique(): string { - return $this->unique;} + public function getUnique(): string + { + return $this->unique; + } /** * @param string $value */ - public function setUnique(string $value): self { - $this->unique = $value;return $this;} + public function setUnique(string $value): self + { + $this->unique = $value; + return $this; + } } diff --git a/seed/php-sdk/extends/private/src/SeedClient.php b/seed/php-sdk/extends/private/src/SeedClient.php index 58e0c0bdfe3e..a6048209cf50 100644 --- a/seed/php-sdk/extends/private/src/SeedClient.php +++ b/seed/php-sdk/extends/private/src/SeedClient.php @@ -12,7 +12,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class SeedClient +class SeedClient { /** * @var array{ @@ -41,22 +41,21 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); @@ -75,7 +74,8 @@ public function __construct( * @throws SeedException * @throws SeedApiException */ - public function extendedInlineRequestBody(Inlined $request, ?array $options = null): void { + public function extendedInlineRequestBody(Inlined $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -88,12 +88,12 @@ public function extendedInlineRequestBody(Inlined $request, ?array $options = nu $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/extends/private/src/Traits/Docs.php b/seed/php-sdk/extends/private/src/Traits/Docs.php index b28e3f89815b..e43f78a0d011 100644 --- a/seed/php-sdk/extends/private/src/Traits/Docs.php +++ b/seed/php-sdk/extends/private/src/Traits/Docs.php @@ -7,7 +7,7 @@ /** * @property string $docs */ -trait Docs +trait Docs { /** * @var string $docs @@ -18,12 +18,17 @@ trait Docs /** * @return string */ - public function getDocs(): string { - return $this->docs;} + public function getDocs(): string + { + return $this->docs; + } /** * @param string $value */ - public function setDocs(string $value): self { - $this->docs = $value;return $this;} + public function setDocs(string $value): self + { + $this->docs = $value; + return $this; + } } diff --git a/seed/php-sdk/extends/private/src/Traits/ExampleType.php b/seed/php-sdk/extends/private/src/Traits/ExampleType.php index 0a3d6e4308c2..ccfecadb3c8b 100644 --- a/seed/php-sdk/extends/private/src/Traits/ExampleType.php +++ b/seed/php-sdk/extends/private/src/Traits/ExampleType.php @@ -7,7 +7,7 @@ /** * @property string $name */ -trait ExampleType +trait ExampleType { use Docs; @@ -20,12 +20,17 @@ trait ExampleType /** * @return string */ - public function getName(): string { - return $this->name;} + public function getName(): string + { + return $this->name; + } /** * @param string $value */ - public function setName(string $value): self { - $this->name = $value;return $this;} + public function setName(string $value): self + { + $this->name = $value; + return $this; + } } diff --git a/seed/php-sdk/extends/private/src/Traits/Json.php b/seed/php-sdk/extends/private/src/Traits/Json.php index 8c3f492724ce..05d9af0ee97a 100644 --- a/seed/php-sdk/extends/private/src/Traits/Json.php +++ b/seed/php-sdk/extends/private/src/Traits/Json.php @@ -7,7 +7,7 @@ /** * @property string $raw */ -trait Json +trait Json { use Docs; @@ -20,12 +20,17 @@ trait Json /** * @return string */ - public function getRaw(): string { - return $this->raw;} + public function getRaw(): string + { + return $this->raw; + } /** * @param string $value */ - public function setRaw(string $value): self { - $this->raw = $value;return $this;} + public function setRaw(string $value): self + { + $this->raw = $value; + return $this; + } } diff --git a/seed/php-sdk/extends/private/src/Types/Docs.php b/seed/php-sdk/extends/private/src/Types/Docs.php index 62168987c4a7..049289eceba1 100644 --- a/seed/php-sdk/extends/private/src/Types/Docs.php +++ b/seed/php-sdk/extends/private/src/Types/Docs.php @@ -20,27 +20,32 @@ class Docs extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->docs = $values['docs']; } /** * @return string */ - public function getDocs(): string { - return $this->docs;} + public function getDocs(): string + { + return $this->docs; + } /** * @param string $value */ - public function setDocs(string $value): self { - $this->docs = $value;return $this;} + public function setDocs(string $value): self + { + $this->docs = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/extends/private/src/Types/ExampleType.php b/seed/php-sdk/extends/private/src/Types/ExampleType.php index dd32f8eb7a30..0e3c2c4c353d 100644 --- a/seed/php-sdk/extends/private/src/Types/ExampleType.php +++ b/seed/php-sdk/extends/private/src/Types/ExampleType.php @@ -24,27 +24,33 @@ class ExampleType extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->docs = $values['docs'];$this->name = $values['name']; + ) { + $this->docs = $values['docs']; + $this->name = $values['name']; } /** * @return string */ - public function getName(): string { - return $this->name;} + public function getName(): string + { + return $this->name; + } /** * @param string $value */ - public function setName(string $value): self { - $this->name = $value;return $this;} + public function setName(string $value): self + { + $this->name = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/extends/private/src/Types/Json.php b/seed/php-sdk/extends/private/src/Types/Json.php index f9744d5b3468..933de902871a 100644 --- a/seed/php-sdk/extends/private/src/Types/Json.php +++ b/seed/php-sdk/extends/private/src/Types/Json.php @@ -24,27 +24,33 @@ class Json extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->docs = $values['docs'];$this->raw = $values['raw']; + ) { + $this->docs = $values['docs']; + $this->raw = $values['raw']; } /** * @return string */ - public function getRaw(): string { - return $this->raw;} + public function getRaw(): string + { + return $this->raw; + } /** * @param string $value */ - public function setRaw(string $value): self { - $this->raw = $value;return $this;} + public function setRaw(string $value): self + { + $this->raw = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/extends/private/src/Types/NestedType.php b/seed/php-sdk/extends/private/src/Types/NestedType.php index b6ea36f15093..911059f218ab 100644 --- a/seed/php-sdk/extends/private/src/Types/NestedType.php +++ b/seed/php-sdk/extends/private/src/Types/NestedType.php @@ -25,27 +25,34 @@ class NestedType extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->raw = $values['raw'];$this->docs = $values['docs'];$this->name = $values['name']; + ) { + $this->raw = $values['raw']; + $this->docs = $values['docs']; + $this->name = $values['name']; } /** * @return string */ - public function getName(): string { - return $this->name;} + public function getName(): string + { + return $this->name; + } /** * @param string $value */ - public function setName(string $value): self { - $this->name = $value;return $this;} + public function setName(string $value): self + { + $this->name = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/extends/private/src/Utils/File.php b/seed/php-sdk/extends/private/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/extends/private/src/Utils/File.php +++ b/seed/php-sdk/extends/private/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/extends/private/tests/Core/Client/RawClientTest.php b/seed/php-sdk/extends/private/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/extends/private/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/extends/private/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/extends/private/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/extends/private/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/extends/private/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/extends/private/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/extends/private/tests/Core/Json/EnumTest.php b/seed/php-sdk/extends/private/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/extends/private/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/extends/private/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/extends/private/tests/Core/Json/TraitTest.php b/seed/php-sdk/extends/private/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/extends/private/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/extends/private/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/extends/private/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/extends/private/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/extends/private/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/extends/private/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/extra-properties/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/extra-properties/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/extra-properties/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/extra-properties/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/extra-properties/src/Core/Client/RawClient.php b/seed/php-sdk/extra-properties/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/extra-properties/src/Core/Client/RawClient.php +++ b/seed/php-sdk/extra-properties/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/extra-properties/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/extra-properties/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/extra-properties/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/extra-properties/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/extra-properties/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/extra-properties/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/extra-properties/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/extra-properties/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/extra-properties/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/extra-properties/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/extra-properties/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/extra-properties/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/extra-properties/src/Core/Types/ArrayType.php b/seed/php-sdk/extra-properties/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/extra-properties/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/extra-properties/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/extra-properties/src/Core/Types/Constant.php b/seed/php-sdk/extra-properties/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/extra-properties/src/Core/Types/Constant.php +++ b/seed/php-sdk/extra-properties/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/extra-properties/src/Core/Types/Union.php b/seed/php-sdk/extra-properties/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/extra-properties/src/Core/Types/Union.php +++ b/seed/php-sdk/extra-properties/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/extra-properties/src/Exceptions/SeedApiException.php b/seed/php-sdk/extra-properties/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/extra-properties/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/extra-properties/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/extra-properties/src/Exceptions/SeedException.php b/seed/php-sdk/extra-properties/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/extra-properties/src/Exceptions/SeedException.php +++ b/seed/php-sdk/extra-properties/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/extra-properties/src/SeedClient.php b/seed/php-sdk/extra-properties/src/SeedClient.php index a13bf5acc053..ee87d1cb303f 100644 --- a/seed/php-sdk/extra-properties/src/SeedClient.php +++ b/seed/php-sdk/extra-properties/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var UserClient $user @@ -40,26 +40,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->user = new UserClient($this->client, $this->options); } } diff --git a/seed/php-sdk/extra-properties/src/Types/Failure.php b/seed/php-sdk/extra-properties/src/Types/Failure.php index 62a7d45bc0e1..294ebed0f019 100644 --- a/seed/php-sdk/extra-properties/src/Types/Failure.php +++ b/seed/php-sdk/extra-properties/src/Types/Failure.php @@ -20,15 +20,15 @@ class Failure extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->status = $values['status']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/extra-properties/src/User/Requests/CreateUserRequest.php b/seed/php-sdk/extra-properties/src/User/Requests/CreateUserRequest.php index e4ff7eb4ee8b..333ab9de9e63 100644 --- a/seed/php-sdk/extra-properties/src/User/Requests/CreateUserRequest.php +++ b/seed/php-sdk/extra-properties/src/User/Requests/CreateUserRequest.php @@ -34,8 +34,9 @@ class CreateUserRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->type = $values['type'];$this->version = $values['version'];$this->name = $values['name']; + ) { + $this->type = $values['type']; + $this->version = $values['version']; + $this->name = $values['name']; } } diff --git a/seed/php-sdk/extra-properties/src/User/Types/User.php b/seed/php-sdk/extra-properties/src/User/Types/User.php index ec0a6fe77413..4b687798977d 100644 --- a/seed/php-sdk/extra-properties/src/User/Types/User.php +++ b/seed/php-sdk/extra-properties/src/User/Types/User.php @@ -20,15 +20,15 @@ class User extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/extra-properties/src/User/UserClient.php b/seed/php-sdk/extra-properties/src/User/UserClient.php index a223a59daace..b8b0858c24b7 100644 --- a/seed/php-sdk/extra-properties/src/User/UserClient.php +++ b/seed/php-sdk/extra-properties/src/User/UserClient.php @@ -14,7 +14,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class UserClient +class UserClient { /** * @var array{ @@ -42,11 +42,10 @@ class UserClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -65,7 +64,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function createUser(CreateUserRequest $request, ?array $options = null): User { + public function createUser(CreateUserRequest $request, ?array $options = null): User + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -78,15 +78,15 @@ public function createUser(CreateUserRequest $request, ?array $options = null): $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return User::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/extra-properties/src/Utils/File.php b/seed/php-sdk/extra-properties/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/extra-properties/src/Utils/File.php +++ b/seed/php-sdk/extra-properties/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/extra-properties/tests/Core/Client/RawClientTest.php b/seed/php-sdk/extra-properties/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/extra-properties/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/extra-properties/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/extra-properties/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/extra-properties/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/extra-properties/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/extra-properties/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/extra-properties/tests/Core/Json/EnumTest.php b/seed/php-sdk/extra-properties/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/extra-properties/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/extra-properties/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/extra-properties/tests/Core/Json/TraitTest.php b/seed/php-sdk/extra-properties/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/extra-properties/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/extra-properties/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/extra-properties/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/extra-properties/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/extra-properties/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/extra-properties/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/file-download/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/file-download/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/file-download/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/file-download/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/file-download/src/Core/Client/RawClient.php b/seed/php-sdk/file-download/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/file-download/src/Core/Client/RawClient.php +++ b/seed/php-sdk/file-download/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/file-download/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/file-download/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/file-download/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/file-download/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/file-download/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/file-download/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/file-download/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/file-download/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/file-download/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/file-download/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/file-download/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/file-download/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/file-download/src/Core/Types/ArrayType.php b/seed/php-sdk/file-download/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/file-download/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/file-download/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/file-download/src/Core/Types/Constant.php b/seed/php-sdk/file-download/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/file-download/src/Core/Types/Constant.php +++ b/seed/php-sdk/file-download/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/file-download/src/Core/Types/Union.php b/seed/php-sdk/file-download/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/file-download/src/Core/Types/Union.php +++ b/seed/php-sdk/file-download/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/file-download/src/Exceptions/SeedApiException.php b/seed/php-sdk/file-download/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/file-download/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/file-download/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/file-download/src/Exceptions/SeedException.php b/seed/php-sdk/file-download/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/file-download/src/Exceptions/SeedException.php +++ b/seed/php-sdk/file-download/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/file-download/src/SeedClient.php b/seed/php-sdk/file-download/src/SeedClient.php index 8d7adcb3b0f2..0fd6a9d1029e 100644 --- a/seed/php-sdk/file-download/src/SeedClient.php +++ b/seed/php-sdk/file-download/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var ServiceClient $service @@ -40,26 +40,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->service = new ServiceClient($this->client, $this->options); } } diff --git a/seed/php-sdk/file-download/src/Service/ServiceClient.php b/seed/php-sdk/file-download/src/Service/ServiceClient.php index e2851aeabc9a..df29795f0358 100644 --- a/seed/php-sdk/file-download/src/Service/ServiceClient.php +++ b/seed/php-sdk/file-download/src/Service/ServiceClient.php @@ -11,7 +11,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class ServiceClient +class ServiceClient { /** * @var array{ @@ -39,11 +39,10 @@ class ServiceClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -60,7 +59,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function simple(?array $options = null): void { + public function simple(?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -72,12 +72,12 @@ public function simple(?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -107,7 +107,8 @@ public function simple(?array $options = null): void { * @throws SeedException * @throws SeedApiException */ - public function downloadFile(?array $options = null): void { + public function downloadFile(?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -121,7 +122,7 @@ public function downloadFile(?array $options = null): void { $statusCode = $response->getStatusCode(); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/file-download/src/Utils/File.php b/seed/php-sdk/file-download/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/file-download/src/Utils/File.php +++ b/seed/php-sdk/file-download/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/file-download/tests/Core/Client/RawClientTest.php b/seed/php-sdk/file-download/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/file-download/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/file-download/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/file-download/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/file-download/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/file-download/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/file-download/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/file-download/tests/Core/Json/EnumTest.php b/seed/php-sdk/file-download/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/file-download/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/file-download/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/file-download/tests/Core/Json/TraitTest.php b/seed/php-sdk/file-download/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/file-download/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/file-download/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/file-download/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/file-download/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/file-download/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/file-download/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/file-upload-openapi/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/file-upload-openapi/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/file-upload-openapi/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/file-upload-openapi/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/file-upload-openapi/src/Core/Client/RawClient.php b/seed/php-sdk/file-upload-openapi/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/file-upload-openapi/src/Core/Client/RawClient.php +++ b/seed/php-sdk/file-upload-openapi/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/file-upload-openapi/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/file-upload-openapi/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/file-upload-openapi/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/file-upload-openapi/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/file-upload-openapi/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/file-upload-openapi/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/file-upload-openapi/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/file-upload-openapi/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/file-upload-openapi/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/file-upload-openapi/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/file-upload-openapi/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/file-upload-openapi/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/file-upload-openapi/src/Core/Types/ArrayType.php b/seed/php-sdk/file-upload-openapi/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/file-upload-openapi/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/file-upload-openapi/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/file-upload-openapi/src/Core/Types/Constant.php b/seed/php-sdk/file-upload-openapi/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/file-upload-openapi/src/Core/Types/Constant.php +++ b/seed/php-sdk/file-upload-openapi/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/file-upload-openapi/src/Core/Types/Union.php b/seed/php-sdk/file-upload-openapi/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/file-upload-openapi/src/Core/Types/Union.php +++ b/seed/php-sdk/file-upload-openapi/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/file-upload-openapi/src/Exceptions/SeedApiException.php b/seed/php-sdk/file-upload-openapi/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/file-upload-openapi/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/file-upload-openapi/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/file-upload-openapi/src/Exceptions/SeedException.php b/seed/php-sdk/file-upload-openapi/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/file-upload-openapi/src/Exceptions/SeedException.php +++ b/seed/php-sdk/file-upload-openapi/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/file-upload-openapi/src/FileUploadExample/FileUploadExampleClient.php b/seed/php-sdk/file-upload-openapi/src/FileUploadExample/FileUploadExampleClient.php index da8cf81ab44b..776bd8c11f94 100644 --- a/seed/php-sdk/file-upload-openapi/src/FileUploadExample/FileUploadExampleClient.php +++ b/seed/php-sdk/file-upload-openapi/src/FileUploadExample/FileUploadExampleClient.php @@ -15,7 +15,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class FileUploadExampleClient +class FileUploadExampleClient { /** * @var array{ @@ -43,11 +43,10 @@ class FileUploadExampleClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -67,11 +66,12 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function uploadFile(UploadFileRequest $request, ?array $options = null): string { + public function uploadFile(UploadFileRequest $request, ?array $options = null): string + { $options = array_merge($this->options, $options ?? []); $body = new MultipartFormData(); $body->add(name: 'name', value: $request->name); - if ($request->file != null){ + if ($request->file != null) { $body->addPart($request->file->toMultipartFormDataPart('file')); } try { @@ -85,15 +85,15 @@ public function uploadFile(UploadFileRequest $request, ?array $options = null): $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/file-upload-openapi/src/FileUploadExample/Requests/UploadFileRequest.php b/seed/php-sdk/file-upload-openapi/src/FileUploadExample/Requests/UploadFileRequest.php index b692de3462ab..e2ec50a8e2d1 100644 --- a/seed/php-sdk/file-upload-openapi/src/FileUploadExample/Requests/UploadFileRequest.php +++ b/seed/php-sdk/file-upload-openapi/src/FileUploadExample/Requests/UploadFileRequest.php @@ -27,8 +27,8 @@ class UploadFileRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->file = $values['file'] ?? null; + ) { + $this->name = $values['name']; + $this->file = $values['file'] ?? null; } } diff --git a/seed/php-sdk/file-upload-openapi/src/SeedClient.php b/seed/php-sdk/file-upload-openapi/src/SeedClient.php index f07db9d97d87..50c9941630e8 100644 --- a/seed/php-sdk/file-upload-openapi/src/SeedClient.php +++ b/seed/php-sdk/file-upload-openapi/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var FileUploadExampleClient $fileUploadExample @@ -40,26 +40,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->fileUploadExample = new FileUploadExampleClient($this->client, $this->options); } } diff --git a/seed/php-sdk/file-upload-openapi/src/Utils/File.php b/seed/php-sdk/file-upload-openapi/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/file-upload-openapi/src/Utils/File.php +++ b/seed/php-sdk/file-upload-openapi/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/file-upload-openapi/tests/Core/Client/RawClientTest.php b/seed/php-sdk/file-upload-openapi/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/file-upload-openapi/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/file-upload-openapi/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/file-upload-openapi/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/file-upload-openapi/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/file-upload-openapi/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/file-upload-openapi/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/file-upload-openapi/tests/Core/Json/EnumTest.php b/seed/php-sdk/file-upload-openapi/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/file-upload-openapi/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/file-upload-openapi/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/file-upload-openapi/tests/Core/Json/TraitTest.php b/seed/php-sdk/file-upload-openapi/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/file-upload-openapi/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/file-upload-openapi/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/file-upload-openapi/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/file-upload-openapi/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/file-upload-openapi/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/file-upload-openapi/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/file-upload/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/file-upload/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/file-upload/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/file-upload/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/file-upload/src/Core/Client/RawClient.php b/seed/php-sdk/file-upload/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/file-upload/src/Core/Client/RawClient.php +++ b/seed/php-sdk/file-upload/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/file-upload/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/file-upload/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/file-upload/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/file-upload/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/file-upload/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/file-upload/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/file-upload/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/file-upload/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/file-upload/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/file-upload/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/file-upload/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/file-upload/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/file-upload/src/Core/Types/ArrayType.php b/seed/php-sdk/file-upload/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/file-upload/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/file-upload/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/file-upload/src/Core/Types/Constant.php b/seed/php-sdk/file-upload/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/file-upload/src/Core/Types/Constant.php +++ b/seed/php-sdk/file-upload/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/file-upload/src/Core/Types/Union.php b/seed/php-sdk/file-upload/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/file-upload/src/Core/Types/Union.php +++ b/seed/php-sdk/file-upload/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/file-upload/src/Exceptions/SeedApiException.php b/seed/php-sdk/file-upload/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/file-upload/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/file-upload/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/file-upload/src/Exceptions/SeedException.php b/seed/php-sdk/file-upload/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/file-upload/src/Exceptions/SeedException.php +++ b/seed/php-sdk/file-upload/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/file-upload/src/SeedClient.php b/seed/php-sdk/file-upload/src/SeedClient.php index 8d7adcb3b0f2..0fd6a9d1029e 100644 --- a/seed/php-sdk/file-upload/src/SeedClient.php +++ b/seed/php-sdk/file-upload/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var ServiceClient $service @@ -40,26 +40,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->service = new ServiceClient($this->client, $this->options); } } diff --git a/seed/php-sdk/file-upload/src/Service/Requests/InlineTypeRequest.php b/seed/php-sdk/file-upload/src/Service/Requests/InlineTypeRequest.php index 44c5fc728d18..b7c131394f5a 100644 --- a/seed/php-sdk/file-upload/src/Service/Requests/InlineTypeRequest.php +++ b/seed/php-sdk/file-upload/src/Service/Requests/InlineTypeRequest.php @@ -28,8 +28,8 @@ class InlineTypeRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->file = $values['file'];$this->request = $values['request']; + ) { + $this->file = $values['file']; + $this->request = $values['request']; } } diff --git a/seed/php-sdk/file-upload/src/Service/Requests/JustFileRequest.php b/seed/php-sdk/file-upload/src/Service/Requests/JustFileRequest.php index d40afb87b3b4..d3f6a4d9e779 100644 --- a/seed/php-sdk/file-upload/src/Service/Requests/JustFileRequest.php +++ b/seed/php-sdk/file-upload/src/Service/Requests/JustFileRequest.php @@ -19,8 +19,7 @@ class JustFileRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->file = $values['file']; } } diff --git a/seed/php-sdk/file-upload/src/Service/Requests/JustFileWithQueryParamsRequest.php b/seed/php-sdk/file-upload/src/Service/Requests/JustFileWithQueryParamsRequest.php index d7f045f15b25..3a25400f5659 100644 --- a/seed/php-sdk/file-upload/src/Service/Requests/JustFileWithQueryParamsRequest.php +++ b/seed/php-sdk/file-upload/src/Service/Requests/JustFileWithQueryParamsRequest.php @@ -49,8 +49,12 @@ class JustFileWithQueryParamsRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->maybeString = $values['maybeString'] ?? null;$this->integer = $values['integer'];$this->maybeInteger = $values['maybeInteger'] ?? null;$this->listOfStrings = $values['listOfStrings'];$this->optionalListOfStrings = $values['optionalListOfStrings'] ?? null;$this->file = $values['file']; + ) { + $this->maybeString = $values['maybeString'] ?? null; + $this->integer = $values['integer']; + $this->maybeInteger = $values['maybeInteger'] ?? null; + $this->listOfStrings = $values['listOfStrings']; + $this->optionalListOfStrings = $values['optionalListOfStrings'] ?? null; + $this->file = $values['file']; } } diff --git a/seed/php-sdk/file-upload/src/Service/Requests/MyOtherRequest.php b/seed/php-sdk/file-upload/src/Service/Requests/MyOtherRequest.php index 3ee9bc96ff83..414301170f2f 100644 --- a/seed/php-sdk/file-upload/src/Service/Requests/MyOtherRequest.php +++ b/seed/php-sdk/file-upload/src/Service/Requests/MyOtherRequest.php @@ -126,8 +126,22 @@ class MyOtherRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->maybeString = $values['maybeString'] ?? null;$this->integer = $values['integer'];$this->file = $values['file'];$this->fileList = $values['fileList'];$this->maybeFile = $values['maybeFile'] ?? null;$this->maybeFileList = $values['maybeFileList'] ?? null;$this->maybeInteger = $values['maybeInteger'] ?? null;$this->optionalListOfStrings = $values['optionalListOfStrings'] ?? null;$this->listOfObjects = $values['listOfObjects'];$this->optionalMetadata = $values['optionalMetadata'] ?? null;$this->optionalObjectType = $values['optionalObjectType'] ?? null;$this->optionalId = $values['optionalId'] ?? null;$this->listOfObjectsWithOptionals = $values['listOfObjectsWithOptionals'];$this->aliasObject = $values['aliasObject'];$this->listOfAliasObject = $values['listOfAliasObject'];$this->aliasListOfObject = $values['aliasListOfObject']; + ) { + $this->maybeString = $values['maybeString'] ?? null; + $this->integer = $values['integer']; + $this->file = $values['file']; + $this->fileList = $values['fileList']; + $this->maybeFile = $values['maybeFile'] ?? null; + $this->maybeFileList = $values['maybeFileList'] ?? null; + $this->maybeInteger = $values['maybeInteger'] ?? null; + $this->optionalListOfStrings = $values['optionalListOfStrings'] ?? null; + $this->listOfObjects = $values['listOfObjects']; + $this->optionalMetadata = $values['optionalMetadata'] ?? null; + $this->optionalObjectType = $values['optionalObjectType'] ?? null; + $this->optionalId = $values['optionalId'] ?? null; + $this->listOfObjectsWithOptionals = $values['listOfObjectsWithOptionals']; + $this->aliasObject = $values['aliasObject']; + $this->listOfAliasObject = $values['listOfAliasObject']; + $this->aliasListOfObject = $values['aliasListOfObject']; } } diff --git a/seed/php-sdk/file-upload/src/Service/Requests/MyRequest.php b/seed/php-sdk/file-upload/src/Service/Requests/MyRequest.php index 808eacc792f2..d5ec43686f90 100644 --- a/seed/php-sdk/file-upload/src/Service/Requests/MyRequest.php +++ b/seed/php-sdk/file-upload/src/Service/Requests/MyRequest.php @@ -118,8 +118,21 @@ class MyRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->maybeString = $values['maybeString'] ?? null;$this->integer = $values['integer'];$this->file = $values['file'];$this->fileList = $values['fileList'];$this->maybeFile = $values['maybeFile'] ?? null;$this->maybeFileList = $values['maybeFileList'] ?? null;$this->maybeInteger = $values['maybeInteger'] ?? null;$this->optionalListOfStrings = $values['optionalListOfStrings'] ?? null;$this->listOfObjects = $values['listOfObjects'];$this->optionalMetadata = $values['optionalMetadata'] ?? null;$this->optionalObjectType = $values['optionalObjectType'] ?? null;$this->optionalId = $values['optionalId'] ?? null;$this->aliasObject = $values['aliasObject'];$this->listOfAliasObject = $values['listOfAliasObject'];$this->aliasListOfObject = $values['aliasListOfObject']; + ) { + $this->maybeString = $values['maybeString'] ?? null; + $this->integer = $values['integer']; + $this->file = $values['file']; + $this->fileList = $values['fileList']; + $this->maybeFile = $values['maybeFile'] ?? null; + $this->maybeFileList = $values['maybeFileList'] ?? null; + $this->maybeInteger = $values['maybeInteger'] ?? null; + $this->optionalListOfStrings = $values['optionalListOfStrings'] ?? null; + $this->listOfObjects = $values['listOfObjects']; + $this->optionalMetadata = $values['optionalMetadata'] ?? null; + $this->optionalObjectType = $values['optionalObjectType'] ?? null; + $this->optionalId = $values['optionalId'] ?? null; + $this->aliasObject = $values['aliasObject']; + $this->listOfAliasObject = $values['listOfAliasObject']; + $this->aliasListOfObject = $values['aliasListOfObject']; } } diff --git a/seed/php-sdk/file-upload/src/Service/Requests/OptionalArgsRequest.php b/seed/php-sdk/file-upload/src/Service/Requests/OptionalArgsRequest.php index e7eeb510de49..41389fbc57c0 100644 --- a/seed/php-sdk/file-upload/src/Service/Requests/OptionalArgsRequest.php +++ b/seed/php-sdk/file-upload/src/Service/Requests/OptionalArgsRequest.php @@ -27,8 +27,8 @@ class OptionalArgsRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->imageFile = $values['imageFile'] ?? null;$this->request = $values['request'] ?? null; + ) { + $this->imageFile = $values['imageFile'] ?? null; + $this->request = $values['request'] ?? null; } } diff --git a/seed/php-sdk/file-upload/src/Service/Requests/WithContentTypeRequest.php b/seed/php-sdk/file-upload/src/Service/Requests/WithContentTypeRequest.php index 08cde5d2a414..385ec2d75ffc 100644 --- a/seed/php-sdk/file-upload/src/Service/Requests/WithContentTypeRequest.php +++ b/seed/php-sdk/file-upload/src/Service/Requests/WithContentTypeRequest.php @@ -42,8 +42,10 @@ class WithContentTypeRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->file = $values['file'];$this->foo = $values['foo'];$this->bar = $values['bar'];$this->fooBar = $values['fooBar'] ?? null; + ) { + $this->file = $values['file']; + $this->foo = $values['foo']; + $this->bar = $values['bar']; + $this->fooBar = $values['fooBar'] ?? null; } } diff --git a/seed/php-sdk/file-upload/src/Service/Requests/WithFormEncodingRequest.php b/seed/php-sdk/file-upload/src/Service/Requests/WithFormEncodingRequest.php index 35b7357ca71c..458466bc7807 100644 --- a/seed/php-sdk/file-upload/src/Service/Requests/WithFormEncodingRequest.php +++ b/seed/php-sdk/file-upload/src/Service/Requests/WithFormEncodingRequest.php @@ -35,8 +35,9 @@ class WithFormEncodingRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->file = $values['file'];$this->foo = $values['foo'];$this->bar = $values['bar']; + ) { + $this->file = $values['file']; + $this->foo = $values['foo']; + $this->bar = $values['bar']; } } diff --git a/seed/php-sdk/file-upload/src/Service/ServiceClient.php b/seed/php-sdk/file-upload/src/Service/ServiceClient.php index 80b5145e6baa..6c22788588be 100644 --- a/seed/php-sdk/file-upload/src/Service/ServiceClient.php +++ b/seed/php-sdk/file-upload/src/Service/ServiceClient.php @@ -24,7 +24,7 @@ use Seed\Service\Requests\InlineTypeRequest; use Seed\Core\Json\JsonApiRequest; -class ServiceClient +class ServiceClient { /** * @var array{ @@ -52,11 +52,10 @@ class ServiceClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -73,50 +72,51 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function post(MyRequest $request, ?array $options = null): void { + public function post(MyRequest $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); $body = new MultipartFormData(); - if ($request->maybeString != null){ + if ($request->maybeString != null) { $body->add(name: 'maybe_string', value: $request->maybeString); } $body->add(name: 'integer', value: $request->integer); $body->addPart($request->file->toMultipartFormDataPart('file')); - foreach ($request->fileList as $file){ + foreach ($request->fileList as $file) { $body->addPart($file->toMultipartFormDataPart('file_list')); } - if ($request->maybeFile != null){ + if ($request->maybeFile != null) { $body->addPart($request->maybeFile->toMultipartFormDataPart('maybe_file')); } - if ($request->maybeFileList != null){ - foreach ($request->maybeFileList as $file){ + if ($request->maybeFileList != null) { + foreach ($request->maybeFileList as $file) { $body->addPart($file->toMultipartFormDataPart('maybe_file_list')); } } - if ($request->maybeInteger != null){ + if ($request->maybeInteger != null) { $body->add(name: 'maybe_integer', value: $request->maybeInteger); } - if ($request->optionalListOfStrings != null){ - foreach ($request->optionalListOfStrings as $element){ + if ($request->optionalListOfStrings != null) { + foreach ($request->optionalListOfStrings as $element) { $body->add(name: 'optional_list_of_strings', value: $element); } } - foreach ($request->listOfObjects as $element){ + foreach ($request->listOfObjects as $element) { $body->add(name: 'list_of_objects', value: $element->toJson()); } - if ($request->optionalMetadata != null){ + if ($request->optionalMetadata != null) { $body->add(name: 'optional_metadata', value: JsonEncoder::encode($request->optionalMetadata)); } - if ($request->optionalObjectType != null){ + if ($request->optionalObjectType != null) { $body->add(name: 'optional_object_type', value: $request->optionalObjectType); } - if ($request->optionalId != null){ + if ($request->optionalId != null) { $body->add(name: 'optional_id', value: $request->optionalId); } $body->add(name: 'alias_object', value: $request->aliasObject->toJson()); - foreach ($request->listOfAliasObject as $element){ + foreach ($request->listOfAliasObject as $element) { $body->add(name: 'list_of_alias_object', value: $element->toJson()); } - foreach ($request->aliasListOfObject as $element){ + foreach ($request->aliasListOfObject as $element) { $body->add(name: 'alias_list_of_object', value: $element->toJson()); } try { @@ -130,12 +130,12 @@ public function post(MyRequest $request, ?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -165,7 +165,8 @@ public function post(MyRequest $request, ?array $options = null): void { * @throws SeedException * @throws SeedApiException */ - public function justFile(JustFileRequest $request, ?array $options = null): void { + public function justFile(JustFileRequest $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); $body = new MultipartFormData(); $body->addPart($request->file->toMultipartFormDataPart('file')); @@ -180,12 +181,12 @@ public function justFile(JustFileRequest $request, ?array $options = null): void $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -215,18 +216,19 @@ public function justFile(JustFileRequest $request, ?array $options = null): void * @throws SeedException * @throws SeedApiException */ - public function justFileWithQueryParams(JustFileWithQueryParamsRequest $request, ?array $options = null): void { + public function justFileWithQueryParams(JustFileWithQueryParamsRequest $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); $query = []; $query['integer'] = $request->integer; $query['listOfStrings'] = $request->listOfStrings; - if ($request->maybeString != null){ + if ($request->maybeString != null) { $query['maybeString'] = $request->maybeString; } - if ($request->maybeInteger != null){ + if ($request->maybeInteger != null) { $query['maybeInteger'] = $request->maybeInteger; } - if ($request->optionalListOfStrings != null){ + if ($request->optionalListOfStrings != null) { $query['optionalListOfStrings'] = $request->optionalListOfStrings; } $body = new MultipartFormData(); @@ -243,12 +245,12 @@ public function justFileWithQueryParams(JustFileWithQueryParamsRequest $request, $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -278,7 +280,8 @@ public function justFileWithQueryParams(JustFileWithQueryParamsRequest $request, * @throws SeedException * @throws SeedApiException */ - public function withContentType(WithContentTypeRequest $request, ?array $options = null): void { + public function withContentType(WithContentTypeRequest $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); $body = new MultipartFormData(); $body->addPart( @@ -293,7 +296,7 @@ public function withContentType(WithContentTypeRequest $request, ?array $options value: $request->bar->toJson(), contentType: 'application/json', ); - if ($request->fooBar != null){ + if ($request->fooBar != null) { $body->add( name: 'foo_bar', value: $request->fooBar->toJson(), @@ -311,12 +314,12 @@ public function withContentType(WithContentTypeRequest $request, ?array $options $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -346,7 +349,8 @@ public function withContentType(WithContentTypeRequest $request, ?array $options * @throws SeedException * @throws SeedApiException */ - public function withFormEncoding(WithFormEncodingRequest $request, ?array $options = null): void { + public function withFormEncoding(WithFormEncodingRequest $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); $body = new MultipartFormData(); $body->addPart( @@ -368,12 +372,12 @@ public function withFormEncoding(WithFormEncodingRequest $request, ?array $optio $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -403,53 +407,54 @@ public function withFormEncoding(WithFormEncodingRequest $request, ?array $optio * @throws SeedException * @throws SeedApiException */ - public function withFormEncodedContainers(MyOtherRequest $request, ?array $options = null): void { + public function withFormEncodedContainers(MyOtherRequest $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); $body = new MultipartFormData(); - if ($request->maybeString != null){ + if ($request->maybeString != null) { $body->add(name: 'maybe_string', value: $request->maybeString); } $body->add(name: 'integer', value: $request->integer); $body->addPart($request->file->toMultipartFormDataPart('file')); - foreach ($request->fileList as $file){ + foreach ($request->fileList as $file) { $body->addPart($file->toMultipartFormDataPart('file_list')); } - if ($request->maybeFile != null){ + if ($request->maybeFile != null) { $body->addPart($request->maybeFile->toMultipartFormDataPart('maybe_file')); } - if ($request->maybeFileList != null){ - foreach ($request->maybeFileList as $file){ + if ($request->maybeFileList != null) { + foreach ($request->maybeFileList as $file) { $body->addPart($file->toMultipartFormDataPart('maybe_file_list')); } } - if ($request->maybeInteger != null){ + if ($request->maybeInteger != null) { $body->add(name: 'maybe_integer', value: $request->maybeInteger); } - if ($request->optionalListOfStrings != null){ - foreach ($request->optionalListOfStrings as $element){ + if ($request->optionalListOfStrings != null) { + foreach ($request->optionalListOfStrings as $element) { $body->add(name: 'optional_list_of_strings', value: $element); } } - foreach ($request->listOfObjects as $element){ + foreach ($request->listOfObjects as $element) { $body->add(name: 'list_of_objects', value: $element->toJson()); } - if ($request->optionalMetadata != null){ + if ($request->optionalMetadata != null) { $body->add(name: 'optional_metadata', value: JsonEncoder::encode($request->optionalMetadata)); } - if ($request->optionalObjectType != null){ + if ($request->optionalObjectType != null) { $body->add(name: 'optional_object_type', value: $request->optionalObjectType); } - if ($request->optionalId != null){ + if ($request->optionalId != null) { $body->add(name: 'optional_id', value: $request->optionalId); } - foreach ($request->listOfObjectsWithOptionals as $element){ + foreach ($request->listOfObjectsWithOptionals as $element) { $body->add(name: 'list_of_objects_with_optionals', value: $element->toJson()); } $body->add(name: 'alias_object', value: $request->aliasObject->toJson()); - foreach ($request->listOfAliasObject as $element){ + foreach ($request->listOfAliasObject as $element) { $body->add(name: 'list_of_alias_object', value: $element->toJson()); } - foreach ($request->aliasListOfObject as $element){ + foreach ($request->aliasListOfObject as $element) { $body->add(name: 'alias_list_of_object', value: $element->toJson()); } try { @@ -463,12 +468,12 @@ public function withFormEncodedContainers(MyOtherRequest $request, ?array $optio $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -499,10 +504,11 @@ public function withFormEncodedContainers(MyOtherRequest $request, ?array $optio * @throws SeedException * @throws SeedApiException */ - public function optionalArgs(OptionalArgsRequest $request = new OptionalArgsRequest(), ?array $options = null): string { + public function optionalArgs(OptionalArgsRequest $request = new OptionalArgsRequest(), ?array $options = null): string + { $options = array_merge($this->options, $options ?? []); $body = new MultipartFormData(); - if ($request->imageFile != null){ + if ($request->imageFile != null) { $body->addPart( $request->imageFile->toMultipartFormDataPart( name: 'image_file', @@ -510,7 +516,7 @@ public function optionalArgs(OptionalArgsRequest $request = new OptionalArgsRequ ), ); } - if ($request->request != null){ + if ($request->request != null) { $body->add( name: 'request', value: JsonEncoder::encode($request->request), @@ -528,15 +534,15 @@ public function optionalArgs(OptionalArgsRequest $request = new OptionalArgsRequ $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -567,7 +573,8 @@ public function optionalArgs(OptionalArgsRequest $request = new OptionalArgsRequ * @throws SeedException * @throws SeedApiException */ - public function withInlineType(InlineTypeRequest $request, ?array $options = null): string { + public function withInlineType(InlineTypeRequest $request, ?array $options = null): string + { $options = array_merge($this->options, $options ?? []); $body = new MultipartFormData(); $body->addPart($request->file->toMultipartFormDataPart('file')); @@ -583,15 +590,15 @@ public function withInlineType(InlineTypeRequest $request, ?array $options = nul $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -621,7 +628,8 @@ public function withInlineType(InlineTypeRequest $request, ?array $options = nul * @throws SeedException * @throws SeedApiException */ - public function simple(?array $options = null): void { + public function simple(?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -633,12 +641,12 @@ public function simple(?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/file-upload/src/Service/Types/MyInlineType.php b/seed/php-sdk/file-upload/src/Service/Types/MyInlineType.php index d0161ed9c4e6..e74817a050cc 100644 --- a/seed/php-sdk/file-upload/src/Service/Types/MyInlineType.php +++ b/seed/php-sdk/file-upload/src/Service/Types/MyInlineType.php @@ -20,15 +20,15 @@ class MyInlineType extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->bar = $values['bar']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/file-upload/src/Service/Types/MyObject.php b/seed/php-sdk/file-upload/src/Service/Types/MyObject.php index dba98cc97282..82255c22acf7 100644 --- a/seed/php-sdk/file-upload/src/Service/Types/MyObject.php +++ b/seed/php-sdk/file-upload/src/Service/Types/MyObject.php @@ -20,15 +20,15 @@ class MyObject extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->foo = $values['foo']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/file-upload/src/Service/Types/MyObjectWithOptional.php b/seed/php-sdk/file-upload/src/Service/Types/MyObjectWithOptional.php index 4987ffd463fc..65f7dac74c2a 100644 --- a/seed/php-sdk/file-upload/src/Service/Types/MyObjectWithOptional.php +++ b/seed/php-sdk/file-upload/src/Service/Types/MyObjectWithOptional.php @@ -27,15 +27,16 @@ class MyObjectWithOptional extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->prop = $values['prop'];$this->optionalProp = $values['optionalProp'] ?? null; + ) { + $this->prop = $values['prop']; + $this->optionalProp = $values['optionalProp'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/file-upload/src/Service/Types/ObjectType.php b/seed/php-sdk/file-upload/src/Service/Types/ObjectType.php index 38e546e46027..f3fe8d12bd4a 100644 --- a/seed/php-sdk/file-upload/src/Service/Types/ObjectType.php +++ b/seed/php-sdk/file-upload/src/Service/Types/ObjectType.php @@ -2,8 +2,8 @@ namespace Seed\Service\Types; -enum ObjectType - : string { +enum ObjectType: string +{ case Foo = "FOO"; case Bar = "BAR"; } diff --git a/seed/php-sdk/file-upload/src/Utils/File.php b/seed/php-sdk/file-upload/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/file-upload/src/Utils/File.php +++ b/seed/php-sdk/file-upload/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/file-upload/tests/Core/Client/RawClientTest.php b/seed/php-sdk/file-upload/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/file-upload/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/file-upload/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/file-upload/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/file-upload/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/file-upload/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/file-upload/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/file-upload/tests/Core/Json/EnumTest.php b/seed/php-sdk/file-upload/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/file-upload/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/file-upload/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/file-upload/tests/Core/Json/TraitTest.php b/seed/php-sdk/file-upload/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/file-upload/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/file-upload/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/file-upload/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/file-upload/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/file-upload/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/file-upload/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/folders/src/A/AClient.php b/seed/php-sdk/folders/src/A/AClient.php index b22d0e69038c..adfb1fccc198 100644 --- a/seed/php-sdk/folders/src/A/AClient.php +++ b/seed/php-sdk/folders/src/A/AClient.php @@ -7,7 +7,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class AClient +class AClient { /** * @var BClient $b @@ -45,11 +45,10 @@ class AClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; $this->b = new BClient($this->client, $this->options); diff --git a/seed/php-sdk/folders/src/A/B/BClient.php b/seed/php-sdk/folders/src/A/B/BClient.php index 1379341d8382..9e9775d0bbf8 100644 --- a/seed/php-sdk/folders/src/A/B/BClient.php +++ b/seed/php-sdk/folders/src/A/B/BClient.php @@ -11,7 +11,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class BClient +class BClient { /** * @var array{ @@ -39,11 +39,10 @@ class BClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -60,7 +59,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function foo(?array $options = null): void { + public function foo(?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -72,12 +72,12 @@ public function foo(?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/folders/src/A/C/CClient.php b/seed/php-sdk/folders/src/A/C/CClient.php index f735b72ac8aa..ce0ab8a7dec8 100644 --- a/seed/php-sdk/folders/src/A/C/CClient.php +++ b/seed/php-sdk/folders/src/A/C/CClient.php @@ -11,7 +11,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class CClient +class CClient { /** * @var array{ @@ -39,11 +39,10 @@ class CClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -60,7 +59,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function foo(?array $options = null): void { + public function foo(?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -72,12 +72,12 @@ public function foo(?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/folders/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/folders/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/folders/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/folders/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/folders/src/Core/Client/RawClient.php b/seed/php-sdk/folders/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/folders/src/Core/Client/RawClient.php +++ b/seed/php-sdk/folders/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/folders/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/folders/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/folders/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/folders/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/folders/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/folders/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/folders/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/folders/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/folders/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/folders/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/folders/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/folders/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/folders/src/Core/Types/ArrayType.php b/seed/php-sdk/folders/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/folders/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/folders/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/folders/src/Core/Types/Constant.php b/seed/php-sdk/folders/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/folders/src/Core/Types/Constant.php +++ b/seed/php-sdk/folders/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/folders/src/Core/Types/Union.php b/seed/php-sdk/folders/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/folders/src/Core/Types/Union.php +++ b/seed/php-sdk/folders/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/folders/src/Exceptions/SeedApiException.php b/seed/php-sdk/folders/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/folders/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/folders/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/folders/src/Exceptions/SeedException.php b/seed/php-sdk/folders/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/folders/src/Exceptions/SeedException.php +++ b/seed/php-sdk/folders/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/folders/src/Folder/FolderClient.php b/seed/php-sdk/folders/src/Folder/FolderClient.php index 80a6b72b1e90..83650d23f63d 100644 --- a/seed/php-sdk/folders/src/Folder/FolderClient.php +++ b/seed/php-sdk/folders/src/Folder/FolderClient.php @@ -12,7 +12,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class FolderClient +class FolderClient { /** * @var ServiceClient $service @@ -45,11 +45,10 @@ class FolderClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; $this->service = new ServiceClient($this->client, $this->options); @@ -67,7 +66,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function foo(?array $options = null): void { + public function foo(?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -79,12 +79,12 @@ public function foo(?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/folders/src/Folder/Service/ServiceClient.php b/seed/php-sdk/folders/src/Folder/Service/ServiceClient.php index e54ba0a5d4d7..c30a4a8336af 100644 --- a/seed/php-sdk/folders/src/Folder/Service/ServiceClient.php +++ b/seed/php-sdk/folders/src/Folder/Service/ServiceClient.php @@ -11,7 +11,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class ServiceClient +class ServiceClient { /** * @var array{ @@ -39,11 +39,10 @@ class ServiceClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -60,7 +59,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function endpoint(?array $options = null): void { + public function endpoint(?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -72,12 +72,12 @@ public function endpoint(?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -108,7 +108,8 @@ public function endpoint(?array $options = null): void { * @throws SeedException * @throws SeedApiException */ - public function unknownRequest(mixed $request, ?array $options = null): void { + public function unknownRequest(mixed $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -121,12 +122,12 @@ public function unknownRequest(mixed $request, ?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/folders/src/SeedClient.php b/seed/php-sdk/folders/src/SeedClient.php index cd4dd929f3be..fca872f1a2a7 100644 --- a/seed/php-sdk/folders/src/SeedClient.php +++ b/seed/php-sdk/folders/src/SeedClient.php @@ -13,7 +13,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class SeedClient +class SeedClient { /** * @var AClient $a @@ -52,26 +52,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->a = new AClient($this->client, $this->options); $this->folder = new FolderClient($this->client, $this->options); } @@ -88,7 +87,8 @@ public function __construct( * @throws SeedException * @throws SeedApiException */ - public function foo(?array $options = null): void { + public function foo(?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -100,12 +100,12 @@ public function foo(?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/folders/src/Utils/File.php b/seed/php-sdk/folders/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/folders/src/Utils/File.php +++ b/seed/php-sdk/folders/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/folders/tests/Core/Client/RawClientTest.php b/seed/php-sdk/folders/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/folders/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/folders/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/folders/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/folders/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/folders/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/folders/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/folders/tests/Core/Json/EnumTest.php b/seed/php-sdk/folders/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/folders/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/folders/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/folders/tests/Core/Json/TraitTest.php b/seed/php-sdk/folders/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/folders/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/folders/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/folders/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/folders/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/folders/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/folders/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/header-auth-environment-variable/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/header-auth-environment-variable/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/header-auth-environment-variable/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/header-auth-environment-variable/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/header-auth-environment-variable/src/Core/Client/RawClient.php b/seed/php-sdk/header-auth-environment-variable/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/header-auth-environment-variable/src/Core/Client/RawClient.php +++ b/seed/php-sdk/header-auth-environment-variable/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/header-auth-environment-variable/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/header-auth-environment-variable/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/header-auth-environment-variable/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/header-auth-environment-variable/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/header-auth-environment-variable/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/header-auth-environment-variable/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/header-auth-environment-variable/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/header-auth-environment-variable/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/header-auth-environment-variable/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/header-auth-environment-variable/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/header-auth-environment-variable/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/header-auth-environment-variable/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/header-auth-environment-variable/src/Core/Types/ArrayType.php b/seed/php-sdk/header-auth-environment-variable/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/header-auth-environment-variable/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/header-auth-environment-variable/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/header-auth-environment-variable/src/Core/Types/Constant.php b/seed/php-sdk/header-auth-environment-variable/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/header-auth-environment-variable/src/Core/Types/Constant.php +++ b/seed/php-sdk/header-auth-environment-variable/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/header-auth-environment-variable/src/Core/Types/Union.php b/seed/php-sdk/header-auth-environment-variable/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/header-auth-environment-variable/src/Core/Types/Union.php +++ b/seed/php-sdk/header-auth-environment-variable/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/header-auth-environment-variable/src/Exceptions/SeedApiException.php b/seed/php-sdk/header-auth-environment-variable/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/header-auth-environment-variable/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/header-auth-environment-variable/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/header-auth-environment-variable/src/Exceptions/SeedException.php b/seed/php-sdk/header-auth-environment-variable/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/header-auth-environment-variable/src/Exceptions/SeedException.php +++ b/seed/php-sdk/header-auth-environment-variable/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/header-auth-environment-variable/src/SeedClient.php b/seed/php-sdk/header-auth-environment-variable/src/SeedClient.php index 703248ef52b7..0403b35a1f62 100644 --- a/seed/php-sdk/header-auth-environment-variable/src/SeedClient.php +++ b/seed/php-sdk/header-auth-environment-variable/src/SeedClient.php @@ -7,7 +7,7 @@ use Seed\Core\Client\RawClient; use Exception; -class SeedClient +class SeedClient { /** * @var ServiceClient $service @@ -43,8 +43,7 @@ class SeedClient public function __construct( ?string $headerTokenAuth = null, ?array $options = null, - ) - { + ) { $headerTokenAuth ??= $this->getFromEnvOrThrow('HEADER_TOKEN_ENV_VAR', 'Please pass in headerTokenAuth or set the environment variable HEADER_TOKEN_ENV_VAR.'); $defaultHeaders = [ 'x-api-key' => "test_prefix $headerTokenAuth", @@ -53,18 +52,18 @@ public function __construct( 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->service = new ServiceClient($this->client, $this->options); } @@ -73,7 +72,8 @@ public function __construct( * @param string $message * @return string */ - private function getFromEnvOrThrow(string $env, string $message): string { + private function getFromEnvOrThrow(string $env, string $message): string + { $value = getenv($env); return $value ? (string) $value : throw new Exception($message); } diff --git a/seed/php-sdk/header-auth-environment-variable/src/Service/ServiceClient.php b/seed/php-sdk/header-auth-environment-variable/src/Service/ServiceClient.php index 5bd3e1044def..1689987a3d90 100644 --- a/seed/php-sdk/header-auth-environment-variable/src/Service/ServiceClient.php +++ b/seed/php-sdk/header-auth-environment-variable/src/Service/ServiceClient.php @@ -13,7 +13,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class ServiceClient +class ServiceClient { /** * @var array{ @@ -41,11 +41,10 @@ class ServiceClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -65,7 +64,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getWithBearerToken(?array $options = null): string { + public function getWithBearerToken(?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -77,15 +77,15 @@ public function getWithBearerToken(?array $options = null): string { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/header-auth-environment-variable/src/Utils/File.php b/seed/php-sdk/header-auth-environment-variable/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/header-auth-environment-variable/src/Utils/File.php +++ b/seed/php-sdk/header-auth-environment-variable/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/header-auth-environment-variable/tests/Core/Client/RawClientTest.php b/seed/php-sdk/header-auth-environment-variable/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/header-auth-environment-variable/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/header-auth-environment-variable/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/header-auth-environment-variable/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/header-auth-environment-variable/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/header-auth-environment-variable/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/header-auth-environment-variable/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/header-auth-environment-variable/tests/Core/Json/EnumTest.php b/seed/php-sdk/header-auth-environment-variable/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/header-auth-environment-variable/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/header-auth-environment-variable/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/header-auth-environment-variable/tests/Core/Json/TraitTest.php b/seed/php-sdk/header-auth-environment-variable/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/header-auth-environment-variable/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/header-auth-environment-variable/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/header-auth-environment-variable/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/header-auth-environment-variable/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/header-auth-environment-variable/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/header-auth-environment-variable/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/header-auth/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/header-auth/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/header-auth/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/header-auth/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/header-auth/src/Core/Client/RawClient.php b/seed/php-sdk/header-auth/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/header-auth/src/Core/Client/RawClient.php +++ b/seed/php-sdk/header-auth/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/header-auth/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/header-auth/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/header-auth/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/header-auth/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/header-auth/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/header-auth/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/header-auth/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/header-auth/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/header-auth/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/header-auth/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/header-auth/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/header-auth/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/header-auth/src/Core/Types/ArrayType.php b/seed/php-sdk/header-auth/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/header-auth/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/header-auth/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/header-auth/src/Core/Types/Constant.php b/seed/php-sdk/header-auth/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/header-auth/src/Core/Types/Constant.php +++ b/seed/php-sdk/header-auth/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/header-auth/src/Core/Types/Union.php b/seed/php-sdk/header-auth/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/header-auth/src/Core/Types/Union.php +++ b/seed/php-sdk/header-auth/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/header-auth/src/Exceptions/SeedApiException.php b/seed/php-sdk/header-auth/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/header-auth/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/header-auth/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/header-auth/src/Exceptions/SeedException.php b/seed/php-sdk/header-auth/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/header-auth/src/Exceptions/SeedException.php +++ b/seed/php-sdk/header-auth/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/header-auth/src/SeedClient.php b/seed/php-sdk/header-auth/src/SeedClient.php index 1de1c4937860..4372495da7d5 100644 --- a/seed/php-sdk/header-auth/src/SeedClient.php +++ b/seed/php-sdk/header-auth/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var ServiceClient $service @@ -42,8 +42,7 @@ class SeedClient public function __construct( string $headerTokenAuth, ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'x-api-key' => "test_prefix $headerTokenAuth", 'X-Fern-Language' => 'PHP', @@ -51,18 +50,18 @@ public function __construct( 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->service = new ServiceClient($this->client, $this->options); } } diff --git a/seed/php-sdk/header-auth/src/Service/ServiceClient.php b/seed/php-sdk/header-auth/src/Service/ServiceClient.php index 5bd3e1044def..1689987a3d90 100644 --- a/seed/php-sdk/header-auth/src/Service/ServiceClient.php +++ b/seed/php-sdk/header-auth/src/Service/ServiceClient.php @@ -13,7 +13,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class ServiceClient +class ServiceClient { /** * @var array{ @@ -41,11 +41,10 @@ class ServiceClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -65,7 +64,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getWithBearerToken(?array $options = null): string { + public function getWithBearerToken(?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -77,15 +77,15 @@ public function getWithBearerToken(?array $options = null): string { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/header-auth/src/Utils/File.php b/seed/php-sdk/header-auth/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/header-auth/src/Utils/File.php +++ b/seed/php-sdk/header-auth/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/header-auth/tests/Core/Client/RawClientTest.php b/seed/php-sdk/header-auth/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/header-auth/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/header-auth/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/header-auth/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/header-auth/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/header-auth/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/header-auth/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/header-auth/tests/Core/Json/EnumTest.php b/seed/php-sdk/header-auth/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/header-auth/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/header-auth/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/header-auth/tests/Core/Json/TraitTest.php b/seed/php-sdk/header-auth/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/header-auth/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/header-auth/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/header-auth/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/header-auth/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/header-auth/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/header-auth/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/http-head/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/http-head/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/http-head/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/http-head/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/http-head/src/Core/Client/RawClient.php b/seed/php-sdk/http-head/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/http-head/src/Core/Client/RawClient.php +++ b/seed/php-sdk/http-head/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/http-head/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/http-head/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/http-head/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/http-head/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/http-head/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/http-head/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/http-head/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/http-head/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/http-head/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/http-head/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/http-head/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/http-head/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/http-head/src/Core/Types/ArrayType.php b/seed/php-sdk/http-head/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/http-head/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/http-head/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/http-head/src/Core/Types/Constant.php b/seed/php-sdk/http-head/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/http-head/src/Core/Types/Constant.php +++ b/seed/php-sdk/http-head/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/http-head/src/Core/Types/Union.php b/seed/php-sdk/http-head/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/http-head/src/Core/Types/Union.php +++ b/seed/php-sdk/http-head/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/http-head/src/Exceptions/SeedApiException.php b/seed/php-sdk/http-head/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/http-head/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/http-head/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/http-head/src/Exceptions/SeedException.php b/seed/php-sdk/http-head/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/http-head/src/Exceptions/SeedException.php +++ b/seed/php-sdk/http-head/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/http-head/src/SeedClient.php b/seed/php-sdk/http-head/src/SeedClient.php index a13bf5acc053..ee87d1cb303f 100644 --- a/seed/php-sdk/http-head/src/SeedClient.php +++ b/seed/php-sdk/http-head/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var UserClient $user @@ -40,26 +40,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->user = new UserClient($this->client, $this->options); } } diff --git a/seed/php-sdk/http-head/src/User/Requests/ListUsersRequest.php b/seed/php-sdk/http-head/src/User/Requests/ListUsersRequest.php index b8b4d20951f2..381a404a0c51 100644 --- a/seed/php-sdk/http-head/src/User/Requests/ListUsersRequest.php +++ b/seed/php-sdk/http-head/src/User/Requests/ListUsersRequest.php @@ -18,8 +18,7 @@ class ListUsersRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->limit = $values['limit']; } } diff --git a/seed/php-sdk/http-head/src/User/Types/User.php b/seed/php-sdk/http-head/src/User/Types/User.php index 0dc2f6896280..c2d2049a60a4 100644 --- a/seed/php-sdk/http-head/src/User/Types/User.php +++ b/seed/php-sdk/http-head/src/User/Types/User.php @@ -28,15 +28,16 @@ class User extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->tags = $values['tags']; + ) { + $this->name = $values['name']; + $this->tags = $values['tags']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/http-head/src/User/UserClient.php b/seed/php-sdk/http-head/src/User/UserClient.php index d3ac6c072910..8a193da62683 100644 --- a/seed/php-sdk/http-head/src/User/UserClient.php +++ b/seed/php-sdk/http-head/src/User/UserClient.php @@ -15,7 +15,7 @@ use Seed\Core\Json\JsonDecoder; use JsonException; -class UserClient +class UserClient { /** * @var array{ @@ -43,11 +43,10 @@ class UserClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -64,7 +63,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function head(?array $options = null): void { + public function head(?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -76,12 +76,12 @@ public function head(?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -113,7 +113,8 @@ public function head(?array $options = null): void { * @throws SeedException * @throws SeedApiException */ - public function list(ListUsersRequest $request, ?array $options = null): array { + public function list(ListUsersRequest $request, ?array $options = null): array + { $options = array_merge($this->options, $options ?? []); $query = []; $query['limit'] = $request->limit; @@ -128,15 +129,15 @@ public function list(ListUsersRequest $request, ?array $options = null): array { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, [User::class]); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/http-head/src/Utils/File.php b/seed/php-sdk/http-head/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/http-head/src/Utils/File.php +++ b/seed/php-sdk/http-head/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/http-head/tests/Core/Client/RawClientTest.php b/seed/php-sdk/http-head/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/http-head/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/http-head/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/http-head/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/http-head/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/http-head/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/http-head/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/http-head/tests/Core/Json/EnumTest.php b/seed/php-sdk/http-head/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/http-head/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/http-head/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/http-head/tests/Core/Json/TraitTest.php b/seed/php-sdk/http-head/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/http-head/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/http-head/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/http-head/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/http-head/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/http-head/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/http-head/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/idempotency-headers/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/idempotency-headers/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/idempotency-headers/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/idempotency-headers/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/idempotency-headers/src/Core/Client/RawClient.php b/seed/php-sdk/idempotency-headers/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/idempotency-headers/src/Core/Client/RawClient.php +++ b/seed/php-sdk/idempotency-headers/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/idempotency-headers/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/idempotency-headers/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/idempotency-headers/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/idempotency-headers/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/idempotency-headers/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/idempotency-headers/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/idempotency-headers/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/idempotency-headers/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/idempotency-headers/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/idempotency-headers/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/idempotency-headers/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/idempotency-headers/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/idempotency-headers/src/Core/Types/ArrayType.php b/seed/php-sdk/idempotency-headers/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/idempotency-headers/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/idempotency-headers/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/idempotency-headers/src/Core/Types/Constant.php b/seed/php-sdk/idempotency-headers/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/idempotency-headers/src/Core/Types/Constant.php +++ b/seed/php-sdk/idempotency-headers/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/idempotency-headers/src/Core/Types/Union.php b/seed/php-sdk/idempotency-headers/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/idempotency-headers/src/Core/Types/Union.php +++ b/seed/php-sdk/idempotency-headers/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/idempotency-headers/src/Exceptions/SeedApiException.php b/seed/php-sdk/idempotency-headers/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/idempotency-headers/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/idempotency-headers/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/idempotency-headers/src/Exceptions/SeedException.php b/seed/php-sdk/idempotency-headers/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/idempotency-headers/src/Exceptions/SeedException.php +++ b/seed/php-sdk/idempotency-headers/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/idempotency-headers/src/Payment/PaymentClient.php b/seed/php-sdk/idempotency-headers/src/Payment/PaymentClient.php index 9ab7bb565bb2..72bb8bd598ee 100644 --- a/seed/php-sdk/idempotency-headers/src/Payment/PaymentClient.php +++ b/seed/php-sdk/idempotency-headers/src/Payment/PaymentClient.php @@ -14,7 +14,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class PaymentClient +class PaymentClient { /** * @var array{ @@ -42,11 +42,10 @@ class PaymentClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -65,7 +64,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function create(CreatePaymentRequest $request, ?array $options = null): string { + public function create(CreatePaymentRequest $request, ?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -78,15 +78,15 @@ public function create(CreatePaymentRequest $request, ?array $options = null): s $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -117,7 +117,8 @@ public function create(CreatePaymentRequest $request, ?array $options = null): s * @throws SeedException * @throws SeedApiException */ - public function delete(string $paymentId, ?array $options = null): void { + public function delete(string $paymentId, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -129,12 +130,12 @@ public function delete(string $paymentId, ?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/idempotency-headers/src/Payment/Requests/CreatePaymentRequest.php b/seed/php-sdk/idempotency-headers/src/Payment/Requests/CreatePaymentRequest.php index d63f2eb503eb..e1fe8ca95a61 100644 --- a/seed/php-sdk/idempotency-headers/src/Payment/Requests/CreatePaymentRequest.php +++ b/seed/php-sdk/idempotency-headers/src/Payment/Requests/CreatePaymentRequest.php @@ -28,8 +28,8 @@ class CreatePaymentRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->amount = $values['amount'];$this->currency = $values['currency']; + ) { + $this->amount = $values['amount']; + $this->currency = $values['currency']; } } diff --git a/seed/php-sdk/idempotency-headers/src/Payment/Types/Currency.php b/seed/php-sdk/idempotency-headers/src/Payment/Types/Currency.php index 2d2fb763b792..b3d118b1a864 100644 --- a/seed/php-sdk/idempotency-headers/src/Payment/Types/Currency.php +++ b/seed/php-sdk/idempotency-headers/src/Payment/Types/Currency.php @@ -2,8 +2,8 @@ namespace Seed\Payment\Types; -enum Currency - : string { +enum Currency: string +{ case Usd = "USD"; case Yen = "YEN"; } diff --git a/seed/php-sdk/idempotency-headers/src/SeedClient.php b/seed/php-sdk/idempotency-headers/src/SeedClient.php index 572d2c216d9b..288903dc453a 100644 --- a/seed/php-sdk/idempotency-headers/src/SeedClient.php +++ b/seed/php-sdk/idempotency-headers/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var PaymentClient $payment @@ -42,8 +42,7 @@ class SeedClient public function __construct( string $token, ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'Authorization' => "Bearer $token", 'X-Fern-Language' => 'PHP', @@ -51,18 +50,18 @@ public function __construct( 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->payment = new PaymentClient($this->client, $this->options); } } diff --git a/seed/php-sdk/idempotency-headers/src/Utils/File.php b/seed/php-sdk/idempotency-headers/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/idempotency-headers/src/Utils/File.php +++ b/seed/php-sdk/idempotency-headers/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/idempotency-headers/tests/Core/Client/RawClientTest.php b/seed/php-sdk/idempotency-headers/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/idempotency-headers/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/idempotency-headers/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/idempotency-headers/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/idempotency-headers/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/idempotency-headers/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/idempotency-headers/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/idempotency-headers/tests/Core/Json/EnumTest.php b/seed/php-sdk/idempotency-headers/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/idempotency-headers/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/idempotency-headers/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/idempotency-headers/tests/Core/Json/TraitTest.php b/seed/php-sdk/idempotency-headers/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/idempotency-headers/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/idempotency-headers/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/idempotency-headers/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/idempotency-headers/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/idempotency-headers/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/idempotency-headers/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/clientName/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/imdb/clientName/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/imdb/clientName/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/imdb/clientName/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/clientName/src/Core/Client/RawClient.php b/seed/php-sdk/imdb/clientName/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/imdb/clientName/src/Core/Client/RawClient.php +++ b/seed/php-sdk/imdb/clientName/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/imdb/clientName/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/imdb/clientName/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/imdb/clientName/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/imdb/clientName/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/imdb/clientName/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/imdb/clientName/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/imdb/clientName/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/imdb/clientName/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/clientName/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/imdb/clientName/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/imdb/clientName/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/imdb/clientName/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/clientName/src/Core/Types/ArrayType.php b/seed/php-sdk/imdb/clientName/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/imdb/clientName/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/imdb/clientName/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/imdb/clientName/src/Core/Types/Constant.php b/seed/php-sdk/imdb/clientName/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/imdb/clientName/src/Core/Types/Constant.php +++ b/seed/php-sdk/imdb/clientName/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/clientName/src/Core/Types/Union.php b/seed/php-sdk/imdb/clientName/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/imdb/clientName/src/Core/Types/Union.php +++ b/seed/php-sdk/imdb/clientName/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/clientName/src/Exceptions/SeedApiException.php b/seed/php-sdk/imdb/clientName/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/imdb/clientName/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/imdb/clientName/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/imdb/clientName/src/Exceptions/SeedException.php b/seed/php-sdk/imdb/clientName/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/imdb/clientName/src/Exceptions/SeedException.php +++ b/seed/php-sdk/imdb/clientName/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/imdb/clientName/src/FernClient.php b/seed/php-sdk/imdb/clientName/src/FernClient.php index 1dec4befba8c..16e63ca45976 100644 --- a/seed/php-sdk/imdb/clientName/src/FernClient.php +++ b/seed/php-sdk/imdb/clientName/src/FernClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class FernClient +class FernClient { /** * @var ImdbClient $imdb @@ -42,29 +42,28 @@ class FernClient public function __construct( ?string $token = null, ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - if ($token != null){ + if ($token != null) { $defaultHeaders['Authorization'] = "Bearer $token"; } - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->imdb = new ImdbClient($this->client, $this->options); } } diff --git a/seed/php-sdk/imdb/clientName/src/Imdb/ImdbClient.php b/seed/php-sdk/imdb/clientName/src/Imdb/ImdbClient.php index 3d80310afd7d..80e43ceb9bca 100644 --- a/seed/php-sdk/imdb/clientName/src/Imdb/ImdbClient.php +++ b/seed/php-sdk/imdb/clientName/src/Imdb/ImdbClient.php @@ -15,7 +15,7 @@ use Psr\Http\Client\ClientExceptionInterface; use Seed\Imdb\Types\Movie; -class ImdbClient +class ImdbClient { /** * @var array{ @@ -43,11 +43,10 @@ class ImdbClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -68,7 +67,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function createMovie(CreateMovieRequest $request, ?array $options = null): string { + public function createMovie(CreateMovieRequest $request, ?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -81,15 +81,15 @@ public function createMovie(CreateMovieRequest $request, ?array $options = null) $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -121,7 +121,8 @@ public function createMovie(CreateMovieRequest $request, ?array $options = null) * @throws SeedException * @throws SeedApiException */ - public function getMovie(string $movieId, ?array $options = null): Movie { + public function getMovie(string $movieId, ?array $options = null): Movie + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -133,15 +134,15 @@ public function getMovie(string $movieId, ?array $options = null): Movie { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return Movie::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/imdb/clientName/src/Imdb/Types/CreateMovieRequest.php b/seed/php-sdk/imdb/clientName/src/Imdb/Types/CreateMovieRequest.php index b427190217fb..c2658c3de191 100644 --- a/seed/php-sdk/imdb/clientName/src/Imdb/Types/CreateMovieRequest.php +++ b/seed/php-sdk/imdb/clientName/src/Imdb/Types/CreateMovieRequest.php @@ -27,15 +27,16 @@ class CreateMovieRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->title = $values['title'];$this->rating = $values['rating']; + ) { + $this->title = $values['title']; + $this->rating = $values['rating']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/imdb/clientName/src/Imdb/Types/Movie.php b/seed/php-sdk/imdb/clientName/src/Imdb/Types/Movie.php index d1a4c73e065e..0a77bc1c88c5 100644 --- a/seed/php-sdk/imdb/clientName/src/Imdb/Types/Movie.php +++ b/seed/php-sdk/imdb/clientName/src/Imdb/Types/Movie.php @@ -34,15 +34,17 @@ class Movie extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->title = $values['title'];$this->rating = $values['rating']; + ) { + $this->id = $values['id']; + $this->title = $values['title']; + $this->rating = $values['rating']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/imdb/clientName/src/Utils/File.php b/seed/php-sdk/imdb/clientName/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/imdb/clientName/src/Utils/File.php +++ b/seed/php-sdk/imdb/clientName/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/clientName/tests/Core/Client/RawClientTest.php b/seed/php-sdk/imdb/clientName/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/imdb/clientName/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/imdb/clientName/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/imdb/clientName/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/imdb/clientName/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/imdb/clientName/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/imdb/clientName/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/clientName/tests/Core/Json/EnumTest.php b/seed/php-sdk/imdb/clientName/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/imdb/clientName/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/imdb/clientName/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/clientName/tests/Core/Json/TraitTest.php b/seed/php-sdk/imdb/clientName/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/imdb/clientName/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/imdb/clientName/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/clientName/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/imdb/clientName/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/imdb/clientName/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/imdb/clientName/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/namespace/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/imdb/namespace/src/Core/Client/BaseApiRequest.php index e9e5ebae1552..194f9d4426e1 100644 --- a/seed/php-sdk/imdb/namespace/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/imdb/namespace/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/namespace/src/Core/Client/RawClient.php b/seed/php-sdk/imdb/namespace/src/Core/Client/RawClient.php index 56ab22f3bcdd..b66ab588cd1c 100644 --- a/seed/php-sdk/imdb/namespace/src/Core/Client/RawClient.php +++ b/seed/php-sdk/imdb/namespace/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/imdb/namespace/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/imdb/namespace/src/Core/Client/RetryMiddleware.php index 6dda5699a588..3b45eddee04f 100644 --- a/seed/php-sdk/imdb/namespace/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/imdb/namespace/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/imdb/namespace/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/imdb/namespace/src/Core/Json/JsonApiRequest.php index 8d0f7e4b1c23..74e2e453cd87 100644 --- a/seed/php-sdk/imdb/namespace/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/imdb/namespace/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/namespace/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/imdb/namespace/src/Core/Multipart/MultipartFormDataPart.php index 39a380355494..9d85364d521d 100644 --- a/seed/php-sdk/imdb/namespace/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/imdb/namespace/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/namespace/src/Core/Types/ArrayType.php b/seed/php-sdk/imdb/namespace/src/Core/Types/ArrayType.php index 66008f8fcade..8d97e38328bd 100644 --- a/seed/php-sdk/imdb/namespace/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/imdb/namespace/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/imdb/namespace/src/Core/Types/Constant.php b/seed/php-sdk/imdb/namespace/src/Core/Types/Constant.php index 6fb433e30354..278d7a8490f3 100644 --- a/seed/php-sdk/imdb/namespace/src/Core/Types/Constant.php +++ b/seed/php-sdk/imdb/namespace/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/namespace/src/Core/Types/Union.php b/seed/php-sdk/imdb/namespace/src/Core/Types/Union.php index 260f47e63578..38a786dd8f56 100644 --- a/seed/php-sdk/imdb/namespace/src/Core/Types/Union.php +++ b/seed/php-sdk/imdb/namespace/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/namespace/src/Exceptions/SeedApiException.php b/seed/php-sdk/imdb/namespace/src/Exceptions/SeedApiException.php index aa705d1d81d1..a94250938503 100644 --- a/seed/php-sdk/imdb/namespace/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/imdb/namespace/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/imdb/namespace/src/Exceptions/SeedException.php b/seed/php-sdk/imdb/namespace/src/Exceptions/SeedException.php index b2975f5a55cf..a3d33748cdf6 100644 --- a/seed/php-sdk/imdb/namespace/src/Exceptions/SeedException.php +++ b/seed/php-sdk/imdb/namespace/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/imdb/namespace/src/Imdb/ImdbClient.php b/seed/php-sdk/imdb/namespace/src/Imdb/ImdbClient.php index 9df556d51ca2..be3e54fb9880 100644 --- a/seed/php-sdk/imdb/namespace/src/Imdb/ImdbClient.php +++ b/seed/php-sdk/imdb/namespace/src/Imdb/ImdbClient.php @@ -15,7 +15,7 @@ use Psr\Http\Client\ClientExceptionInterface; use Fern\Imdb\Types\Movie; -class ImdbClient +class ImdbClient { /** * @var array{ @@ -43,11 +43,10 @@ class ImdbClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -68,7 +67,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function createMovie(CreateMovieRequest $request, ?array $options = null): string { + public function createMovie(CreateMovieRequest $request, ?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -81,15 +81,15 @@ public function createMovie(CreateMovieRequest $request, ?array $options = null) $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -121,7 +121,8 @@ public function createMovie(CreateMovieRequest $request, ?array $options = null) * @throws SeedException * @throws SeedApiException */ - public function getMovie(string $movieId, ?array $options = null): Movie { + public function getMovie(string $movieId, ?array $options = null): Movie + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -133,15 +134,15 @@ public function getMovie(string $movieId, ?array $options = null): Movie { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return Movie::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/imdb/namespace/src/Imdb/Types/CreateMovieRequest.php b/seed/php-sdk/imdb/namespace/src/Imdb/Types/CreateMovieRequest.php index f92595e37146..de15158540a0 100644 --- a/seed/php-sdk/imdb/namespace/src/Imdb/Types/CreateMovieRequest.php +++ b/seed/php-sdk/imdb/namespace/src/Imdb/Types/CreateMovieRequest.php @@ -27,15 +27,16 @@ class CreateMovieRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->title = $values['title'];$this->rating = $values['rating']; + ) { + $this->title = $values['title']; + $this->rating = $values['rating']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/imdb/namespace/src/Imdb/Types/Movie.php b/seed/php-sdk/imdb/namespace/src/Imdb/Types/Movie.php index 6290a9dc969e..4954f7b30c4c 100644 --- a/seed/php-sdk/imdb/namespace/src/Imdb/Types/Movie.php +++ b/seed/php-sdk/imdb/namespace/src/Imdb/Types/Movie.php @@ -34,15 +34,17 @@ class Movie extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->title = $values['title'];$this->rating = $values['rating']; + ) { + $this->id = $values['id']; + $this->title = $values['title']; + $this->rating = $values['rating']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/imdb/namespace/src/SeedClient.php b/seed/php-sdk/imdb/namespace/src/SeedClient.php index 8e5ed1a4d154..bbbfa40a904d 100644 --- a/seed/php-sdk/imdb/namespace/src/SeedClient.php +++ b/seed/php-sdk/imdb/namespace/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Fern\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var ImdbClient $imdb @@ -42,29 +42,28 @@ class SeedClient public function __construct( ?string $token = null, ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Fern', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - if ($token != null){ + if ($token != null) { $defaultHeaders['Authorization'] = "Bearer $token"; } - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->imdb = new ImdbClient($this->client, $this->options); } } diff --git a/seed/php-sdk/imdb/namespace/src/Utils/File.php b/seed/php-sdk/imdb/namespace/src/Utils/File.php index 499378a0aad8..a1eb55e25e96 100644 --- a/seed/php-sdk/imdb/namespace/src/Utils/File.php +++ b/seed/php-sdk/imdb/namespace/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/namespace/tests/Core/Client/RawClientTest.php b/seed/php-sdk/imdb/namespace/tests/Core/Client/RawClientTest.php index 468bf2a124b7..7b3b946aef1f 100644 --- a/seed/php-sdk/imdb/namespace/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/imdb/namespace/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Fern\Core\Json\JsonSerializableType; use Fern\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/imdb/namespace/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/imdb/namespace/tests/Core/Json/AdditionalPropertiesTest.php index acc97cf6fd04..e8cbb92a8fa5 100644 --- a/seed/php-sdk/imdb/namespace/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/imdb/namespace/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/namespace/tests/Core/Json/EnumTest.php b/seed/php-sdk/imdb/namespace/tests/Core/Json/EnumTest.php index 4990f8691592..0b901cf8d372 100644 --- a/seed/php-sdk/imdb/namespace/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/imdb/namespace/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/namespace/tests/Core/Json/TraitTest.php b/seed/php-sdk/imdb/namespace/tests/Core/Json/TraitTest.php index 2aeb6e0d341a..fce7bbe997fa 100644 --- a/seed/php-sdk/imdb/namespace/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/imdb/namespace/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/namespace/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/imdb/namespace/tests/Core/Json/UnionPropertyTest.php index 51378957678b..8d3a3c44101b 100644 --- a/seed/php-sdk/imdb/namespace/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/imdb/namespace/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/no-custom-config/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/imdb/no-custom-config/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/imdb/no-custom-config/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/imdb/no-custom-config/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/no-custom-config/src/Core/Client/RawClient.php b/seed/php-sdk/imdb/no-custom-config/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/imdb/no-custom-config/src/Core/Client/RawClient.php +++ b/seed/php-sdk/imdb/no-custom-config/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/imdb/no-custom-config/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/imdb/no-custom-config/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/imdb/no-custom-config/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/imdb/no-custom-config/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/imdb/no-custom-config/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/imdb/no-custom-config/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/imdb/no-custom-config/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/imdb/no-custom-config/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/no-custom-config/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/imdb/no-custom-config/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/imdb/no-custom-config/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/imdb/no-custom-config/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/no-custom-config/src/Core/Types/ArrayType.php b/seed/php-sdk/imdb/no-custom-config/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/imdb/no-custom-config/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/imdb/no-custom-config/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/imdb/no-custom-config/src/Core/Types/Constant.php b/seed/php-sdk/imdb/no-custom-config/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/imdb/no-custom-config/src/Core/Types/Constant.php +++ b/seed/php-sdk/imdb/no-custom-config/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/no-custom-config/src/Core/Types/Union.php b/seed/php-sdk/imdb/no-custom-config/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/imdb/no-custom-config/src/Core/Types/Union.php +++ b/seed/php-sdk/imdb/no-custom-config/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/no-custom-config/src/Exceptions/SeedApiException.php b/seed/php-sdk/imdb/no-custom-config/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/imdb/no-custom-config/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/imdb/no-custom-config/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/imdb/no-custom-config/src/Exceptions/SeedException.php b/seed/php-sdk/imdb/no-custom-config/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/imdb/no-custom-config/src/Exceptions/SeedException.php +++ b/seed/php-sdk/imdb/no-custom-config/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/imdb/no-custom-config/src/Imdb/ImdbClient.php b/seed/php-sdk/imdb/no-custom-config/src/Imdb/ImdbClient.php index 3d80310afd7d..80e43ceb9bca 100644 --- a/seed/php-sdk/imdb/no-custom-config/src/Imdb/ImdbClient.php +++ b/seed/php-sdk/imdb/no-custom-config/src/Imdb/ImdbClient.php @@ -15,7 +15,7 @@ use Psr\Http\Client\ClientExceptionInterface; use Seed\Imdb\Types\Movie; -class ImdbClient +class ImdbClient { /** * @var array{ @@ -43,11 +43,10 @@ class ImdbClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -68,7 +67,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function createMovie(CreateMovieRequest $request, ?array $options = null): string { + public function createMovie(CreateMovieRequest $request, ?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -81,15 +81,15 @@ public function createMovie(CreateMovieRequest $request, ?array $options = null) $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -121,7 +121,8 @@ public function createMovie(CreateMovieRequest $request, ?array $options = null) * @throws SeedException * @throws SeedApiException */ - public function getMovie(string $movieId, ?array $options = null): Movie { + public function getMovie(string $movieId, ?array $options = null): Movie + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -133,15 +134,15 @@ public function getMovie(string $movieId, ?array $options = null): Movie { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return Movie::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/imdb/no-custom-config/src/Imdb/Types/CreateMovieRequest.php b/seed/php-sdk/imdb/no-custom-config/src/Imdb/Types/CreateMovieRequest.php index b427190217fb..c2658c3de191 100644 --- a/seed/php-sdk/imdb/no-custom-config/src/Imdb/Types/CreateMovieRequest.php +++ b/seed/php-sdk/imdb/no-custom-config/src/Imdb/Types/CreateMovieRequest.php @@ -27,15 +27,16 @@ class CreateMovieRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->title = $values['title'];$this->rating = $values['rating']; + ) { + $this->title = $values['title']; + $this->rating = $values['rating']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/imdb/no-custom-config/src/Imdb/Types/Movie.php b/seed/php-sdk/imdb/no-custom-config/src/Imdb/Types/Movie.php index d1a4c73e065e..0a77bc1c88c5 100644 --- a/seed/php-sdk/imdb/no-custom-config/src/Imdb/Types/Movie.php +++ b/seed/php-sdk/imdb/no-custom-config/src/Imdb/Types/Movie.php @@ -34,15 +34,17 @@ class Movie extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->title = $values['title'];$this->rating = $values['rating']; + ) { + $this->id = $values['id']; + $this->title = $values['title']; + $this->rating = $values['rating']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/imdb/no-custom-config/src/SeedClient.php b/seed/php-sdk/imdb/no-custom-config/src/SeedClient.php index f07f60fc71b7..367f4c5a6946 100644 --- a/seed/php-sdk/imdb/no-custom-config/src/SeedClient.php +++ b/seed/php-sdk/imdb/no-custom-config/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var ImdbClient $imdb @@ -42,29 +42,28 @@ class SeedClient public function __construct( ?string $token = null, ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - if ($token != null){ + if ($token != null) { $defaultHeaders['Authorization'] = "Bearer $token"; } - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->imdb = new ImdbClient($this->client, $this->options); } } diff --git a/seed/php-sdk/imdb/no-custom-config/src/Utils/File.php b/seed/php-sdk/imdb/no-custom-config/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/imdb/no-custom-config/src/Utils/File.php +++ b/seed/php-sdk/imdb/no-custom-config/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/no-custom-config/tests/Core/Client/RawClientTest.php b/seed/php-sdk/imdb/no-custom-config/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/imdb/no-custom-config/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/imdb/no-custom-config/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/imdb/no-custom-config/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/imdb/no-custom-config/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/imdb/no-custom-config/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/imdb/no-custom-config/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/no-custom-config/tests/Core/Json/EnumTest.php b/seed/php-sdk/imdb/no-custom-config/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/imdb/no-custom-config/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/imdb/no-custom-config/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/no-custom-config/tests/Core/Json/TraitTest.php b/seed/php-sdk/imdb/no-custom-config/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/imdb/no-custom-config/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/imdb/no-custom-config/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/no-custom-config/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/imdb/no-custom-config/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/imdb/no-custom-config/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/imdb/no-custom-config/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Core/Client/BaseApiRequest.php b/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Core/Client/BaseApiRequest.php index 572733034502..43c98ba9e498 100644 --- a/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Core/Client/RawClient.php b/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Core/Client/RawClient.php index 14afd1b85c26..767353ed1512 100644 --- a/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Core/Client/RawClient.php +++ b/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Core/Client/RetryMiddleware.php b/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Core/Client/RetryMiddleware.php index fbacc6fb7335..998954b8dfb2 100644 --- a/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Core/Json/JsonApiRequest.php b/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Core/Json/JsonApiRequest.php index c88f44e52bd2..32809c81dc67 100644 --- a/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Core/Multipart/MultipartFormDataPart.php index 4f1e4c7e9fa5..f7213c01d8b0 100644 --- a/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Core/Types/ArrayType.php b/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Core/Types/ArrayType.php index 6a49e5827074..f27ed8e96c90 100644 --- a/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Core/Types/ArrayType.php +++ b/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Core/Types/Constant.php b/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Core/Types/Constant.php index d07b612ea18b..9c4a667d8733 100644 --- a/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Core/Types/Constant.php +++ b/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Core/Types/Union.php b/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Core/Types/Union.php index 6e65dfd41207..2c97f3f8310f 100644 --- a/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Core/Types/Union.php +++ b/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Exceptions/SeedApiException.php b/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Exceptions/SeedApiException.php index 4c080d5bfd2b..c824ff0da26c 100644 --- a/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Exceptions/SeedApiException.php +++ b/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Exceptions/SeedException.php b/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Exceptions/SeedException.php index 2d8334b22d6f..82739c81ba12 100644 --- a/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Exceptions/SeedException.php +++ b/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Imdb/ImdbClient.php b/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Imdb/ImdbClient.php index d611fa82a08f..44e98d7cd22e 100644 --- a/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Imdb/ImdbClient.php +++ b/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Imdb/ImdbClient.php @@ -15,7 +15,7 @@ use Psr\Http\Client\ClientExceptionInterface; use Custom\Package\Path\Imdb\Types\Movie; -class ImdbClient +class ImdbClient { /** * @var array{ @@ -43,11 +43,10 @@ class ImdbClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -68,7 +67,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function createMovie(CreateMovieRequest $request, ?array $options = null): string { + public function createMovie(CreateMovieRequest $request, ?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -81,15 +81,15 @@ public function createMovie(CreateMovieRequest $request, ?array $options = null) $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -121,7 +121,8 @@ public function createMovie(CreateMovieRequest $request, ?array $options = null) * @throws SeedException * @throws SeedApiException */ - public function getMovie(string $movieId, ?array $options = null): Movie { + public function getMovie(string $movieId, ?array $options = null): Movie + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -133,15 +134,15 @@ public function getMovie(string $movieId, ?array $options = null): Movie { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return Movie::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Imdb/Types/CreateMovieRequest.php b/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Imdb/Types/CreateMovieRequest.php index 341fb42d5274..5c6eeb9d2416 100644 --- a/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Imdb/Types/CreateMovieRequest.php +++ b/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Imdb/Types/CreateMovieRequest.php @@ -27,15 +27,16 @@ class CreateMovieRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->title = $values['title'];$this->rating = $values['rating']; + ) { + $this->title = $values['title']; + $this->rating = $values['rating']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Imdb/Types/Movie.php b/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Imdb/Types/Movie.php index db50f0e9a4f0..78625aa89301 100644 --- a/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Imdb/Types/Movie.php +++ b/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Imdb/Types/Movie.php @@ -34,15 +34,17 @@ class Movie extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->title = $values['title'];$this->rating = $values['rating']; + ) { + $this->id = $values['id']; + $this->title = $values['title']; + $this->rating = $values['rating']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/SeedClient.php b/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/SeedClient.php index 34a7376e3159..36b61821a636 100644 --- a/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/SeedClient.php +++ b/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Custom\Package\Path\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var ImdbClient $imdb @@ -42,29 +42,28 @@ class SeedClient public function __construct( ?string $token = null, ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Custom\Package\Path', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - if ($token != null){ + if ($token != null) { $defaultHeaders['Authorization'] = "Bearer $token"; } - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->imdb = new ImdbClient($this->client, $this->options); } } diff --git a/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Utils/File.php b/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Utils/File.php index 244fccff29fb..893642ef7f95 100644 --- a/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Utils/File.php +++ b/seed/php-sdk/imdb/package-path/src/Custom/Package/Path/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/package-path/tests/Custom/Package/Path/Core/Client/RawClientTest.php b/seed/php-sdk/imdb/package-path/tests/Custom/Package/Path/Core/Client/RawClientTest.php index f08959baa9ea..8593f5775939 100644 --- a/seed/php-sdk/imdb/package-path/tests/Custom/Package/Path/Core/Client/RawClientTest.php +++ b/seed/php-sdk/imdb/package-path/tests/Custom/Package/Path/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Custom\Package\Path\Core\Json\JsonSerializableType; use Custom\Package\Path\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/imdb/package-path/tests/Custom/Package/Path/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/imdb/package-path/tests/Custom/Package/Path/Core/Json/AdditionalPropertiesTest.php index a60a6ccfb6a0..b4b8b726512e 100644 --- a/seed/php-sdk/imdb/package-path/tests/Custom/Package/Path/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/imdb/package-path/tests/Custom/Package/Path/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/package-path/tests/Custom/Package/Path/Core/Json/EnumTest.php b/seed/php-sdk/imdb/package-path/tests/Custom/Package/Path/Core/Json/EnumTest.php index 1214bbf92eec..c0a85f437aff 100644 --- a/seed/php-sdk/imdb/package-path/tests/Custom/Package/Path/Core/Json/EnumTest.php +++ b/seed/php-sdk/imdb/package-path/tests/Custom/Package/Path/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/package-path/tests/Custom/Package/Path/Core/Json/TraitTest.php b/seed/php-sdk/imdb/package-path/tests/Custom/Package/Path/Core/Json/TraitTest.php index 66e623571818..861d39618902 100644 --- a/seed/php-sdk/imdb/package-path/tests/Custom/Package/Path/Core/Json/TraitTest.php +++ b/seed/php-sdk/imdb/package-path/tests/Custom/Package/Path/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/package-path/tests/Custom/Package/Path/Core/Json/UnionPropertyTest.php b/seed/php-sdk/imdb/package-path/tests/Custom/Package/Path/Core/Json/UnionPropertyTest.php index 49671b8a9c77..cc10dee7e701 100644 --- a/seed/php-sdk/imdb/package-path/tests/Custom/Package/Path/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/imdb/package-path/tests/Custom/Package/Path/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/packageName/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/imdb/packageName/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/imdb/packageName/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/imdb/packageName/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/packageName/src/Core/Client/RawClient.php b/seed/php-sdk/imdb/packageName/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/imdb/packageName/src/Core/Client/RawClient.php +++ b/seed/php-sdk/imdb/packageName/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/imdb/packageName/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/imdb/packageName/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/imdb/packageName/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/imdb/packageName/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/imdb/packageName/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/imdb/packageName/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/imdb/packageName/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/imdb/packageName/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/packageName/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/imdb/packageName/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/imdb/packageName/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/imdb/packageName/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/packageName/src/Core/Types/ArrayType.php b/seed/php-sdk/imdb/packageName/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/imdb/packageName/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/imdb/packageName/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/imdb/packageName/src/Core/Types/Constant.php b/seed/php-sdk/imdb/packageName/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/imdb/packageName/src/Core/Types/Constant.php +++ b/seed/php-sdk/imdb/packageName/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/packageName/src/Core/Types/Union.php b/seed/php-sdk/imdb/packageName/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/imdb/packageName/src/Core/Types/Union.php +++ b/seed/php-sdk/imdb/packageName/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/packageName/src/Exceptions/SeedApiException.php b/seed/php-sdk/imdb/packageName/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/imdb/packageName/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/imdb/packageName/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/imdb/packageName/src/Exceptions/SeedException.php b/seed/php-sdk/imdb/packageName/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/imdb/packageName/src/Exceptions/SeedException.php +++ b/seed/php-sdk/imdb/packageName/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/imdb/packageName/src/Imdb/ImdbClient.php b/seed/php-sdk/imdb/packageName/src/Imdb/ImdbClient.php index 3d80310afd7d..80e43ceb9bca 100644 --- a/seed/php-sdk/imdb/packageName/src/Imdb/ImdbClient.php +++ b/seed/php-sdk/imdb/packageName/src/Imdb/ImdbClient.php @@ -15,7 +15,7 @@ use Psr\Http\Client\ClientExceptionInterface; use Seed\Imdb\Types\Movie; -class ImdbClient +class ImdbClient { /** * @var array{ @@ -43,11 +43,10 @@ class ImdbClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -68,7 +67,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function createMovie(CreateMovieRequest $request, ?array $options = null): string { + public function createMovie(CreateMovieRequest $request, ?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -81,15 +81,15 @@ public function createMovie(CreateMovieRequest $request, ?array $options = null) $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -121,7 +121,8 @@ public function createMovie(CreateMovieRequest $request, ?array $options = null) * @throws SeedException * @throws SeedApiException */ - public function getMovie(string $movieId, ?array $options = null): Movie { + public function getMovie(string $movieId, ?array $options = null): Movie + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -133,15 +134,15 @@ public function getMovie(string $movieId, ?array $options = null): Movie { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return Movie::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/imdb/packageName/src/Imdb/Types/CreateMovieRequest.php b/seed/php-sdk/imdb/packageName/src/Imdb/Types/CreateMovieRequest.php index b427190217fb..c2658c3de191 100644 --- a/seed/php-sdk/imdb/packageName/src/Imdb/Types/CreateMovieRequest.php +++ b/seed/php-sdk/imdb/packageName/src/Imdb/Types/CreateMovieRequest.php @@ -27,15 +27,16 @@ class CreateMovieRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->title = $values['title'];$this->rating = $values['rating']; + ) { + $this->title = $values['title']; + $this->rating = $values['rating']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/imdb/packageName/src/Imdb/Types/Movie.php b/seed/php-sdk/imdb/packageName/src/Imdb/Types/Movie.php index d1a4c73e065e..0a77bc1c88c5 100644 --- a/seed/php-sdk/imdb/packageName/src/Imdb/Types/Movie.php +++ b/seed/php-sdk/imdb/packageName/src/Imdb/Types/Movie.php @@ -34,15 +34,17 @@ class Movie extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->title = $values['title'];$this->rating = $values['rating']; + ) { + $this->id = $values['id']; + $this->title = $values['title']; + $this->rating = $values['rating']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/imdb/packageName/src/SeedClient.php b/seed/php-sdk/imdb/packageName/src/SeedClient.php index d99af2d8ea90..bd1047624d5c 100644 --- a/seed/php-sdk/imdb/packageName/src/SeedClient.php +++ b/seed/php-sdk/imdb/packageName/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var ImdbClient $imdb @@ -42,29 +42,28 @@ class SeedClient public function __construct( ?string $token = null, ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'fern-api/imdb-php/0.0.1', ]; - if ($token != null){ + if ($token != null) { $defaultHeaders['Authorization'] = "Bearer $token"; } - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->imdb = new ImdbClient($this->client, $this->options); } } diff --git a/seed/php-sdk/imdb/packageName/src/Utils/File.php b/seed/php-sdk/imdb/packageName/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/imdb/packageName/src/Utils/File.php +++ b/seed/php-sdk/imdb/packageName/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/packageName/tests/Core/Client/RawClientTest.php b/seed/php-sdk/imdb/packageName/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/imdb/packageName/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/imdb/packageName/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/imdb/packageName/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/imdb/packageName/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/imdb/packageName/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/imdb/packageName/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/packageName/tests/Core/Json/EnumTest.php b/seed/php-sdk/imdb/packageName/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/imdb/packageName/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/imdb/packageName/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/packageName/tests/Core/Json/TraitTest.php b/seed/php-sdk/imdb/packageName/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/imdb/packageName/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/imdb/packageName/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/packageName/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/imdb/packageName/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/imdb/packageName/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/imdb/packageName/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/private/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/imdb/private/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/imdb/private/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/imdb/private/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/private/src/Core/Client/RawClient.php b/seed/php-sdk/imdb/private/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/imdb/private/src/Core/Client/RawClient.php +++ b/seed/php-sdk/imdb/private/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/imdb/private/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/imdb/private/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/imdb/private/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/imdb/private/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/imdb/private/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/imdb/private/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/imdb/private/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/imdb/private/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/private/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/imdb/private/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/imdb/private/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/imdb/private/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/private/src/Core/Types/ArrayType.php b/seed/php-sdk/imdb/private/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/imdb/private/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/imdb/private/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/imdb/private/src/Core/Types/Constant.php b/seed/php-sdk/imdb/private/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/imdb/private/src/Core/Types/Constant.php +++ b/seed/php-sdk/imdb/private/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/private/src/Core/Types/Union.php b/seed/php-sdk/imdb/private/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/imdb/private/src/Core/Types/Union.php +++ b/seed/php-sdk/imdb/private/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/private/src/Exceptions/SeedApiException.php b/seed/php-sdk/imdb/private/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/imdb/private/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/imdb/private/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/imdb/private/src/Exceptions/SeedException.php b/seed/php-sdk/imdb/private/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/imdb/private/src/Exceptions/SeedException.php +++ b/seed/php-sdk/imdb/private/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/imdb/private/src/Imdb/ImdbClient.php b/seed/php-sdk/imdb/private/src/Imdb/ImdbClient.php index 3d80310afd7d..80e43ceb9bca 100644 --- a/seed/php-sdk/imdb/private/src/Imdb/ImdbClient.php +++ b/seed/php-sdk/imdb/private/src/Imdb/ImdbClient.php @@ -15,7 +15,7 @@ use Psr\Http\Client\ClientExceptionInterface; use Seed\Imdb\Types\Movie; -class ImdbClient +class ImdbClient { /** * @var array{ @@ -43,11 +43,10 @@ class ImdbClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -68,7 +67,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function createMovie(CreateMovieRequest $request, ?array $options = null): string { + public function createMovie(CreateMovieRequest $request, ?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -81,15 +81,15 @@ public function createMovie(CreateMovieRequest $request, ?array $options = null) $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -121,7 +121,8 @@ public function createMovie(CreateMovieRequest $request, ?array $options = null) * @throws SeedException * @throws SeedApiException */ - public function getMovie(string $movieId, ?array $options = null): Movie { + public function getMovie(string $movieId, ?array $options = null): Movie + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -133,15 +134,15 @@ public function getMovie(string $movieId, ?array $options = null): Movie { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return Movie::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/imdb/private/src/Imdb/Types/CreateMovieRequest.php b/seed/php-sdk/imdb/private/src/Imdb/Types/CreateMovieRequest.php index 86f3b78964da..7232e5565ae0 100644 --- a/seed/php-sdk/imdb/private/src/Imdb/Types/CreateMovieRequest.php +++ b/seed/php-sdk/imdb/private/src/Imdb/Types/CreateMovieRequest.php @@ -27,39 +27,50 @@ class CreateMovieRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->title = $values['title'];$this->rating = $values['rating']; + ) { + $this->title = $values['title']; + $this->rating = $values['rating']; } /** * @return string */ - public function getTitle(): string { - return $this->title;} + public function getTitle(): string + { + return $this->title; + } /** * @param string $value */ - public function setTitle(string $value): self { - $this->title = $value;return $this;} + public function setTitle(string $value): self + { + $this->title = $value; + return $this; + } /** * @return float */ - public function getRating(): float { - return $this->rating;} + public function getRating(): float + { + return $this->rating; + } /** * @param float $value */ - public function setRating(float $value): self { - $this->rating = $value;return $this;} + public function setRating(float $value): self + { + $this->rating = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/imdb/private/src/Imdb/Types/Movie.php b/seed/php-sdk/imdb/private/src/Imdb/Types/Movie.php index c54272019d10..2dec77ca36bd 100644 --- a/seed/php-sdk/imdb/private/src/Imdb/Types/Movie.php +++ b/seed/php-sdk/imdb/private/src/Imdb/Types/Movie.php @@ -34,51 +34,68 @@ class Movie extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->title = $values['title'];$this->rating = $values['rating']; + ) { + $this->id = $values['id']; + $this->title = $values['title']; + $this->rating = $values['rating']; } /** * @return string */ - public function getId(): string { - return $this->id;} + public function getId(): string + { + return $this->id; + } /** * @param string $value */ - public function setId(string $value): self { - $this->id = $value;return $this;} + public function setId(string $value): self + { + $this->id = $value; + return $this; + } /** * @return string */ - public function getTitle(): string { - return $this->title;} + public function getTitle(): string + { + return $this->title; + } /** * @param string $value */ - public function setTitle(string $value): self { - $this->title = $value;return $this;} + public function setTitle(string $value): self + { + $this->title = $value; + return $this; + } /** * @return float */ - public function getRating(): float { - return $this->rating;} + public function getRating(): float + { + return $this->rating; + } /** * @param float $value */ - public function setRating(float $value): self { - $this->rating = $value;return $this;} + public function setRating(float $value): self + { + $this->rating = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/imdb/private/src/SeedClient.php b/seed/php-sdk/imdb/private/src/SeedClient.php index f07f60fc71b7..367f4c5a6946 100644 --- a/seed/php-sdk/imdb/private/src/SeedClient.php +++ b/seed/php-sdk/imdb/private/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var ImdbClient $imdb @@ -42,29 +42,28 @@ class SeedClient public function __construct( ?string $token = null, ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - if ($token != null){ + if ($token != null) { $defaultHeaders['Authorization'] = "Bearer $token"; } - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->imdb = new ImdbClient($this->client, $this->options); } } diff --git a/seed/php-sdk/imdb/private/src/Utils/File.php b/seed/php-sdk/imdb/private/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/imdb/private/src/Utils/File.php +++ b/seed/php-sdk/imdb/private/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/private/tests/Core/Client/RawClientTest.php b/seed/php-sdk/imdb/private/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/imdb/private/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/imdb/private/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/imdb/private/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/imdb/private/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/imdb/private/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/imdb/private/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/private/tests/Core/Json/EnumTest.php b/seed/php-sdk/imdb/private/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/imdb/private/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/imdb/private/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/private/tests/Core/Json/TraitTest.php b/seed/php-sdk/imdb/private/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/imdb/private/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/imdb/private/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/imdb/private/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/imdb/private/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/imdb/private/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/imdb/private/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/inferred-auth-explicit/src/Auth/AuthClient.php b/seed/php-sdk/inferred-auth-explicit/src/Auth/AuthClient.php index 7ad1292f95fb..785e797ce7d7 100644 --- a/seed/php-sdk/inferred-auth-explicit/src/Auth/AuthClient.php +++ b/seed/php-sdk/inferred-auth-explicit/src/Auth/AuthClient.php @@ -15,7 +15,7 @@ use Psr\Http\Client\ClientExceptionInterface; use Seed\Auth\Requests\RefreshTokenRequest; -class AuthClient +class AuthClient { /** * @var array{ @@ -43,11 +43,10 @@ class AuthClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -66,7 +65,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getTokenWithClientCredentials(GetTokenRequest $request, ?array $options = null): TokenResponse { + public function getTokenWithClientCredentials(GetTokenRequest $request, ?array $options = null): TokenResponse + { $options = array_merge($this->options, $options ?? []); $headers = []; $headers['X-Api-Key'] = $request->xApiKey; @@ -82,15 +82,15 @@ public function getTokenWithClientCredentials(GetTokenRequest $request, ?array $ $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return TokenResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -122,7 +122,8 @@ public function getTokenWithClientCredentials(GetTokenRequest $request, ?array $ * @throws SeedException * @throws SeedApiException */ - public function refreshToken(RefreshTokenRequest $request, ?array $options = null): TokenResponse { + public function refreshToken(RefreshTokenRequest $request, ?array $options = null): TokenResponse + { $options = array_merge($this->options, $options ?? []); $headers = []; $headers['X-Api-Key'] = $request->xApiKey; @@ -138,15 +139,15 @@ public function refreshToken(RefreshTokenRequest $request, ?array $options = nul $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return TokenResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/inferred-auth-explicit/src/Auth/Requests/GetTokenRequest.php b/seed/php-sdk/inferred-auth-explicit/src/Auth/Requests/GetTokenRequest.php index bc4ae17c29a0..697143e98133 100644 --- a/seed/php-sdk/inferred-auth-explicit/src/Auth/Requests/GetTokenRequest.php +++ b/seed/php-sdk/inferred-auth-explicit/src/Auth/Requests/GetTokenRequest.php @@ -54,8 +54,12 @@ class GetTokenRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->xApiKey = $values['xApiKey'];$this->clientId = $values['clientId'];$this->clientSecret = $values['clientSecret'];$this->audience = $values['audience'];$this->grantType = $values['grantType'];$this->scope = $values['scope'] ?? null; + ) { + $this->xApiKey = $values['xApiKey']; + $this->clientId = $values['clientId']; + $this->clientSecret = $values['clientSecret']; + $this->audience = $values['audience']; + $this->grantType = $values['grantType']; + $this->scope = $values['scope'] ?? null; } } diff --git a/seed/php-sdk/inferred-auth-explicit/src/Auth/Requests/RefreshTokenRequest.php b/seed/php-sdk/inferred-auth-explicit/src/Auth/Requests/RefreshTokenRequest.php index 4bdcbb368671..295e771fba01 100644 --- a/seed/php-sdk/inferred-auth-explicit/src/Auth/Requests/RefreshTokenRequest.php +++ b/seed/php-sdk/inferred-auth-explicit/src/Auth/Requests/RefreshTokenRequest.php @@ -61,8 +61,13 @@ class RefreshTokenRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->xApiKey = $values['xApiKey'];$this->clientId = $values['clientId'];$this->clientSecret = $values['clientSecret'];$this->refreshToken = $values['refreshToken'];$this->audience = $values['audience'];$this->grantType = $values['grantType'];$this->scope = $values['scope'] ?? null; + ) { + $this->xApiKey = $values['xApiKey']; + $this->clientId = $values['clientId']; + $this->clientSecret = $values['clientSecret']; + $this->refreshToken = $values['refreshToken']; + $this->audience = $values['audience']; + $this->grantType = $values['grantType']; + $this->scope = $values['scope'] ?? null; } } diff --git a/seed/php-sdk/inferred-auth-explicit/src/Auth/Types/TokenResponse.php b/seed/php-sdk/inferred-auth-explicit/src/Auth/Types/TokenResponse.php index 2a7c2ec254a1..f5379b9051d4 100644 --- a/seed/php-sdk/inferred-auth-explicit/src/Auth/Types/TokenResponse.php +++ b/seed/php-sdk/inferred-auth-explicit/src/Auth/Types/TokenResponse.php @@ -37,15 +37,17 @@ class TokenResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->accessToken = $values['accessToken'];$this->expiresIn = $values['expiresIn'];$this->refreshToken = $values['refreshToken'] ?? null; + ) { + $this->accessToken = $values['accessToken']; + $this->expiresIn = $values['expiresIn']; + $this->refreshToken = $values['refreshToken'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/inferred-auth-explicit/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/inferred-auth-explicit/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/inferred-auth-explicit/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/inferred-auth-explicit/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/inferred-auth-explicit/src/Core/Client/RawClient.php b/seed/php-sdk/inferred-auth-explicit/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/inferred-auth-explicit/src/Core/Client/RawClient.php +++ b/seed/php-sdk/inferred-auth-explicit/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/inferred-auth-explicit/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/inferred-auth-explicit/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/inferred-auth-explicit/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/inferred-auth-explicit/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/inferred-auth-explicit/src/Core/InferredAuthProvider.php b/seed/php-sdk/inferred-auth-explicit/src/Core/InferredAuthProvider.php index d58dbf3af78e..819fe7f93bee 100644 --- a/seed/php-sdk/inferred-auth-explicit/src/Core/InferredAuthProvider.php +++ b/seed/php-sdk/inferred-auth-explicit/src/Core/InferredAuthProvider.php @@ -10,7 +10,7 @@ * The InferredAuthProvider retrieves an access token from the configured token endpoint. * The access token is then used as the bearer token in every authenticated request. */ -class InferredAuthProvider +class InferredAuthProvider { /** * @var int $BUFFER_IN_MINUTES @@ -41,11 +41,10 @@ class InferredAuthProvider * @param AuthClient $authClient The client used to retrieve the access token. * @param array $options The options containing credentials for the token endpoint. */ - function __construct( + public function __construct( AuthClient $authClient, array $options, - ) - { + ) { $this->authClient = $authClient; $this->options = $options; $this->accessToken = null; @@ -57,8 +56,9 @@ function __construct( * * @return string */ - public function getToken(): string { - if ($this->accessToken !== null && ($this->expiresAt === null || $this->expiresAt > new DateTime())){ + public function getToken(): string + { + if ($this->accessToken !== null && ($this->expiresAt === null || $this->expiresAt > new DateTime())) { return $this->accessToken; } return $this->refresh(); @@ -69,7 +69,8 @@ public function getToken(): string { * * @return array */ - public function getAuthHeaders(): array { + public function getAuthHeaders(): array + { $token = $this->getToken(); return [ 'Authorization' => "Bearer " . $token, @@ -81,7 +82,8 @@ public function getAuthHeaders(): array { * * @return string */ - private function refresh(): string { + private function refresh(): string + { /** @var array{xApiKey: string, clientId: string, clientSecret: string, audience: 'https://api.example.com', grantType: 'client_credentials', scope?: string|null} $values */ $values = [ 'xApiKey' => $this->options['xApiKey'] ?? '', @@ -109,7 +111,8 @@ private function refresh(): string { * @param int $bufferInMinutes The buffer time in minutes to subtract from the expiration. * @return DateTime */ - private function getExpiresAt(int $expiresInSeconds, int $bufferInMinutes): DateTime { + private function getExpiresAt(int $expiresInSeconds, int $bufferInMinutes): DateTime + { $now = new DateTime(); $expiresInSecondsWithBuffer = $expiresInSeconds - ($bufferInMinutes * 60); $now->modify("+{$expiresInSecondsWithBuffer} seconds"); diff --git a/seed/php-sdk/inferred-auth-explicit/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/inferred-auth-explicit/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/inferred-auth-explicit/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/inferred-auth-explicit/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/inferred-auth-explicit/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/inferred-auth-explicit/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/inferred-auth-explicit/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/inferred-auth-explicit/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/inferred-auth-explicit/src/Core/Types/ArrayType.php b/seed/php-sdk/inferred-auth-explicit/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/inferred-auth-explicit/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/inferred-auth-explicit/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/inferred-auth-explicit/src/Core/Types/Constant.php b/seed/php-sdk/inferred-auth-explicit/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/inferred-auth-explicit/src/Core/Types/Constant.php +++ b/seed/php-sdk/inferred-auth-explicit/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/inferred-auth-explicit/src/Core/Types/Union.php b/seed/php-sdk/inferred-auth-explicit/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/inferred-auth-explicit/src/Core/Types/Union.php +++ b/seed/php-sdk/inferred-auth-explicit/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/inferred-auth-explicit/src/Exceptions/SeedApiException.php b/seed/php-sdk/inferred-auth-explicit/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/inferred-auth-explicit/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/inferred-auth-explicit/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/inferred-auth-explicit/src/Exceptions/SeedException.php b/seed/php-sdk/inferred-auth-explicit/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/inferred-auth-explicit/src/Exceptions/SeedException.php +++ b/seed/php-sdk/inferred-auth-explicit/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/inferred-auth-explicit/src/Nested/Api/ApiClient.php b/seed/php-sdk/inferred-auth-explicit/src/Nested/Api/ApiClient.php index 55aa05a36ce7..ce6b4aec2e42 100644 --- a/seed/php-sdk/inferred-auth-explicit/src/Nested/Api/ApiClient.php +++ b/seed/php-sdk/inferred-auth-explicit/src/Nested/Api/ApiClient.php @@ -11,7 +11,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class ApiClient +class ApiClient { /** * @var array{ @@ -39,11 +39,10 @@ class ApiClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -60,7 +59,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getSomething(?array $options = null): void { + public function getSomething(?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -72,12 +72,12 @@ public function getSomething(?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/inferred-auth-explicit/src/Nested/NestedClient.php b/seed/php-sdk/inferred-auth-explicit/src/Nested/NestedClient.php index 7857c019672d..26f17dd0c9ac 100644 --- a/seed/php-sdk/inferred-auth-explicit/src/Nested/NestedClient.php +++ b/seed/php-sdk/inferred-auth-explicit/src/Nested/NestedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class NestedClient +class NestedClient { /** * @var ApiClient $api @@ -39,11 +39,10 @@ class NestedClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; $this->api = new ApiClient($this->client, $this->options); diff --git a/seed/php-sdk/inferred-auth-explicit/src/NestedNoAuth/Api/ApiClient.php b/seed/php-sdk/inferred-auth-explicit/src/NestedNoAuth/Api/ApiClient.php index 6141523e925e..2bd84c067499 100644 --- a/seed/php-sdk/inferred-auth-explicit/src/NestedNoAuth/Api/ApiClient.php +++ b/seed/php-sdk/inferred-auth-explicit/src/NestedNoAuth/Api/ApiClient.php @@ -11,7 +11,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class ApiClient +class ApiClient { /** * @var array{ @@ -39,11 +39,10 @@ class ApiClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -60,7 +59,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getSomething(?array $options = null): void { + public function getSomething(?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -72,12 +72,12 @@ public function getSomething(?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/inferred-auth-explicit/src/NestedNoAuth/NestedNoAuthClient.php b/seed/php-sdk/inferred-auth-explicit/src/NestedNoAuth/NestedNoAuthClient.php index c9f5659a49b0..f7c33619f24c 100644 --- a/seed/php-sdk/inferred-auth-explicit/src/NestedNoAuth/NestedNoAuthClient.php +++ b/seed/php-sdk/inferred-auth-explicit/src/NestedNoAuth/NestedNoAuthClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class NestedNoAuthClient +class NestedNoAuthClient { /** * @var ApiClient $api @@ -39,11 +39,10 @@ class NestedNoAuthClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; $this->api = new ApiClient($this->client, $this->options); diff --git a/seed/php-sdk/inferred-auth-explicit/src/SeedClient.php b/seed/php-sdk/inferred-auth-explicit/src/SeedClient.php index 6763442e4fcc..1ca739e04a7d 100644 --- a/seed/php-sdk/inferred-auth-explicit/src/SeedClient.php +++ b/seed/php-sdk/inferred-auth-explicit/src/SeedClient.php @@ -10,7 +10,7 @@ use Seed\Core\Client\RawClient; use Seed\Core\InferredAuthProvider; -class SeedClient +class SeedClient { /** * @var AuthClient $auth @@ -48,6 +48,11 @@ class SeedClient */ private RawClient $client; + /** + * @var InferredAuthProvider $inferredAuthProvider + */ + private InferredAuthProvider $inferredAuthProvider; + /** * @param ?string $clientId * @param ?string $clientSecret @@ -67,20 +72,19 @@ public function __construct( ?string $scope = null, ?string $xApiKey = null, ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - if ($xApiKey != null){ + if ($xApiKey != null) { $defaultHeaders['X-Api-Key'] = $xApiKey; } - + $this->options = $options ?? []; - + $authRawClient = new RawClient(['headers' => []]); $authClient = new AuthClient($authRawClient); $inferredAuthOptions = [ @@ -91,19 +95,20 @@ public function __construct( 'scope' => $scope ?? '', 'xApiKey' => $xApiKey ?? '', ]; - $inferredAuthProvider = new InferredAuthProvider($authClient, $inferredAuthOptions); - $authHeaders = $inferredAuthProvider->getAuthHeaders(); - - $defaultHeaders = array_merge($defaultHeaders, $authHeaders); + $this->inferredAuthProvider = new InferredAuthProvider($authClient, $inferredAuthOptions); + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + + $this->options['getAuthHeaders'] = fn () => + $this->inferredAuthProvider->getAuthHeaders(); + $this->client = new RawClient( options: $this->options, ); - + $this->auth = new AuthClient($this->client, $this->options); $this->nestedNoAuth = new NestedNoAuthClient($this->client, $this->options); $this->nested = new NestedClient($this->client, $this->options); diff --git a/seed/php-sdk/inferred-auth-explicit/src/Simple/SimpleClient.php b/seed/php-sdk/inferred-auth-explicit/src/Simple/SimpleClient.php index 50b6b54f52c0..7a8df43cf28d 100644 --- a/seed/php-sdk/inferred-auth-explicit/src/Simple/SimpleClient.php +++ b/seed/php-sdk/inferred-auth-explicit/src/Simple/SimpleClient.php @@ -11,7 +11,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class SimpleClient +class SimpleClient { /** * @var array{ @@ -39,11 +39,10 @@ class SimpleClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -60,7 +59,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getSomething(?array $options = null): void { + public function getSomething(?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -72,12 +72,12 @@ public function getSomething(?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/inferred-auth-explicit/src/Utils/File.php b/seed/php-sdk/inferred-auth-explicit/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/inferred-auth-explicit/src/Utils/File.php +++ b/seed/php-sdk/inferred-auth-explicit/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/inferred-auth-explicit/tests/Core/Client/RawClientTest.php b/seed/php-sdk/inferred-auth-explicit/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/inferred-auth-explicit/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/inferred-auth-explicit/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/inferred-auth-explicit/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/inferred-auth-explicit/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/inferred-auth-explicit/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/inferred-auth-explicit/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/inferred-auth-explicit/tests/Core/Json/EnumTest.php b/seed/php-sdk/inferred-auth-explicit/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/inferred-auth-explicit/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/inferred-auth-explicit/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/inferred-auth-explicit/tests/Core/Json/TraitTest.php b/seed/php-sdk/inferred-auth-explicit/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/inferred-auth-explicit/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/inferred-auth-explicit/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/inferred-auth-explicit/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/inferred-auth-explicit/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/inferred-auth-explicit/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/inferred-auth-explicit/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Auth/AuthClient.php b/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Auth/AuthClient.php index 7ad1292f95fb..785e797ce7d7 100644 --- a/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Auth/AuthClient.php +++ b/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Auth/AuthClient.php @@ -15,7 +15,7 @@ use Psr\Http\Client\ClientExceptionInterface; use Seed\Auth\Requests\RefreshTokenRequest; -class AuthClient +class AuthClient { /** * @var array{ @@ -43,11 +43,10 @@ class AuthClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -66,7 +65,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getTokenWithClientCredentials(GetTokenRequest $request, ?array $options = null): TokenResponse { + public function getTokenWithClientCredentials(GetTokenRequest $request, ?array $options = null): TokenResponse + { $options = array_merge($this->options, $options ?? []); $headers = []; $headers['X-Api-Key'] = $request->xApiKey; @@ -82,15 +82,15 @@ public function getTokenWithClientCredentials(GetTokenRequest $request, ?array $ $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return TokenResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -122,7 +122,8 @@ public function getTokenWithClientCredentials(GetTokenRequest $request, ?array $ * @throws SeedException * @throws SeedApiException */ - public function refreshToken(RefreshTokenRequest $request, ?array $options = null): TokenResponse { + public function refreshToken(RefreshTokenRequest $request, ?array $options = null): TokenResponse + { $options = array_merge($this->options, $options ?? []); $headers = []; $headers['X-Api-Key'] = $request->xApiKey; @@ -138,15 +139,15 @@ public function refreshToken(RefreshTokenRequest $request, ?array $options = nul $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return TokenResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Auth/Requests/GetTokenRequest.php b/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Auth/Requests/GetTokenRequest.php index bc4ae17c29a0..697143e98133 100644 --- a/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Auth/Requests/GetTokenRequest.php +++ b/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Auth/Requests/GetTokenRequest.php @@ -54,8 +54,12 @@ class GetTokenRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->xApiKey = $values['xApiKey'];$this->clientId = $values['clientId'];$this->clientSecret = $values['clientSecret'];$this->audience = $values['audience'];$this->grantType = $values['grantType'];$this->scope = $values['scope'] ?? null; + ) { + $this->xApiKey = $values['xApiKey']; + $this->clientId = $values['clientId']; + $this->clientSecret = $values['clientSecret']; + $this->audience = $values['audience']; + $this->grantType = $values['grantType']; + $this->scope = $values['scope'] ?? null; } } diff --git a/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Auth/Requests/RefreshTokenRequest.php b/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Auth/Requests/RefreshTokenRequest.php index 4bdcbb368671..295e771fba01 100644 --- a/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Auth/Requests/RefreshTokenRequest.php +++ b/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Auth/Requests/RefreshTokenRequest.php @@ -61,8 +61,13 @@ class RefreshTokenRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->xApiKey = $values['xApiKey'];$this->clientId = $values['clientId'];$this->clientSecret = $values['clientSecret'];$this->refreshToken = $values['refreshToken'];$this->audience = $values['audience'];$this->grantType = $values['grantType'];$this->scope = $values['scope'] ?? null; + ) { + $this->xApiKey = $values['xApiKey']; + $this->clientId = $values['clientId']; + $this->clientSecret = $values['clientSecret']; + $this->refreshToken = $values['refreshToken']; + $this->audience = $values['audience']; + $this->grantType = $values['grantType']; + $this->scope = $values['scope'] ?? null; } } diff --git a/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Auth/Types/TokenResponse.php b/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Auth/Types/TokenResponse.php index 7727dea3a96e..d175c0311eb2 100644 --- a/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Auth/Types/TokenResponse.php +++ b/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Auth/Types/TokenResponse.php @@ -30,15 +30,16 @@ class TokenResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->accessToken = $values['accessToken'];$this->refreshToken = $values['refreshToken'] ?? null; + ) { + $this->accessToken = $values['accessToken']; + $this->refreshToken = $values['refreshToken'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Core/Client/RawClient.php b/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Core/Client/RawClient.php +++ b/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Core/InferredAuthProvider.php b/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Core/InferredAuthProvider.php index 4ed88b38733d..16fe88230618 100644 --- a/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Core/InferredAuthProvider.php +++ b/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Core/InferredAuthProvider.php @@ -9,7 +9,7 @@ * The InferredAuthProvider retrieves an access token from the configured token endpoint. * The access token is then used as the bearer token in every authenticated request. */ -class InferredAuthProvider +class InferredAuthProvider { /** * @var AuthClient $authClient @@ -30,11 +30,10 @@ class InferredAuthProvider * @param AuthClient $authClient The client used to retrieve the access token. * @param array $options The options containing credentials for the token endpoint. */ - function __construct( + public function __construct( AuthClient $authClient, array $options, - ) - { + ) { $this->authClient = $authClient; $this->options = $options; $this->accessToken = null; @@ -45,8 +44,9 @@ function __construct( * * @return string */ - public function getToken(): string { - if ($this->accessToken !== null){ + public function getToken(): string + { + if ($this->accessToken !== null) { return $this->accessToken; } return $this->refresh(); @@ -57,7 +57,8 @@ public function getToken(): string { * * @return array */ - public function getAuthHeaders(): array { + public function getAuthHeaders(): array + { $token = $this->getToken(); return [ 'Authorization' => "Bearer " . $token, @@ -69,7 +70,8 @@ public function getAuthHeaders(): array { * * @return string */ - private function refresh(): string { + private function refresh(): string + { /** @var array{xApiKey: string, clientId: string, clientSecret: string, audience: 'https://api.example.com', grantType: 'client_credentials', scope?: string|null} $values */ $values = [ 'xApiKey' => $this->options['xApiKey'] ?? '', diff --git a/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Core/Types/ArrayType.php b/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Core/Types/Constant.php b/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Core/Types/Constant.php +++ b/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Core/Types/Union.php b/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Core/Types/Union.php +++ b/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Exceptions/SeedApiException.php b/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Exceptions/SeedException.php b/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Exceptions/SeedException.php +++ b/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Nested/Api/ApiClient.php b/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Nested/Api/ApiClient.php index 55aa05a36ce7..ce6b4aec2e42 100644 --- a/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Nested/Api/ApiClient.php +++ b/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Nested/Api/ApiClient.php @@ -11,7 +11,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class ApiClient +class ApiClient { /** * @var array{ @@ -39,11 +39,10 @@ class ApiClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -60,7 +59,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getSomething(?array $options = null): void { + public function getSomething(?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -72,12 +72,12 @@ public function getSomething(?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Nested/NestedClient.php b/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Nested/NestedClient.php index 7857c019672d..26f17dd0c9ac 100644 --- a/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Nested/NestedClient.php +++ b/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Nested/NestedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class NestedClient +class NestedClient { /** * @var ApiClient $api @@ -39,11 +39,10 @@ class NestedClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; $this->api = new ApiClient($this->client, $this->options); diff --git a/seed/php-sdk/inferred-auth-implicit-no-expiry/src/NestedNoAuth/Api/ApiClient.php b/seed/php-sdk/inferred-auth-implicit-no-expiry/src/NestedNoAuth/Api/ApiClient.php index 6141523e925e..2bd84c067499 100644 --- a/seed/php-sdk/inferred-auth-implicit-no-expiry/src/NestedNoAuth/Api/ApiClient.php +++ b/seed/php-sdk/inferred-auth-implicit-no-expiry/src/NestedNoAuth/Api/ApiClient.php @@ -11,7 +11,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class ApiClient +class ApiClient { /** * @var array{ @@ -39,11 +39,10 @@ class ApiClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -60,7 +59,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getSomething(?array $options = null): void { + public function getSomething(?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -72,12 +72,12 @@ public function getSomething(?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/inferred-auth-implicit-no-expiry/src/NestedNoAuth/NestedNoAuthClient.php b/seed/php-sdk/inferred-auth-implicit-no-expiry/src/NestedNoAuth/NestedNoAuthClient.php index c9f5659a49b0..f7c33619f24c 100644 --- a/seed/php-sdk/inferred-auth-implicit-no-expiry/src/NestedNoAuth/NestedNoAuthClient.php +++ b/seed/php-sdk/inferred-auth-implicit-no-expiry/src/NestedNoAuth/NestedNoAuthClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class NestedNoAuthClient +class NestedNoAuthClient { /** * @var ApiClient $api @@ -39,11 +39,10 @@ class NestedNoAuthClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; $this->api = new ApiClient($this->client, $this->options); diff --git a/seed/php-sdk/inferred-auth-implicit-no-expiry/src/SeedClient.php b/seed/php-sdk/inferred-auth-implicit-no-expiry/src/SeedClient.php index 6763442e4fcc..1ca739e04a7d 100644 --- a/seed/php-sdk/inferred-auth-implicit-no-expiry/src/SeedClient.php +++ b/seed/php-sdk/inferred-auth-implicit-no-expiry/src/SeedClient.php @@ -10,7 +10,7 @@ use Seed\Core\Client\RawClient; use Seed\Core\InferredAuthProvider; -class SeedClient +class SeedClient { /** * @var AuthClient $auth @@ -48,6 +48,11 @@ class SeedClient */ private RawClient $client; + /** + * @var InferredAuthProvider $inferredAuthProvider + */ + private InferredAuthProvider $inferredAuthProvider; + /** * @param ?string $clientId * @param ?string $clientSecret @@ -67,20 +72,19 @@ public function __construct( ?string $scope = null, ?string $xApiKey = null, ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - if ($xApiKey != null){ + if ($xApiKey != null) { $defaultHeaders['X-Api-Key'] = $xApiKey; } - + $this->options = $options ?? []; - + $authRawClient = new RawClient(['headers' => []]); $authClient = new AuthClient($authRawClient); $inferredAuthOptions = [ @@ -91,19 +95,20 @@ public function __construct( 'scope' => $scope ?? '', 'xApiKey' => $xApiKey ?? '', ]; - $inferredAuthProvider = new InferredAuthProvider($authClient, $inferredAuthOptions); - $authHeaders = $inferredAuthProvider->getAuthHeaders(); - - $defaultHeaders = array_merge($defaultHeaders, $authHeaders); + $this->inferredAuthProvider = new InferredAuthProvider($authClient, $inferredAuthOptions); + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + + $this->options['getAuthHeaders'] = fn () => + $this->inferredAuthProvider->getAuthHeaders(); + $this->client = new RawClient( options: $this->options, ); - + $this->auth = new AuthClient($this->client, $this->options); $this->nestedNoAuth = new NestedNoAuthClient($this->client, $this->options); $this->nested = new NestedClient($this->client, $this->options); diff --git a/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Simple/SimpleClient.php b/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Simple/SimpleClient.php index 50b6b54f52c0..7a8df43cf28d 100644 --- a/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Simple/SimpleClient.php +++ b/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Simple/SimpleClient.php @@ -11,7 +11,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class SimpleClient +class SimpleClient { /** * @var array{ @@ -39,11 +39,10 @@ class SimpleClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -60,7 +59,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getSomething(?array $options = null): void { + public function getSomething(?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -72,12 +72,12 @@ public function getSomething(?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Utils/File.php b/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Utils/File.php +++ b/seed/php-sdk/inferred-auth-implicit-no-expiry/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/inferred-auth-implicit-no-expiry/tests/Core/Client/RawClientTest.php b/seed/php-sdk/inferred-auth-implicit-no-expiry/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/inferred-auth-implicit-no-expiry/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/inferred-auth-implicit-no-expiry/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/inferred-auth-implicit-no-expiry/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/inferred-auth-implicit-no-expiry/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/inferred-auth-implicit-no-expiry/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/inferred-auth-implicit-no-expiry/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/inferred-auth-implicit-no-expiry/tests/Core/Json/EnumTest.php b/seed/php-sdk/inferred-auth-implicit-no-expiry/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/inferred-auth-implicit-no-expiry/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/inferred-auth-implicit-no-expiry/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/inferred-auth-implicit-no-expiry/tests/Core/Json/TraitTest.php b/seed/php-sdk/inferred-auth-implicit-no-expiry/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/inferred-auth-implicit-no-expiry/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/inferred-auth-implicit-no-expiry/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/inferred-auth-implicit-no-expiry/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/inferred-auth-implicit-no-expiry/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/inferred-auth-implicit-no-expiry/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/inferred-auth-implicit-no-expiry/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/license/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/license/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/license/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/license/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/license/src/Core/Client/RawClient.php b/seed/php-sdk/license/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/license/src/Core/Client/RawClient.php +++ b/seed/php-sdk/license/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/license/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/license/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/license/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/license/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/license/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/license/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/license/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/license/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/license/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/license/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/license/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/license/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/license/src/Core/Types/ArrayType.php b/seed/php-sdk/license/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/license/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/license/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/license/src/Core/Types/Constant.php b/seed/php-sdk/license/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/license/src/Core/Types/Constant.php +++ b/seed/php-sdk/license/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/license/src/Core/Types/Union.php b/seed/php-sdk/license/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/license/src/Core/Types/Union.php +++ b/seed/php-sdk/license/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/license/src/Exceptions/SeedApiException.php b/seed/php-sdk/license/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/license/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/license/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/license/src/Exceptions/SeedException.php b/seed/php-sdk/license/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/license/src/Exceptions/SeedException.php +++ b/seed/php-sdk/license/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/license/src/SeedClient.php b/seed/php-sdk/license/src/SeedClient.php index 00d0ef1be863..12cbfe796e53 100644 --- a/seed/php-sdk/license/src/SeedClient.php +++ b/seed/php-sdk/license/src/SeedClient.php @@ -11,7 +11,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class SeedClient +class SeedClient { /** * @var array{ @@ -40,22 +40,21 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); @@ -73,7 +72,8 @@ public function __construct( * @throws SeedException * @throws SeedApiException */ - public function get(?array $options = null): void { + public function get(?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -85,12 +85,12 @@ public function get(?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/license/src/Types/Type.php b/seed/php-sdk/license/src/Types/Type.php index 53fd92903c94..94d7ae5598b2 100644 --- a/seed/php-sdk/license/src/Types/Type.php +++ b/seed/php-sdk/license/src/Types/Type.php @@ -23,15 +23,15 @@ class Type extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/license/src/Utils/File.php b/seed/php-sdk/license/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/license/src/Utils/File.php +++ b/seed/php-sdk/license/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/license/tests/Core/Client/RawClientTest.php b/seed/php-sdk/license/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/license/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/license/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/license/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/license/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/license/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/license/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/license/tests/Core/Json/EnumTest.php b/seed/php-sdk/license/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/license/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/license/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/license/tests/Core/Json/TraitTest.php b/seed/php-sdk/license/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/license/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/license/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/license/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/license/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/license/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/license/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/literal/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/literal/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/literal/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/literal/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/literal/src/Core/Client/RawClient.php b/seed/php-sdk/literal/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/literal/src/Core/Client/RawClient.php +++ b/seed/php-sdk/literal/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/literal/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/literal/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/literal/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/literal/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/literal/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/literal/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/literal/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/literal/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/literal/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/literal/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/literal/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/literal/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/literal/src/Core/Types/ArrayType.php b/seed/php-sdk/literal/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/literal/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/literal/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/literal/src/Core/Types/Constant.php b/seed/php-sdk/literal/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/literal/src/Core/Types/Constant.php +++ b/seed/php-sdk/literal/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/literal/src/Core/Types/Union.php b/seed/php-sdk/literal/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/literal/src/Core/Types/Union.php +++ b/seed/php-sdk/literal/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/literal/src/Exceptions/SeedApiException.php b/seed/php-sdk/literal/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/literal/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/literal/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/literal/src/Exceptions/SeedException.php b/seed/php-sdk/literal/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/literal/src/Exceptions/SeedException.php +++ b/seed/php-sdk/literal/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/literal/src/Headers/HeadersClient.php b/seed/php-sdk/literal/src/Headers/HeadersClient.php index 895c00ab2789..70f741f41c8b 100644 --- a/seed/php-sdk/literal/src/Headers/HeadersClient.php +++ b/seed/php-sdk/literal/src/Headers/HeadersClient.php @@ -14,7 +14,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class HeadersClient +class HeadersClient { /** * @var array{ @@ -42,11 +42,10 @@ class HeadersClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -65,7 +64,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function send(SendLiteralsInHeadersRequest $request, ?array $options = null): SendResponse { + public function send(SendLiteralsInHeadersRequest $request, ?array $options = null): SendResponse + { $options = array_merge($this->options, $options ?? []); $headers = []; $headers['X-Endpoint-Version'] = '02-12-2024'; @@ -82,15 +82,15 @@ public function send(SendLiteralsInHeadersRequest $request, ?array $options = nu $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return SendResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/literal/src/Headers/Requests/SendLiteralsInHeadersRequest.php b/seed/php-sdk/literal/src/Headers/Requests/SendLiteralsInHeadersRequest.php index 7bd3745b4e7a..f3ede63d049f 100644 --- a/seed/php-sdk/literal/src/Headers/Requests/SendLiteralsInHeadersRequest.php +++ b/seed/php-sdk/literal/src/Headers/Requests/SendLiteralsInHeadersRequest.php @@ -32,8 +32,9 @@ class SendLiteralsInHeadersRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->endpointVersion = $values['endpointVersion'];$this->async = $values['async'];$this->query = $values['query']; + ) { + $this->endpointVersion = $values['endpointVersion']; + $this->async = $values['async']; + $this->query = $values['query']; } } diff --git a/seed/php-sdk/literal/src/Inlined/InlinedClient.php b/seed/php-sdk/literal/src/Inlined/InlinedClient.php index 9618d12bbba4..ccd01b69efbc 100644 --- a/seed/php-sdk/literal/src/Inlined/InlinedClient.php +++ b/seed/php-sdk/literal/src/Inlined/InlinedClient.php @@ -14,7 +14,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class InlinedClient +class InlinedClient { /** * @var array{ @@ -42,11 +42,10 @@ class InlinedClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -65,7 +64,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function send(SendLiteralsInlinedRequest $request, ?array $options = null): SendResponse { + public function send(SendLiteralsInlinedRequest $request, ?array $options = null): SendResponse + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -78,15 +78,15 @@ public function send(SendLiteralsInlinedRequest $request, ?array $options = null $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return SendResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/literal/src/Inlined/Requests/SendLiteralsInlinedRequest.php b/seed/php-sdk/literal/src/Inlined/Requests/SendLiteralsInlinedRequest.php index 2cfa40ac8bf3..98416623b115 100644 --- a/seed/php-sdk/literal/src/Inlined/Requests/SendLiteralsInlinedRequest.php +++ b/seed/php-sdk/literal/src/Inlined/Requests/SendLiteralsInlinedRequest.php @@ -70,8 +70,14 @@ class SendLiteralsInlinedRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->prompt = $values['prompt'];$this->context = $values['context'] ?? null;$this->query = $values['query'];$this->temperature = $values['temperature'] ?? null;$this->stream = $values['stream'];$this->aliasedContext = $values['aliasedContext'];$this->maybeContext = $values['maybeContext'] ?? null;$this->objectWithLiteral = $values['objectWithLiteral']; + ) { + $this->prompt = $values['prompt']; + $this->context = $values['context'] ?? null; + $this->query = $values['query']; + $this->temperature = $values['temperature'] ?? null; + $this->stream = $values['stream']; + $this->aliasedContext = $values['aliasedContext']; + $this->maybeContext = $values['maybeContext'] ?? null; + $this->objectWithLiteral = $values['objectWithLiteral']; } } diff --git a/seed/php-sdk/literal/src/Inlined/Types/ANestedLiteral.php b/seed/php-sdk/literal/src/Inlined/Types/ANestedLiteral.php index 8dbf92c6a95e..d3acb1b80f91 100644 --- a/seed/php-sdk/literal/src/Inlined/Types/ANestedLiteral.php +++ b/seed/php-sdk/literal/src/Inlined/Types/ANestedLiteral.php @@ -20,15 +20,15 @@ class ANestedLiteral extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->myLiteral = $values['myLiteral']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/literal/src/Inlined/Types/ATopLevelLiteral.php b/seed/php-sdk/literal/src/Inlined/Types/ATopLevelLiteral.php index cfb1ea4bd3d2..6ea2fa6ed3f0 100644 --- a/seed/php-sdk/literal/src/Inlined/Types/ATopLevelLiteral.php +++ b/seed/php-sdk/literal/src/Inlined/Types/ATopLevelLiteral.php @@ -20,15 +20,15 @@ class ATopLevelLiteral extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->nestedLiteral = $values['nestedLiteral']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/literal/src/Inlined/Types/DiscriminatedLiteral.php b/seed/php-sdk/literal/src/Inlined/Types/DiscriminatedLiteral.php index fc3ac858beb1..43ded607874b 100644 --- a/seed/php-sdk/literal/src/Inlined/Types/DiscriminatedLiteral.php +++ b/seed/php-sdk/literal/src/Inlined/Types/DiscriminatedLiteral.php @@ -50,16 +50,17 @@ class DiscriminatedLiteral extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param string $customName * @return DiscriminatedLiteral */ - public static function customName(string $customName): DiscriminatedLiteral { + public static function customName(string $customName): DiscriminatedLiteral + { return new DiscriminatedLiteral([ 'type' => 'customName', 'value' => $customName, @@ -70,7 +71,8 @@ public static function customName(string $customName): DiscriminatedLiteral { * @param 'Bob' $defaultName * @return DiscriminatedLiteral */ - public static function defaultName(string $defaultName): DiscriminatedLiteral { + public static function defaultName(string $defaultName): DiscriminatedLiteral + { return new DiscriminatedLiteral([ 'type' => 'defaultName', 'value' => $defaultName, @@ -81,7 +83,8 @@ public static function defaultName(string $defaultName): DiscriminatedLiteral { * @param bool $george * @return DiscriminatedLiteral */ - public static function george(bool $george): DiscriminatedLiteral { + public static function george(bool $george): DiscriminatedLiteral + { return new DiscriminatedLiteral([ 'type' => 'george', 'value' => $george, @@ -92,7 +95,8 @@ public static function george(bool $george): DiscriminatedLiteral { * @param true $literalGeorge * @return DiscriminatedLiteral */ - public static function literalGeorge(bool $literalGeorge): DiscriminatedLiteral { + public static function literalGeorge(bool $literalGeorge): DiscriminatedLiteral + { return new DiscriminatedLiteral([ 'type' => 'literalGeorge', 'value' => $literalGeorge, @@ -102,101 +106,111 @@ public static function literalGeorge(bool $literalGeorge): DiscriminatedLiteral /** * @return bool */ - public function isCustomName(): bool { - return is_string($this->value)&& $this->type === 'customName'; + public function isCustomName(): bool + { + return is_string($this->value) && $this->type === 'customName'; } /** * @return string */ - public function asCustomName(): string { - if (!(is_string($this->value)&& $this->type === 'customName')){ + public function asCustomName(): string + { + if (!(is_string($this->value) && $this->type === 'customName')) { throw new Exception( "Expected customName; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isDefaultName(): bool { - return $this->value === 'Bob'&& $this->type === 'defaultName'; + public function isDefaultName(): bool + { + return $this->value === 'Bob' && $this->type === 'defaultName'; } /** * @return 'Bob' */ - public function asDefaultName(): string { - if (!($this->value === 'Bob'&& $this->type === 'defaultName')){ + public function asDefaultName(): string + { + if (!($this->value === 'Bob' && $this->type === 'defaultName')) { throw new Exception( "Expected defaultName; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isGeorge(): bool { - return is_bool($this->value)&& $this->type === 'george'; + public function isGeorge(): bool + { + return is_bool($this->value) && $this->type === 'george'; } /** * @return bool */ - public function asGeorge(): bool { - if (!(is_bool($this->value)&& $this->type === 'george')){ + public function asGeorge(): bool + { + if (!(is_bool($this->value) && $this->type === 'george')) { throw new Exception( "Expected george; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isLiteralGeorge(): bool { - return $this->value === true&& $this->type === 'literalGeorge'; + public function isLiteralGeorge(): bool + { + return $this->value === true && $this->type === 'literalGeorge'; } /** * @return true */ - public function asLiteralGeorge(): bool { - if (!($this->value === true&& $this->type === 'literalGeorge')){ + public function asLiteralGeorge(): bool + { + if (!($this->value === true && $this->type === 'literalGeorge')) { throw new Exception( "Expected literalGeorge; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'customName': $value = $this->value; $result['customName'] = $value; @@ -215,26 +229,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -243,56 +258,57 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'customName': - if (!array_key_exists('customName', $data)){ + if (!array_key_exists('customName', $data)) { throw new Exception( "JSON data is missing property 'customName'", ); } - + $args['value'] = $data['customName']; break; case 'defaultName': - if (!array_key_exists('defaultName', $data)){ + if (!array_key_exists('defaultName', $data)) { throw new Exception( "JSON data is missing property 'defaultName'", ); } - + $args['value'] = $data['defaultName']; break; case 'george': - if (!array_key_exists('george', $data)){ + if (!array_key_exists('george', $data)) { throw new Exception( "JSON data is missing property 'george'", ); } - + $args['value'] = $data['george']; break; case 'literalGeorge': - if (!array_key_exists('literalGeorge', $data)){ + if (!array_key_exists('literalGeorge', $data)) { throw new Exception( "JSON data is missing property 'literalGeorge'", ); } - + $args['value'] = $data['literalGeorge']; break; case '_unknown': @@ -300,7 +316,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/literal/src/Path/PathClient.php b/seed/php-sdk/literal/src/Path/PathClient.php index e306a7cc57db..e505a0579a36 100644 --- a/seed/php-sdk/literal/src/Path/PathClient.php +++ b/seed/php-sdk/literal/src/Path/PathClient.php @@ -13,7 +13,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class PathClient +class PathClient { /** * @var array{ @@ -41,11 +41,10 @@ class PathClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -64,7 +63,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function send(string $id, ?array $options = null): SendResponse { + public function send(string $id, ?array $options = null): SendResponse + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -76,15 +76,15 @@ public function send(string $id, ?array $options = null): SendResponse { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return SendResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/literal/src/Query/QueryClient.php b/seed/php-sdk/literal/src/Query/QueryClient.php index 2b7ff2733d58..3cc965bb3ed4 100644 --- a/seed/php-sdk/literal/src/Query/QueryClient.php +++ b/seed/php-sdk/literal/src/Query/QueryClient.php @@ -14,7 +14,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class QueryClient +class QueryClient { /** * @var array{ @@ -42,11 +42,10 @@ class QueryClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -65,7 +64,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function send(SendLiteralsInQueryRequest $request, ?array $options = null): SendResponse { + public function send(SendLiteralsInQueryRequest $request, ?array $options = null): SendResponse + { $options = array_merge($this->options, $options ?? []); $query = []; $query['prompt'] = 'You are a helpful assistant'; @@ -73,16 +73,16 @@ public function send(SendLiteralsInQueryRequest $request, ?array $options = null $query['query'] = $request->query; $query['stream'] = 'false'; $query['alias_stream'] = $request->aliasStream; - if ($request->optionalPrompt != null){ + if ($request->optionalPrompt != null) { $query['optional_prompt'] = $request->optionalPrompt; } - if ($request->aliasOptionalPrompt != null){ + if ($request->aliasOptionalPrompt != null) { $query['alias_optional_prompt'] = $request->aliasOptionalPrompt; } - if ($request->optionalStream != null){ + if ($request->optionalStream != null) { $query['optional_stream'] = $request->optionalStream; } - if ($request->aliasOptionalStream != null){ + if ($request->aliasOptionalStream != null) { $query['alias_optional_stream'] = $request->aliasOptionalStream; } try { @@ -96,15 +96,15 @@ public function send(SendLiteralsInQueryRequest $request, ?array $options = null $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return SendResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/literal/src/Query/Requests/SendLiteralsInQueryRequest.php b/seed/php-sdk/literal/src/Query/Requests/SendLiteralsInQueryRequest.php index e14e272b379c..ce0115d5a291 100644 --- a/seed/php-sdk/literal/src/Query/Requests/SendLiteralsInQueryRequest.php +++ b/seed/php-sdk/literal/src/Query/Requests/SendLiteralsInQueryRequest.php @@ -66,8 +66,15 @@ class SendLiteralsInQueryRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->prompt = $values['prompt'];$this->optionalPrompt = $values['optionalPrompt'] ?? null;$this->aliasPrompt = $values['aliasPrompt'];$this->aliasOptionalPrompt = $values['aliasOptionalPrompt'] ?? null;$this->query = $values['query'];$this->stream = $values['stream'];$this->optionalStream = $values['optionalStream'] ?? null;$this->aliasStream = $values['aliasStream'];$this->aliasOptionalStream = $values['aliasOptionalStream'] ?? null; + ) { + $this->prompt = $values['prompt']; + $this->optionalPrompt = $values['optionalPrompt'] ?? null; + $this->aliasPrompt = $values['aliasPrompt']; + $this->aliasOptionalPrompt = $values['aliasOptionalPrompt'] ?? null; + $this->query = $values['query']; + $this->stream = $values['stream']; + $this->optionalStream = $values['optionalStream'] ?? null; + $this->aliasStream = $values['aliasStream']; + $this->aliasOptionalStream = $values['aliasOptionalStream'] ?? null; } } diff --git a/seed/php-sdk/literal/src/Reference/ReferenceClient.php b/seed/php-sdk/literal/src/Reference/ReferenceClient.php index db6818339dc7..2037b93022a7 100644 --- a/seed/php-sdk/literal/src/Reference/ReferenceClient.php +++ b/seed/php-sdk/literal/src/Reference/ReferenceClient.php @@ -14,7 +14,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class ReferenceClient +class ReferenceClient { /** * @var array{ @@ -42,11 +42,10 @@ class ReferenceClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -65,7 +64,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function send(SendRequest $request, ?array $options = null): SendResponse { + public function send(SendRequest $request, ?array $options = null): SendResponse + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -78,15 +78,15 @@ public function send(SendRequest $request, ?array $options = null): SendResponse $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return SendResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/literal/src/Reference/Types/ContainerObject.php b/seed/php-sdk/literal/src/Reference/Types/ContainerObject.php index ac7549aea497..385d0d0a88e8 100644 --- a/seed/php-sdk/literal/src/Reference/Types/ContainerObject.php +++ b/seed/php-sdk/literal/src/Reference/Types/ContainerObject.php @@ -21,15 +21,15 @@ class ContainerObject extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->nestedObjects = $values['nestedObjects']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/literal/src/Reference/Types/NestedObjectWithLiterals.php b/seed/php-sdk/literal/src/Reference/Types/NestedObjectWithLiterals.php index 7e69439c57b9..e5766f71636f 100644 --- a/seed/php-sdk/literal/src/Reference/Types/NestedObjectWithLiterals.php +++ b/seed/php-sdk/literal/src/Reference/Types/NestedObjectWithLiterals.php @@ -34,15 +34,17 @@ class NestedObjectWithLiterals extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->literal1 = $values['literal1'];$this->literal2 = $values['literal2'];$this->strProp = $values['strProp']; + ) { + $this->literal1 = $values['literal1']; + $this->literal2 = $values['literal2']; + $this->strProp = $values['strProp']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/literal/src/Reference/Types/SendRequest.php b/seed/php-sdk/literal/src/Reference/Types/SendRequest.php index 9e4540e8e484..880748603442 100644 --- a/seed/php-sdk/literal/src/Reference/Types/SendRequest.php +++ b/seed/php-sdk/literal/src/Reference/Types/SendRequest.php @@ -62,15 +62,21 @@ class SendRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->prompt = $values['prompt'];$this->query = $values['query'];$this->stream = $values['stream'];$this->ending = $values['ending'];$this->context = $values['context'];$this->maybeContext = $values['maybeContext'] ?? null;$this->containerObject = $values['containerObject']; + ) { + $this->prompt = $values['prompt']; + $this->query = $values['query']; + $this->stream = $values['stream']; + $this->ending = $values['ending']; + $this->context = $values['context']; + $this->maybeContext = $values['maybeContext'] ?? null; + $this->containerObject = $values['containerObject']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/literal/src/SeedClient.php b/seed/php-sdk/literal/src/SeedClient.php index 20b243f3cff1..108a80048f1f 100644 --- a/seed/php-sdk/literal/src/SeedClient.php +++ b/seed/php-sdk/literal/src/SeedClient.php @@ -10,7 +10,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var HeadersClient $headers @@ -68,8 +68,7 @@ public function __construct( ?string $version = null, ?bool $auditLogging = null, ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-API-Version' => '02-02-2024', 'X-API-Enable-Audit-Logging' => 'true', @@ -78,24 +77,24 @@ public function __construct( 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - if ($version != null){ + if ($version != null) { $defaultHeaders['X-API-Version'] = $version; } - if ($auditLogging != null){ + if ($auditLogging != null) { $defaultHeaders['X-API-Enable-Audit-Logging'] = $auditLogging; } - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->headers = new HeadersClient($this->client, $this->options); $this->inlined = new InlinedClient($this->client, $this->options); $this->path = new PathClient($this->client, $this->options); diff --git a/seed/php-sdk/literal/src/Types/SendResponse.php b/seed/php-sdk/literal/src/Types/SendResponse.php index 8c5d96ec0e35..fd4146487ade 100644 --- a/seed/php-sdk/literal/src/Types/SendResponse.php +++ b/seed/php-sdk/literal/src/Types/SendResponse.php @@ -34,15 +34,17 @@ class SendResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->message = $values['message'];$this->status = $values['status'];$this->success = $values['success']; + ) { + $this->message = $values['message']; + $this->status = $values['status']; + $this->success = $values['success']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/literal/src/Utils/File.php b/seed/php-sdk/literal/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/literal/src/Utils/File.php +++ b/seed/php-sdk/literal/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/literal/tests/Core/Client/RawClientTest.php b/seed/php-sdk/literal/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/literal/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/literal/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/literal/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/literal/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/literal/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/literal/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/literal/tests/Core/Json/EnumTest.php b/seed/php-sdk/literal/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/literal/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/literal/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/literal/tests/Core/Json/TraitTest.php b/seed/php-sdk/literal/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/literal/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/literal/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/literal/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/literal/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/literal/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/literal/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/literals-unions/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/literals-unions/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/literals-unions/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/literals-unions/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/literals-unions/src/Core/Client/RawClient.php b/seed/php-sdk/literals-unions/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/literals-unions/src/Core/Client/RawClient.php +++ b/seed/php-sdk/literals-unions/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/literals-unions/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/literals-unions/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/literals-unions/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/literals-unions/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/literals-unions/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/literals-unions/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/literals-unions/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/literals-unions/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/literals-unions/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/literals-unions/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/literals-unions/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/literals-unions/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/literals-unions/src/Core/Types/ArrayType.php b/seed/php-sdk/literals-unions/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/literals-unions/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/literals-unions/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/literals-unions/src/Core/Types/Constant.php b/seed/php-sdk/literals-unions/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/literals-unions/src/Core/Types/Constant.php +++ b/seed/php-sdk/literals-unions/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/literals-unions/src/Core/Types/Union.php b/seed/php-sdk/literals-unions/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/literals-unions/src/Core/Types/Union.php +++ b/seed/php-sdk/literals-unions/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/literals-unions/src/Exceptions/SeedApiException.php b/seed/php-sdk/literals-unions/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/literals-unions/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/literals-unions/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/literals-unions/src/Exceptions/SeedException.php b/seed/php-sdk/literals-unions/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/literals-unions/src/Exceptions/SeedException.php +++ b/seed/php-sdk/literals-unions/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/literals-unions/src/SeedClient.php b/seed/php-sdk/literals-unions/src/SeedClient.php index efe6e7438b31..37a1c4dd76aa 100644 --- a/seed/php-sdk/literals-unions/src/SeedClient.php +++ b/seed/php-sdk/literals-unions/src/SeedClient.php @@ -2,7 +2,6 @@ namespace Seed; -class SeedClient +class SeedClient { - } diff --git a/seed/php-sdk/literals-unions/src/Utils/File.php b/seed/php-sdk/literals-unions/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/literals-unions/src/Utils/File.php +++ b/seed/php-sdk/literals-unions/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/literals-unions/tests/Core/Client/RawClientTest.php b/seed/php-sdk/literals-unions/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/literals-unions/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/literals-unions/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/literals-unions/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/literals-unions/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/literals-unions/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/literals-unions/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/literals-unions/tests/Core/Json/EnumTest.php b/seed/php-sdk/literals-unions/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/literals-unions/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/literals-unions/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/literals-unions/tests/Core/Json/TraitTest.php b/seed/php-sdk/literals-unions/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/literals-unions/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/literals-unions/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/literals-unions/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/literals-unions/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/literals-unions/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/literals-unions/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/mixed-case/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/mixed-case/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/mixed-case/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/mixed-case/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/mixed-case/src/Core/Client/RawClient.php b/seed/php-sdk/mixed-case/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/mixed-case/src/Core/Client/RawClient.php +++ b/seed/php-sdk/mixed-case/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/mixed-case/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/mixed-case/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/mixed-case/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/mixed-case/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/mixed-case/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/mixed-case/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/mixed-case/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/mixed-case/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/mixed-case/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/mixed-case/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/mixed-case/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/mixed-case/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/mixed-case/src/Core/Types/ArrayType.php b/seed/php-sdk/mixed-case/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/mixed-case/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/mixed-case/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/mixed-case/src/Core/Types/Constant.php b/seed/php-sdk/mixed-case/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/mixed-case/src/Core/Types/Constant.php +++ b/seed/php-sdk/mixed-case/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/mixed-case/src/Core/Types/Union.php b/seed/php-sdk/mixed-case/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/mixed-case/src/Core/Types/Union.php +++ b/seed/php-sdk/mixed-case/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/mixed-case/src/Exceptions/SeedApiException.php b/seed/php-sdk/mixed-case/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/mixed-case/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/mixed-case/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/mixed-case/src/Exceptions/SeedException.php b/seed/php-sdk/mixed-case/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/mixed-case/src/Exceptions/SeedException.php +++ b/seed/php-sdk/mixed-case/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/mixed-case/src/SeedClient.php b/seed/php-sdk/mixed-case/src/SeedClient.php index 8d7adcb3b0f2..0fd6a9d1029e 100644 --- a/seed/php-sdk/mixed-case/src/SeedClient.php +++ b/seed/php-sdk/mixed-case/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var ServiceClient $service @@ -40,26 +40,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->service = new ServiceClient($this->client, $this->options); } } diff --git a/seed/php-sdk/mixed-case/src/Service/Requests/ListResourcesRequest.php b/seed/php-sdk/mixed-case/src/Service/Requests/ListResourcesRequest.php index 5f07c2241f23..951e10c07d2c 100644 --- a/seed/php-sdk/mixed-case/src/Service/Requests/ListResourcesRequest.php +++ b/seed/php-sdk/mixed-case/src/Service/Requests/ListResourcesRequest.php @@ -25,8 +25,8 @@ class ListResourcesRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->pageLimit = $values['pageLimit'];$this->beforeDate = $values['beforeDate']; + ) { + $this->pageLimit = $values['pageLimit']; + $this->beforeDate = $values['beforeDate']; } } diff --git a/seed/php-sdk/mixed-case/src/Service/ServiceClient.php b/seed/php-sdk/mixed-case/src/Service/ServiceClient.php index 0b4c47c43721..e8e51f7638f5 100644 --- a/seed/php-sdk/mixed-case/src/Service/ServiceClient.php +++ b/seed/php-sdk/mixed-case/src/Service/ServiceClient.php @@ -16,7 +16,7 @@ use Seed\Core\Json\JsonSerializer; use Seed\Core\Json\JsonDecoder; -class ServiceClient +class ServiceClient { /** * @var array{ @@ -44,11 +44,10 @@ class ServiceClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -67,7 +66,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getResource(string $resourceId, ?array $options = null): Resource { + public function getResource(string $resourceId, ?array $options = null): Resource + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -79,15 +79,15 @@ public function getResource(string $resourceId, ?array $options = null): Resourc $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return Resource::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -119,7 +119,8 @@ public function getResource(string $resourceId, ?array $options = null): Resourc * @throws SeedException * @throws SeedApiException */ - public function listResources(ListResourcesRequest $request, ?array $options = null): array { + public function listResources(ListResourcesRequest $request, ?array $options = null): array + { $options = array_merge($this->options, $options ?? []); $query = []; $query['page_limit'] = $request->pageLimit; @@ -135,15 +136,15 @@ public function listResources(ListResourcesRequest $request, ?array $options = n $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, [Resource::class]); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/mixed-case/src/Service/Types/NestedUser.php b/seed/php-sdk/mixed-case/src/Service/Types/NestedUser.php index 1844c1cbd453..957068057efd 100644 --- a/seed/php-sdk/mixed-case/src/Service/Types/NestedUser.php +++ b/seed/php-sdk/mixed-case/src/Service/Types/NestedUser.php @@ -27,15 +27,16 @@ class NestedUser extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->nestedUser = $values['nestedUser']; + ) { + $this->name = $values['name']; + $this->nestedUser = $values['nestedUser']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/mixed-case/src/Service/Types/Organization.php b/seed/php-sdk/mixed-case/src/Service/Types/Organization.php index 7517e7b4bd14..25f272b106a1 100644 --- a/seed/php-sdk/mixed-case/src/Service/Types/Organization.php +++ b/seed/php-sdk/mixed-case/src/Service/Types/Organization.php @@ -20,15 +20,15 @@ class Organization extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/mixed-case/src/Service/Types/Resource.php b/seed/php-sdk/mixed-case/src/Service/Types/Resource.php index 7740102d1f26..48c26f1fa722 100644 --- a/seed/php-sdk/mixed-case/src/Service/Types/Resource.php +++ b/seed/php-sdk/mixed-case/src/Service/Types/Resource.php @@ -50,9 +50,10 @@ class Resource extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->status = $values['status'];$this->resourceType = $values['resourceType'];$this->value = $values['value']; + ) { + $this->status = $values['status']; + $this->resourceType = $values['resourceType']; + $this->value = $values['value']; } /** @@ -60,7 +61,8 @@ private function __construct( * @param User $user * @return Resource */ - public static function user(string $status, User $user): Resource { + public static function user(string $status, User $user): Resource + { return new Resource([ 'status' => $status, 'resourceType' => 'user', @@ -73,7 +75,8 @@ public static function user(string $status, User $user): Resource { * @param Organization $organization * @return Resource */ - public static function organization(string $status, Organization $organization): Resource { + public static function organization(string $status, Organization $organization): Resource + { return new Resource([ 'status' => $status, 'resourceType' => 'Organization', @@ -84,61 +87,67 @@ public static function organization(string $status, Organization $organization): /** * @return bool */ - public function isUser(): bool { - return $this->value instanceof User&& $this->resourceType === 'user'; + public function isUser(): bool + { + return $this->value instanceof User && $this->resourceType === 'user'; } /** * @return User */ - public function asUser(): User { - if (!($this->value instanceof User&& $this->resourceType === 'user')){ + public function asUser(): User + { + if (!($this->value instanceof User && $this->resourceType === 'user')) { throw new Exception( "Expected user; got " . $this->resourceType . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isOrganization(): bool { - return $this->value instanceof Organization&& $this->resourceType === 'Organization'; + public function isOrganization(): bool + { + return $this->value instanceof Organization && $this->resourceType === 'Organization'; } /** * @return Organization */ - public function asOrganization(): Organization { - if (!($this->value instanceof Organization&& $this->resourceType === 'Organization')){ + public function asOrganization(): Organization + { + if (!($this->value instanceof Organization && $this->resourceType === 'Organization')) { throw new Exception( "Expected Organization; got " . $this->resourceType . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['resource_type'] = $this->resourceType; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->resourceType){ + + switch ($this->resourceType) { case 'user': $value = $this->asUser()->jsonSerialize(); $result = array_merge($value, $result); @@ -149,26 +158,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -177,34 +187,35 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('status', $data)){ + if (!array_key_exists('status', $data)) { throw new Exception( "JSON data is missing property 'status'", ); } - if (!($data['status'] instanceof ResourceStatus)){ + if (!($data['status'] instanceof ResourceStatus)) { throw new Exception( "Expected property 'status' in JSON data to be enumString, instead received " . get_debug_type($data['status']), ); } $args['status'] = $data['status']; - - if (!array_key_exists('resource_type', $data)){ + + if (!array_key_exists('resource_type', $data)) { throw new Exception( "JSON data is missing property 'resource_type'", ); } $resourceType = $data['resource_type']; - if (!(is_string($resourceType))){ + if (!(is_string($resourceType))) { throw new Exception( "Expected property 'resourceType' in JSON data to be string, instead received " . get_debug_type($data['resource_type']), ); } - + $args['resourceType'] = $resourceType; - switch ($resourceType){ + switch ($resourceType) { case 'user': $args['value'] = User::jsonDeserialize($data); break; @@ -216,7 +227,7 @@ public static function jsonDeserialize(array $data): static { $args['resourceType'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/mixed-case/src/Service/Types/ResourceStatus.php b/seed/php-sdk/mixed-case/src/Service/Types/ResourceStatus.php index 39f6e63b3dfb..5fe25731244c 100644 --- a/seed/php-sdk/mixed-case/src/Service/Types/ResourceStatus.php +++ b/seed/php-sdk/mixed-case/src/Service/Types/ResourceStatus.php @@ -2,8 +2,8 @@ namespace Seed\Service\Types; -enum ResourceStatus - : string { +enum ResourceStatus: string +{ case Active = "ACTIVE"; case Inactive = "INACTIVE"; } diff --git a/seed/php-sdk/mixed-case/src/Service/Types/User.php b/seed/php-sdk/mixed-case/src/Service/Types/User.php index ed1cb8b67673..21758b072830 100644 --- a/seed/php-sdk/mixed-case/src/Service/Types/User.php +++ b/seed/php-sdk/mixed-case/src/Service/Types/User.php @@ -35,15 +35,17 @@ class User extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->userName = $values['userName'];$this->metadataTags = $values['metadataTags'];$this->extraProperties = $values['extraProperties']; + ) { + $this->userName = $values['userName']; + $this->metadataTags = $values['metadataTags']; + $this->extraProperties = $values['extraProperties']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/mixed-case/src/Utils/File.php b/seed/php-sdk/mixed-case/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/mixed-case/src/Utils/File.php +++ b/seed/php-sdk/mixed-case/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/mixed-case/tests/Core/Client/RawClientTest.php b/seed/php-sdk/mixed-case/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/mixed-case/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/mixed-case/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/mixed-case/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/mixed-case/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/mixed-case/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/mixed-case/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/mixed-case/tests/Core/Json/EnumTest.php b/seed/php-sdk/mixed-case/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/mixed-case/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/mixed-case/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/mixed-case/tests/Core/Json/TraitTest.php b/seed/php-sdk/mixed-case/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/mixed-case/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/mixed-case/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/mixed-case/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/mixed-case/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/mixed-case/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/mixed-case/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/mixed-file-directory/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/mixed-file-directory/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/mixed-file-directory/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/mixed-file-directory/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/mixed-file-directory/src/Core/Client/RawClient.php b/seed/php-sdk/mixed-file-directory/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/mixed-file-directory/src/Core/Client/RawClient.php +++ b/seed/php-sdk/mixed-file-directory/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/mixed-file-directory/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/mixed-file-directory/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/mixed-file-directory/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/mixed-file-directory/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/mixed-file-directory/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/mixed-file-directory/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/mixed-file-directory/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/mixed-file-directory/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/mixed-file-directory/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/mixed-file-directory/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/mixed-file-directory/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/mixed-file-directory/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/mixed-file-directory/src/Core/Types/ArrayType.php b/seed/php-sdk/mixed-file-directory/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/mixed-file-directory/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/mixed-file-directory/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/mixed-file-directory/src/Core/Types/Constant.php b/seed/php-sdk/mixed-file-directory/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/mixed-file-directory/src/Core/Types/Constant.php +++ b/seed/php-sdk/mixed-file-directory/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/mixed-file-directory/src/Core/Types/Union.php b/seed/php-sdk/mixed-file-directory/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/mixed-file-directory/src/Core/Types/Union.php +++ b/seed/php-sdk/mixed-file-directory/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/mixed-file-directory/src/Exceptions/SeedApiException.php b/seed/php-sdk/mixed-file-directory/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/mixed-file-directory/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/mixed-file-directory/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/mixed-file-directory/src/Exceptions/SeedException.php b/seed/php-sdk/mixed-file-directory/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/mixed-file-directory/src/Exceptions/SeedException.php +++ b/seed/php-sdk/mixed-file-directory/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/mixed-file-directory/src/Organization/OrganizationClient.php b/seed/php-sdk/mixed-file-directory/src/Organization/OrganizationClient.php index 46510fddff61..8946a2925a42 100644 --- a/seed/php-sdk/mixed-file-directory/src/Organization/OrganizationClient.php +++ b/seed/php-sdk/mixed-file-directory/src/Organization/OrganizationClient.php @@ -14,7 +14,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class OrganizationClient +class OrganizationClient { /** * @var array{ @@ -42,11 +42,10 @@ class OrganizationClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -67,7 +66,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function create(CreateOrganizationRequest $request, ?array $options = null): Organization { + public function create(CreateOrganizationRequest $request, ?array $options = null): Organization + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -80,15 +80,15 @@ public function create(CreateOrganizationRequest $request, ?array $options = nul $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return Organization::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/mixed-file-directory/src/Organization/Types/CreateOrganizationRequest.php b/seed/php-sdk/mixed-file-directory/src/Organization/Types/CreateOrganizationRequest.php index a0b9b5d34b1b..f305a6d39c81 100644 --- a/seed/php-sdk/mixed-file-directory/src/Organization/Types/CreateOrganizationRequest.php +++ b/seed/php-sdk/mixed-file-directory/src/Organization/Types/CreateOrganizationRequest.php @@ -20,15 +20,15 @@ class CreateOrganizationRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/mixed-file-directory/src/Organization/Types/Organization.php b/seed/php-sdk/mixed-file-directory/src/Organization/Types/Organization.php index 317eccdd8438..908a301e9678 100644 --- a/seed/php-sdk/mixed-file-directory/src/Organization/Types/Organization.php +++ b/seed/php-sdk/mixed-file-directory/src/Organization/Types/Organization.php @@ -36,15 +36,17 @@ class Organization extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->name = $values['name'];$this->users = $values['users']; + ) { + $this->id = $values['id']; + $this->name = $values['name']; + $this->users = $values['users']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/mixed-file-directory/src/SeedClient.php b/seed/php-sdk/mixed-file-directory/src/SeedClient.php index ad202aa0e856..75d8e5706705 100644 --- a/seed/php-sdk/mixed-file-directory/src/SeedClient.php +++ b/seed/php-sdk/mixed-file-directory/src/SeedClient.php @@ -7,7 +7,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var OrganizationClient $organization @@ -46,26 +46,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->organization = new OrganizationClient($this->client, $this->options); $this->user = new UserClient($this->client, $this->options); } diff --git a/seed/php-sdk/mixed-file-directory/src/User/Events/EventsClient.php b/seed/php-sdk/mixed-file-directory/src/User/Events/EventsClient.php index e93249644620..498f70b8ecac 100644 --- a/seed/php-sdk/mixed-file-directory/src/User/Events/EventsClient.php +++ b/seed/php-sdk/mixed-file-directory/src/User/Events/EventsClient.php @@ -16,7 +16,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class EventsClient +class EventsClient { /** * @var MetadataClient $metadata @@ -49,11 +49,10 @@ class EventsClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; $this->metadata = new MetadataClient($this->client, $this->options); @@ -75,10 +74,11 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function listEvents(ListUserEventsRequest $request = new ListUserEventsRequest(), ?array $options = null): array { + public function listEvents(ListUserEventsRequest $request = new ListUserEventsRequest(), ?array $options = null): array + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->limit != null){ + if ($request->limit != null) { $query['limit'] = $request->limit; } try { @@ -92,15 +92,15 @@ public function listEvents(ListUserEventsRequest $request = new ListUserEventsRe $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, [Event::class]); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/mixed-file-directory/src/User/Events/Metadata/MetadataClient.php b/seed/php-sdk/mixed-file-directory/src/User/Events/Metadata/MetadataClient.php index bd6031300f8c..1e0fe20da3cb 100644 --- a/seed/php-sdk/mixed-file-directory/src/User/Events/Metadata/MetadataClient.php +++ b/seed/php-sdk/mixed-file-directory/src/User/Events/Metadata/MetadataClient.php @@ -14,7 +14,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class MetadataClient +class MetadataClient { /** * @var array{ @@ -42,11 +42,10 @@ class MetadataClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -67,7 +66,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getMetadata(GetEventMetadataRequest $request, ?array $options = null): Metadata { + public function getMetadata(GetEventMetadataRequest $request, ?array $options = null): Metadata + { $options = array_merge($this->options, $options ?? []); $query = []; $query['id'] = $request->id; @@ -82,15 +82,15 @@ public function getMetadata(GetEventMetadataRequest $request, ?array $options = $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return Metadata::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/mixed-file-directory/src/User/Events/Metadata/Requests/GetEventMetadataRequest.php b/seed/php-sdk/mixed-file-directory/src/User/Events/Metadata/Requests/GetEventMetadataRequest.php index 03bd432946f1..5b803c319818 100644 --- a/seed/php-sdk/mixed-file-directory/src/User/Events/Metadata/Requests/GetEventMetadataRequest.php +++ b/seed/php-sdk/mixed-file-directory/src/User/Events/Metadata/Requests/GetEventMetadataRequest.php @@ -18,8 +18,7 @@ class GetEventMetadataRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->id = $values['id']; } } diff --git a/seed/php-sdk/mixed-file-directory/src/User/Events/Metadata/Types/Metadata.php b/seed/php-sdk/mixed-file-directory/src/User/Events/Metadata/Types/Metadata.php index ea0ba897077e..4b2613f42f40 100644 --- a/seed/php-sdk/mixed-file-directory/src/User/Events/Metadata/Types/Metadata.php +++ b/seed/php-sdk/mixed-file-directory/src/User/Events/Metadata/Types/Metadata.php @@ -27,15 +27,16 @@ class Metadata extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->value = $values['value']; + ) { + $this->id = $values['id']; + $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/mixed-file-directory/src/User/Events/Requests/ListUserEventsRequest.php b/seed/php-sdk/mixed-file-directory/src/User/Events/Requests/ListUserEventsRequest.php index eb482c4f3725..f9ccd0fa7b75 100644 --- a/seed/php-sdk/mixed-file-directory/src/User/Events/Requests/ListUserEventsRequest.php +++ b/seed/php-sdk/mixed-file-directory/src/User/Events/Requests/ListUserEventsRequest.php @@ -18,8 +18,7 @@ class ListUserEventsRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->limit = $values['limit'] ?? null; } } diff --git a/seed/php-sdk/mixed-file-directory/src/User/Events/Types/Event.php b/seed/php-sdk/mixed-file-directory/src/User/Events/Types/Event.php index a5356cc7f773..0405bbe0ff2a 100644 --- a/seed/php-sdk/mixed-file-directory/src/User/Events/Types/Event.php +++ b/seed/php-sdk/mixed-file-directory/src/User/Events/Types/Event.php @@ -27,15 +27,16 @@ class Event extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->name = $values['name']; + ) { + $this->id = $values['id']; + $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/mixed-file-directory/src/User/Requests/ListUsersRequest.php b/seed/php-sdk/mixed-file-directory/src/User/Requests/ListUsersRequest.php index 3276eb139460..fd2f7e3cb6b0 100644 --- a/seed/php-sdk/mixed-file-directory/src/User/Requests/ListUsersRequest.php +++ b/seed/php-sdk/mixed-file-directory/src/User/Requests/ListUsersRequest.php @@ -18,8 +18,7 @@ class ListUsersRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->limit = $values['limit'] ?? null; } } diff --git a/seed/php-sdk/mixed-file-directory/src/User/Types/User.php b/seed/php-sdk/mixed-file-directory/src/User/Types/User.php index ceaf4f12db96..28382d104bb5 100644 --- a/seed/php-sdk/mixed-file-directory/src/User/Types/User.php +++ b/seed/php-sdk/mixed-file-directory/src/User/Types/User.php @@ -34,15 +34,17 @@ class User extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->name = $values['name'];$this->age = $values['age']; + ) { + $this->id = $values['id']; + $this->name = $values['name']; + $this->age = $values['age']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/mixed-file-directory/src/User/UserClient.php b/seed/php-sdk/mixed-file-directory/src/User/UserClient.php index 8fb33b6fb653..0760a37d1253 100644 --- a/seed/php-sdk/mixed-file-directory/src/User/UserClient.php +++ b/seed/php-sdk/mixed-file-directory/src/User/UserClient.php @@ -16,7 +16,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class UserClient +class UserClient { /** * @var EventsClient $events @@ -49,11 +49,10 @@ class UserClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; $this->events = new EventsClient($this->client, $this->options); @@ -75,10 +74,11 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function list(ListUsersRequest $request = new ListUsersRequest(), ?array $options = null): array { + public function list(ListUsersRequest $request = new ListUsersRequest(), ?array $options = null): array + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->limit != null){ + if ($request->limit != null) { $query['limit'] = $request->limit; } try { @@ -92,15 +92,15 @@ public function list(ListUsersRequest $request = new ListUsersRequest(), ?array $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, [User::class]); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/mixed-file-directory/src/Utils/File.php b/seed/php-sdk/mixed-file-directory/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/mixed-file-directory/src/Utils/File.php +++ b/seed/php-sdk/mixed-file-directory/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/mixed-file-directory/tests/Core/Client/RawClientTest.php b/seed/php-sdk/mixed-file-directory/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/mixed-file-directory/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/mixed-file-directory/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/mixed-file-directory/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/mixed-file-directory/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/mixed-file-directory/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/mixed-file-directory/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/mixed-file-directory/tests/Core/Json/EnumTest.php b/seed/php-sdk/mixed-file-directory/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/mixed-file-directory/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/mixed-file-directory/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/mixed-file-directory/tests/Core/Json/TraitTest.php b/seed/php-sdk/mixed-file-directory/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/mixed-file-directory/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/mixed-file-directory/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/mixed-file-directory/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/mixed-file-directory/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/mixed-file-directory/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/mixed-file-directory/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/multi-line-docs/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/multi-line-docs/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/multi-line-docs/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/multi-line-docs/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/multi-line-docs/src/Core/Client/RawClient.php b/seed/php-sdk/multi-line-docs/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/multi-line-docs/src/Core/Client/RawClient.php +++ b/seed/php-sdk/multi-line-docs/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/multi-line-docs/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/multi-line-docs/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/multi-line-docs/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/multi-line-docs/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/multi-line-docs/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/multi-line-docs/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/multi-line-docs/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/multi-line-docs/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/multi-line-docs/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/multi-line-docs/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/multi-line-docs/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/multi-line-docs/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/multi-line-docs/src/Core/Types/ArrayType.php b/seed/php-sdk/multi-line-docs/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/multi-line-docs/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/multi-line-docs/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/multi-line-docs/src/Core/Types/Constant.php b/seed/php-sdk/multi-line-docs/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/multi-line-docs/src/Core/Types/Constant.php +++ b/seed/php-sdk/multi-line-docs/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/multi-line-docs/src/Core/Types/Union.php b/seed/php-sdk/multi-line-docs/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/multi-line-docs/src/Core/Types/Union.php +++ b/seed/php-sdk/multi-line-docs/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/multi-line-docs/src/Exceptions/SeedApiException.php b/seed/php-sdk/multi-line-docs/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/multi-line-docs/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/multi-line-docs/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/multi-line-docs/src/Exceptions/SeedException.php b/seed/php-sdk/multi-line-docs/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/multi-line-docs/src/Exceptions/SeedException.php +++ b/seed/php-sdk/multi-line-docs/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/multi-line-docs/src/SeedClient.php b/seed/php-sdk/multi-line-docs/src/SeedClient.php index a13bf5acc053..ee87d1cb303f 100644 --- a/seed/php-sdk/multi-line-docs/src/SeedClient.php +++ b/seed/php-sdk/multi-line-docs/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var UserClient $user @@ -40,26 +40,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->user = new UserClient($this->client, $this->options); } } diff --git a/seed/php-sdk/multi-line-docs/src/Types/Operand.php b/seed/php-sdk/multi-line-docs/src/Types/Operand.php index a2bb43367db1..e750c5677cc2 100644 --- a/seed/php-sdk/multi-line-docs/src/Types/Operand.php +++ b/seed/php-sdk/multi-line-docs/src/Types/Operand.php @@ -2,8 +2,8 @@ namespace Seed\Types; -enum Operand - : string { +enum Operand: string +{ case GreaterThan = ">"; case EqualTo = "="; case LessThan = "less_than"; diff --git a/seed/php-sdk/multi-line-docs/src/User/Requests/CreateUserRequest.php b/seed/php-sdk/multi-line-docs/src/User/Requests/CreateUserRequest.php index d9bac111dc99..51a80ef5308f 100644 --- a/seed/php-sdk/multi-line-docs/src/User/Requests/CreateUserRequest.php +++ b/seed/php-sdk/multi-line-docs/src/User/Requests/CreateUserRequest.php @@ -33,8 +33,8 @@ class CreateUserRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->age = $values['age'] ?? null; + ) { + $this->name = $values['name']; + $this->age = $values['age'] ?? null; } } diff --git a/seed/php-sdk/multi-line-docs/src/User/Types/User.php b/seed/php-sdk/multi-line-docs/src/User/Types/User.php index a57d7fc737dc..701845b43300 100644 --- a/seed/php-sdk/multi-line-docs/src/User/Types/User.php +++ b/seed/php-sdk/multi-line-docs/src/User/Types/User.php @@ -44,15 +44,17 @@ class User extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->name = $values['name'];$this->age = $values['age'] ?? null; + ) { + $this->id = $values['id']; + $this->name = $values['name']; + $this->age = $values['age'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/multi-line-docs/src/User/UserClient.php b/seed/php-sdk/multi-line-docs/src/User/UserClient.php index c837a2d080ec..4b965caa78b3 100644 --- a/seed/php-sdk/multi-line-docs/src/User/UserClient.php +++ b/seed/php-sdk/multi-line-docs/src/User/UserClient.php @@ -14,7 +14,7 @@ use Seed\User\Types\User; use JsonException; -class UserClient +class UserClient { /** * @var array{ @@ -42,11 +42,10 @@ class UserClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -70,7 +69,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getUser(string $userId, ?array $options = null): void { + public function getUser(string $userId, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -82,12 +82,12 @@ public function getUser(string $userId, ?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -122,7 +122,8 @@ public function getUser(string $userId, ?array $options = null): void { * @throws SeedException * @throws SeedApiException */ - public function createUser(CreateUserRequest $request, ?array $options = null): User { + public function createUser(CreateUserRequest $request, ?array $options = null): User + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -135,15 +136,15 @@ public function createUser(CreateUserRequest $request, ?array $options = null): $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return User::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/multi-line-docs/src/Utils/File.php b/seed/php-sdk/multi-line-docs/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/multi-line-docs/src/Utils/File.php +++ b/seed/php-sdk/multi-line-docs/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/multi-line-docs/tests/Core/Client/RawClientTest.php b/seed/php-sdk/multi-line-docs/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/multi-line-docs/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/multi-line-docs/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/multi-line-docs/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/multi-line-docs/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/multi-line-docs/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/multi-line-docs/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/multi-line-docs/tests/Core/Json/EnumTest.php b/seed/php-sdk/multi-line-docs/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/multi-line-docs/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/multi-line-docs/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/multi-line-docs/tests/Core/Json/TraitTest.php b/seed/php-sdk/multi-line-docs/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/multi-line-docs/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/multi-line-docs/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/multi-line-docs/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/multi-line-docs/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/multi-line-docs/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/multi-line-docs/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/multi-url-environment-no-default/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/multi-url-environment-no-default/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/multi-url-environment-no-default/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/multi-url-environment-no-default/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/multi-url-environment-no-default/src/Core/Client/RawClient.php b/seed/php-sdk/multi-url-environment-no-default/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/multi-url-environment-no-default/src/Core/Client/RawClient.php +++ b/seed/php-sdk/multi-url-environment-no-default/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/multi-url-environment-no-default/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/multi-url-environment-no-default/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/multi-url-environment-no-default/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/multi-url-environment-no-default/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/multi-url-environment-no-default/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/multi-url-environment-no-default/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/multi-url-environment-no-default/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/multi-url-environment-no-default/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/multi-url-environment-no-default/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/multi-url-environment-no-default/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/multi-url-environment-no-default/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/multi-url-environment-no-default/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/multi-url-environment-no-default/src/Core/Types/ArrayType.php b/seed/php-sdk/multi-url-environment-no-default/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/multi-url-environment-no-default/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/multi-url-environment-no-default/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/multi-url-environment-no-default/src/Core/Types/Constant.php b/seed/php-sdk/multi-url-environment-no-default/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/multi-url-environment-no-default/src/Core/Types/Constant.php +++ b/seed/php-sdk/multi-url-environment-no-default/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/multi-url-environment-no-default/src/Core/Types/Union.php b/seed/php-sdk/multi-url-environment-no-default/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/multi-url-environment-no-default/src/Core/Types/Union.php +++ b/seed/php-sdk/multi-url-environment-no-default/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/multi-url-environment-no-default/src/Ec2/Ec2Client.php b/seed/php-sdk/multi-url-environment-no-default/src/Ec2/Ec2Client.php index fd3e20711aa9..78a905659250 100644 --- a/seed/php-sdk/multi-url-environment-no-default/src/Ec2/Ec2Client.php +++ b/seed/php-sdk/multi-url-environment-no-default/src/Ec2/Ec2Client.php @@ -13,7 +13,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class Ec2Client +class Ec2Client { /** * @var array{ @@ -39,11 +39,10 @@ class Ec2Client * @param RawClient $client * @param Environments $environment */ - function __construct( + public function __construct( RawClient $client, Environments $environment, - ) - { + ) { $this->client = $client; $this->environment = $environment; $this->options = []; @@ -61,7 +60,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function bootInstance(BootInstanceRequest $request, ?array $options = null): void { + public function bootInstance(BootInstanceRequest $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -74,12 +74,12 @@ public function bootInstance(BootInstanceRequest $request, ?array $options = nul $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/multi-url-environment-no-default/src/Ec2/Requests/BootInstanceRequest.php b/seed/php-sdk/multi-url-environment-no-default/src/Ec2/Requests/BootInstanceRequest.php index 0268f9ee1107..cc00599b7e5f 100644 --- a/seed/php-sdk/multi-url-environment-no-default/src/Ec2/Requests/BootInstanceRequest.php +++ b/seed/php-sdk/multi-url-environment-no-default/src/Ec2/Requests/BootInstanceRequest.php @@ -20,8 +20,7 @@ class BootInstanceRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->size = $values['size']; } } diff --git a/seed/php-sdk/multi-url-environment-no-default/src/Environments.php b/seed/php-sdk/multi-url-environment-no-default/src/Environments.php index a965ab69921e..d197650b72ad 100644 --- a/seed/php-sdk/multi-url-environment-no-default/src/Environments.php +++ b/seed/php-sdk/multi-url-environment-no-default/src/Environments.php @@ -5,7 +5,7 @@ /** * Represents the available environments for the API with multiple base URLs. */ -class Environments +class Environments { /** * @var string $ec2 @@ -24,8 +24,7 @@ class Environments private function __construct( string $ec2, string $s3, - ) - { + ) { $this->ec2 = $ec2; $this->s3 = $s3; } @@ -35,7 +34,8 @@ private function __construct( * * @return Environments */ - public static function Production(): Environments { + public static function Production(): Environments + { return new self( ec2: 'https://ec2.aws.com', s3: 'https://s3.aws.com' @@ -47,7 +47,8 @@ public static function Production(): Environments { * * @return Environments */ - public static function Staging(): Environments { + public static function Staging(): Environments + { return new self( ec2: 'https://staging.ec2.aws.com', s3: 'https://staging.s3.aws.com' @@ -61,7 +62,8 @@ public static function Staging(): Environments { * @param string $s3 The s3 base URL * @return Environments */ - public static function custom(string $ec2, string $s3): Environments { + public static function custom(string $ec2, string $s3): Environments + { return new self( ec2: $ec2, s3: $s3 diff --git a/seed/php-sdk/multi-url-environment-no-default/src/Exceptions/SeedApiException.php b/seed/php-sdk/multi-url-environment-no-default/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/multi-url-environment-no-default/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/multi-url-environment-no-default/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/multi-url-environment-no-default/src/Exceptions/SeedException.php b/seed/php-sdk/multi-url-environment-no-default/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/multi-url-environment-no-default/src/Exceptions/SeedException.php +++ b/seed/php-sdk/multi-url-environment-no-default/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/multi-url-environment-no-default/src/S3/Requests/GetPresignedUrlRequest.php b/seed/php-sdk/multi-url-environment-no-default/src/S3/Requests/GetPresignedUrlRequest.php index 8f7cb59df497..4f64517e0137 100644 --- a/seed/php-sdk/multi-url-environment-no-default/src/S3/Requests/GetPresignedUrlRequest.php +++ b/seed/php-sdk/multi-url-environment-no-default/src/S3/Requests/GetPresignedUrlRequest.php @@ -20,8 +20,7 @@ class GetPresignedUrlRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->s3Key = $values['s3Key']; } } diff --git a/seed/php-sdk/multi-url-environment-no-default/src/S3/S3Client.php b/seed/php-sdk/multi-url-environment-no-default/src/S3/S3Client.php index a9cafc5bf56a..5bd36eb48f98 100644 --- a/seed/php-sdk/multi-url-environment-no-default/src/S3/S3Client.php +++ b/seed/php-sdk/multi-url-environment-no-default/src/S3/S3Client.php @@ -15,7 +15,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class S3Client +class S3Client { /** * @var array{ @@ -41,11 +41,10 @@ class S3Client * @param RawClient $client * @param Environments $environment */ - function __construct( + public function __construct( RawClient $client, Environments $environment, - ) - { + ) { $this->client = $client; $this->environment = $environment; $this->options = []; @@ -64,7 +63,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getPresignedUrl(GetPresignedUrlRequest $request, ?array $options = null): string { + public function getPresignedUrl(GetPresignedUrlRequest $request, ?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -77,15 +77,15 @@ public function getPresignedUrl(GetPresignedUrlRequest $request, ?array $options $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/multi-url-environment-no-default/src/SeedClient.php b/seed/php-sdk/multi-url-environment-no-default/src/SeedClient.php index 2424cd81c8b8..c2f87b61a53c 100644 --- a/seed/php-sdk/multi-url-environment-no-default/src/SeedClient.php +++ b/seed/php-sdk/multi-url-environment-no-default/src/SeedClient.php @@ -7,7 +7,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var Ec2Client $ec2 @@ -53,8 +53,7 @@ public function __construct( string $token, Environments $environment, ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'Authorization' => "Bearer $token", 'X-Fern-Language' => 'PHP', @@ -62,19 +61,19 @@ public function __construct( 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; $this->environment = $environment; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->ec2 = new Ec2Client($this->client, $this->environment); $this->s3 = new S3Client($this->client, $this->environment); } diff --git a/seed/php-sdk/multi-url-environment-no-default/src/Utils/File.php b/seed/php-sdk/multi-url-environment-no-default/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/multi-url-environment-no-default/src/Utils/File.php +++ b/seed/php-sdk/multi-url-environment-no-default/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/multi-url-environment-no-default/tests/Core/Client/RawClientTest.php b/seed/php-sdk/multi-url-environment-no-default/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/multi-url-environment-no-default/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/multi-url-environment-no-default/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/multi-url-environment-no-default/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/multi-url-environment-no-default/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/multi-url-environment-no-default/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/multi-url-environment-no-default/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/multi-url-environment-no-default/tests/Core/Json/EnumTest.php b/seed/php-sdk/multi-url-environment-no-default/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/multi-url-environment-no-default/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/multi-url-environment-no-default/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/multi-url-environment-no-default/tests/Core/Json/TraitTest.php b/seed/php-sdk/multi-url-environment-no-default/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/multi-url-environment-no-default/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/multi-url-environment-no-default/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/multi-url-environment-no-default/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/multi-url-environment-no-default/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/multi-url-environment-no-default/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/multi-url-environment-no-default/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/multi-url-environment/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/multi-url-environment/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/multi-url-environment/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/multi-url-environment/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/multi-url-environment/src/Core/Client/RawClient.php b/seed/php-sdk/multi-url-environment/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/multi-url-environment/src/Core/Client/RawClient.php +++ b/seed/php-sdk/multi-url-environment/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/multi-url-environment/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/multi-url-environment/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/multi-url-environment/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/multi-url-environment/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/multi-url-environment/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/multi-url-environment/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/multi-url-environment/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/multi-url-environment/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/multi-url-environment/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/multi-url-environment/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/multi-url-environment/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/multi-url-environment/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/multi-url-environment/src/Core/Types/ArrayType.php b/seed/php-sdk/multi-url-environment/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/multi-url-environment/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/multi-url-environment/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/multi-url-environment/src/Core/Types/Constant.php b/seed/php-sdk/multi-url-environment/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/multi-url-environment/src/Core/Types/Constant.php +++ b/seed/php-sdk/multi-url-environment/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/multi-url-environment/src/Core/Types/Union.php b/seed/php-sdk/multi-url-environment/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/multi-url-environment/src/Core/Types/Union.php +++ b/seed/php-sdk/multi-url-environment/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/multi-url-environment/src/Ec2/Ec2Client.php b/seed/php-sdk/multi-url-environment/src/Ec2/Ec2Client.php index fd3e20711aa9..78a905659250 100644 --- a/seed/php-sdk/multi-url-environment/src/Ec2/Ec2Client.php +++ b/seed/php-sdk/multi-url-environment/src/Ec2/Ec2Client.php @@ -13,7 +13,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class Ec2Client +class Ec2Client { /** * @var array{ @@ -39,11 +39,10 @@ class Ec2Client * @param RawClient $client * @param Environments $environment */ - function __construct( + public function __construct( RawClient $client, Environments $environment, - ) - { + ) { $this->client = $client; $this->environment = $environment; $this->options = []; @@ -61,7 +60,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function bootInstance(BootInstanceRequest $request, ?array $options = null): void { + public function bootInstance(BootInstanceRequest $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -74,12 +74,12 @@ public function bootInstance(BootInstanceRequest $request, ?array $options = nul $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/multi-url-environment/src/Ec2/Requests/BootInstanceRequest.php b/seed/php-sdk/multi-url-environment/src/Ec2/Requests/BootInstanceRequest.php index 0268f9ee1107..cc00599b7e5f 100644 --- a/seed/php-sdk/multi-url-environment/src/Ec2/Requests/BootInstanceRequest.php +++ b/seed/php-sdk/multi-url-environment/src/Ec2/Requests/BootInstanceRequest.php @@ -20,8 +20,7 @@ class BootInstanceRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->size = $values['size']; } } diff --git a/seed/php-sdk/multi-url-environment/src/Environments.php b/seed/php-sdk/multi-url-environment/src/Environments.php index a965ab69921e..d197650b72ad 100644 --- a/seed/php-sdk/multi-url-environment/src/Environments.php +++ b/seed/php-sdk/multi-url-environment/src/Environments.php @@ -5,7 +5,7 @@ /** * Represents the available environments for the API with multiple base URLs. */ -class Environments +class Environments { /** * @var string $ec2 @@ -24,8 +24,7 @@ class Environments private function __construct( string $ec2, string $s3, - ) - { + ) { $this->ec2 = $ec2; $this->s3 = $s3; } @@ -35,7 +34,8 @@ private function __construct( * * @return Environments */ - public static function Production(): Environments { + public static function Production(): Environments + { return new self( ec2: 'https://ec2.aws.com', s3: 'https://s3.aws.com' @@ -47,7 +47,8 @@ public static function Production(): Environments { * * @return Environments */ - public static function Staging(): Environments { + public static function Staging(): Environments + { return new self( ec2: 'https://staging.ec2.aws.com', s3: 'https://staging.s3.aws.com' @@ -61,7 +62,8 @@ public static function Staging(): Environments { * @param string $s3 The s3 base URL * @return Environments */ - public static function custom(string $ec2, string $s3): Environments { + public static function custom(string $ec2, string $s3): Environments + { return new self( ec2: $ec2, s3: $s3 diff --git a/seed/php-sdk/multi-url-environment/src/Exceptions/SeedApiException.php b/seed/php-sdk/multi-url-environment/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/multi-url-environment/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/multi-url-environment/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/multi-url-environment/src/Exceptions/SeedException.php b/seed/php-sdk/multi-url-environment/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/multi-url-environment/src/Exceptions/SeedException.php +++ b/seed/php-sdk/multi-url-environment/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/multi-url-environment/src/S3/Requests/GetPresignedUrlRequest.php b/seed/php-sdk/multi-url-environment/src/S3/Requests/GetPresignedUrlRequest.php index 8f7cb59df497..4f64517e0137 100644 --- a/seed/php-sdk/multi-url-environment/src/S3/Requests/GetPresignedUrlRequest.php +++ b/seed/php-sdk/multi-url-environment/src/S3/Requests/GetPresignedUrlRequest.php @@ -20,8 +20,7 @@ class GetPresignedUrlRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->s3Key = $values['s3Key']; } } diff --git a/seed/php-sdk/multi-url-environment/src/S3/S3Client.php b/seed/php-sdk/multi-url-environment/src/S3/S3Client.php index a9cafc5bf56a..5bd36eb48f98 100644 --- a/seed/php-sdk/multi-url-environment/src/S3/S3Client.php +++ b/seed/php-sdk/multi-url-environment/src/S3/S3Client.php @@ -15,7 +15,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class S3Client +class S3Client { /** * @var array{ @@ -41,11 +41,10 @@ class S3Client * @param RawClient $client * @param Environments $environment */ - function __construct( + public function __construct( RawClient $client, Environments $environment, - ) - { + ) { $this->client = $client; $this->environment = $environment; $this->options = []; @@ -64,7 +63,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getPresignedUrl(GetPresignedUrlRequest $request, ?array $options = null): string { + public function getPresignedUrl(GetPresignedUrlRequest $request, ?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -77,15 +77,15 @@ public function getPresignedUrl(GetPresignedUrlRequest $request, ?array $options $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/multi-url-environment/src/SeedClient.php b/seed/php-sdk/multi-url-environment/src/SeedClient.php index d2a4fa7db0f8..416c0146652f 100644 --- a/seed/php-sdk/multi-url-environment/src/SeedClient.php +++ b/seed/php-sdk/multi-url-environment/src/SeedClient.php @@ -7,7 +7,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var Ec2Client $ec2 @@ -53,8 +53,7 @@ public function __construct( string $token, ?Environments $environment = null, ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'Authorization' => "Bearer $token", 'X-Fern-Language' => 'PHP', @@ -62,20 +61,20 @@ public function __construct( 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; $environment ??= Environments::Production(); $this->environment = $environment; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->ec2 = new Ec2Client($this->client, $this->environment); $this->s3 = new S3Client($this->client, $this->environment); } diff --git a/seed/php-sdk/multi-url-environment/src/Utils/File.php b/seed/php-sdk/multi-url-environment/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/multi-url-environment/src/Utils/File.php +++ b/seed/php-sdk/multi-url-environment/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/multi-url-environment/tests/Core/Client/RawClientTest.php b/seed/php-sdk/multi-url-environment/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/multi-url-environment/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/multi-url-environment/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/multi-url-environment/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/multi-url-environment/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/multi-url-environment/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/multi-url-environment/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/multi-url-environment/tests/Core/Json/EnumTest.php b/seed/php-sdk/multi-url-environment/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/multi-url-environment/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/multi-url-environment/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/multi-url-environment/tests/Core/Json/TraitTest.php b/seed/php-sdk/multi-url-environment/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/multi-url-environment/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/multi-url-environment/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/multi-url-environment/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/multi-url-environment/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/multi-url-environment/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/multi-url-environment/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/multiple-request-bodies/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/multiple-request-bodies/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/multiple-request-bodies/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/multiple-request-bodies/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/multiple-request-bodies/src/Core/Client/RawClient.php b/seed/php-sdk/multiple-request-bodies/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/multiple-request-bodies/src/Core/Client/RawClient.php +++ b/seed/php-sdk/multiple-request-bodies/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/multiple-request-bodies/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/multiple-request-bodies/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/multiple-request-bodies/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/multiple-request-bodies/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/multiple-request-bodies/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/multiple-request-bodies/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/multiple-request-bodies/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/multiple-request-bodies/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/multiple-request-bodies/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/multiple-request-bodies/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/multiple-request-bodies/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/multiple-request-bodies/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/multiple-request-bodies/src/Core/Types/ArrayType.php b/seed/php-sdk/multiple-request-bodies/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/multiple-request-bodies/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/multiple-request-bodies/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/multiple-request-bodies/src/Core/Types/Constant.php b/seed/php-sdk/multiple-request-bodies/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/multiple-request-bodies/src/Core/Types/Constant.php +++ b/seed/php-sdk/multiple-request-bodies/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/multiple-request-bodies/src/Core/Types/Union.php b/seed/php-sdk/multiple-request-bodies/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/multiple-request-bodies/src/Core/Types/Union.php +++ b/seed/php-sdk/multiple-request-bodies/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/multiple-request-bodies/src/Environments.php b/seed/php-sdk/multiple-request-bodies/src/Environments.php index 62b1df8a67bb..3ea530cecf43 100644 --- a/seed/php-sdk/multiple-request-bodies/src/Environments.php +++ b/seed/php-sdk/multiple-request-bodies/src/Environments.php @@ -2,7 +2,7 @@ namespace Seed; -enum Environments - : string { +enum Environments: string +{ case Default_ = "https://api.example.com/v1"; } diff --git a/seed/php-sdk/multiple-request-bodies/src/Exceptions/SeedApiException.php b/seed/php-sdk/multiple-request-bodies/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/multiple-request-bodies/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/multiple-request-bodies/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/multiple-request-bodies/src/Exceptions/SeedException.php b/seed/php-sdk/multiple-request-bodies/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/multiple-request-bodies/src/Exceptions/SeedException.php +++ b/seed/php-sdk/multiple-request-bodies/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/multiple-request-bodies/src/Requests/UploadDocumentRequest.php b/seed/php-sdk/multiple-request-bodies/src/Requests/UploadDocumentRequest.php index c9a9b2e436eb..87d9edbef240 100644 --- a/seed/php-sdk/multiple-request-bodies/src/Requests/UploadDocumentRequest.php +++ b/seed/php-sdk/multiple-request-bodies/src/Requests/UploadDocumentRequest.php @@ -35,8 +35,9 @@ class UploadDocumentRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->author = $values['author'] ?? null;$this->tags = $values['tags'] ?? null;$this->title = $values['title'] ?? null; + ) { + $this->author = $values['author'] ?? null; + $this->tags = $values['tags'] ?? null; + $this->title = $values['title'] ?? null; } } diff --git a/seed/php-sdk/multiple-request-bodies/src/SeedClient.php b/seed/php-sdk/multiple-request-bodies/src/SeedClient.php index 45b4703f073a..443b8b9848f8 100644 --- a/seed/php-sdk/multiple-request-bodies/src/SeedClient.php +++ b/seed/php-sdk/multiple-request-bodies/src/SeedClient.php @@ -17,7 +17,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class SeedClient +class SeedClient { /** * @var array{ @@ -48,8 +48,7 @@ class SeedClient public function __construct( string $token, ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'Authorization' => "Bearer $token", 'X-Fern-Language' => 'PHP', @@ -57,14 +56,14 @@ public function __construct( 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); @@ -87,7 +86,8 @@ public function __construct( * @throws SeedException * @throws SeedApiException */ - public function uploadJsonDocument(UploadDocumentRequest $request = new UploadDocumentRequest(), ?array $options = null): DocumentMetadata|DocumentUploadResult { + public function uploadJsonDocument(UploadDocumentRequest $request = new UploadDocumentRequest(), ?array $options = null): DocumentMetadata|DocumentUploadResult + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -100,15 +100,15 @@ public function uploadJsonDocument(UploadDocumentRequest $request = new UploadDo $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeUnion($json, new Union(DocumentMetadata::class, DocumentUploadResult::class)); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -142,7 +142,8 @@ public function uploadJsonDocument(UploadDocumentRequest $request = new UploadDo * @throws SeedException * @throws SeedApiException */ - public function uploadPdfDocument(?array $options = null): DocumentMetadata|DocumentUploadResult { + public function uploadPdfDocument(?array $options = null): DocumentMetadata|DocumentUploadResult + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -154,15 +155,15 @@ public function uploadPdfDocument(?array $options = null): DocumentMetadata|Docu $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeUnion($json, new Union(DocumentMetadata::class, DocumentUploadResult::class)); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/multiple-request-bodies/src/Types/DocumentMetadata.php b/seed/php-sdk/multiple-request-bodies/src/Types/DocumentMetadata.php index 0fb6e62e5471..ebb0a0265c53 100644 --- a/seed/php-sdk/multiple-request-bodies/src/Types/DocumentMetadata.php +++ b/seed/php-sdk/multiple-request-bodies/src/Types/DocumentMetadata.php @@ -42,15 +42,18 @@ class DocumentMetadata extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->author = $values['author'] ?? null;$this->id = $values['id'] ?? null;$this->tags = $values['tags'] ?? null;$this->title = $values['title'] ?? null; + ) { + $this->author = $values['author'] ?? null; + $this->id = $values['id'] ?? null; + $this->tags = $values['tags'] ?? null; + $this->title = $values['title'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/multiple-request-bodies/src/Types/DocumentUploadResult.php b/seed/php-sdk/multiple-request-bodies/src/Types/DocumentUploadResult.php index a685a6aa9723..b97a3a73693e 100644 --- a/seed/php-sdk/multiple-request-bodies/src/Types/DocumentUploadResult.php +++ b/seed/php-sdk/multiple-request-bodies/src/Types/DocumentUploadResult.php @@ -27,15 +27,16 @@ class DocumentUploadResult extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->fileId = $values['fileId'] ?? null;$this->status = $values['status'] ?? null; + ) { + $this->fileId = $values['fileId'] ?? null; + $this->status = $values['status'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/multiple-request-bodies/src/Utils/File.php b/seed/php-sdk/multiple-request-bodies/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/multiple-request-bodies/src/Utils/File.php +++ b/seed/php-sdk/multiple-request-bodies/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/multiple-request-bodies/tests/Core/Client/RawClientTest.php b/seed/php-sdk/multiple-request-bodies/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/multiple-request-bodies/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/multiple-request-bodies/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/multiple-request-bodies/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/multiple-request-bodies/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/multiple-request-bodies/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/multiple-request-bodies/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/multiple-request-bodies/tests/Core/Json/EnumTest.php b/seed/php-sdk/multiple-request-bodies/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/multiple-request-bodies/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/multiple-request-bodies/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/multiple-request-bodies/tests/Core/Json/TraitTest.php b/seed/php-sdk/multiple-request-bodies/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/multiple-request-bodies/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/multiple-request-bodies/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/multiple-request-bodies/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/multiple-request-bodies/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/multiple-request-bodies/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/multiple-request-bodies/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/no-environment/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/no-environment/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/no-environment/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/no-environment/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/no-environment/src/Core/Client/RawClient.php b/seed/php-sdk/no-environment/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/no-environment/src/Core/Client/RawClient.php +++ b/seed/php-sdk/no-environment/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/no-environment/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/no-environment/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/no-environment/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/no-environment/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/no-environment/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/no-environment/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/no-environment/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/no-environment/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/no-environment/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/no-environment/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/no-environment/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/no-environment/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/no-environment/src/Core/Types/ArrayType.php b/seed/php-sdk/no-environment/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/no-environment/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/no-environment/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/no-environment/src/Core/Types/Constant.php b/seed/php-sdk/no-environment/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/no-environment/src/Core/Types/Constant.php +++ b/seed/php-sdk/no-environment/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/no-environment/src/Core/Types/Union.php b/seed/php-sdk/no-environment/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/no-environment/src/Core/Types/Union.php +++ b/seed/php-sdk/no-environment/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/no-environment/src/Dummy/DummyClient.php b/seed/php-sdk/no-environment/src/Dummy/DummyClient.php index 1fe039c12c39..94dfc4ade2cb 100644 --- a/seed/php-sdk/no-environment/src/Dummy/DummyClient.php +++ b/seed/php-sdk/no-environment/src/Dummy/DummyClient.php @@ -13,7 +13,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class DummyClient +class DummyClient { /** * @var array{ @@ -41,11 +41,10 @@ class DummyClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -63,7 +62,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getDummy(?array $options = null): string { + public function getDummy(?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -75,15 +75,15 @@ public function getDummy(?array $options = null): string { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/no-environment/src/Exceptions/SeedApiException.php b/seed/php-sdk/no-environment/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/no-environment/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/no-environment/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/no-environment/src/Exceptions/SeedException.php b/seed/php-sdk/no-environment/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/no-environment/src/Exceptions/SeedException.php +++ b/seed/php-sdk/no-environment/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/no-environment/src/SeedClient.php b/seed/php-sdk/no-environment/src/SeedClient.php index 1b7f12777f12..35ffb9b5f682 100644 --- a/seed/php-sdk/no-environment/src/SeedClient.php +++ b/seed/php-sdk/no-environment/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var DummyClient $dummy @@ -42,8 +42,7 @@ class SeedClient public function __construct( string $token, ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'Authorization' => "Bearer $token", 'X-Fern-Language' => 'PHP', @@ -51,18 +50,18 @@ public function __construct( 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->dummy = new DummyClient($this->client, $this->options); } } diff --git a/seed/php-sdk/no-environment/src/Utils/File.php b/seed/php-sdk/no-environment/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/no-environment/src/Utils/File.php +++ b/seed/php-sdk/no-environment/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/no-environment/tests/Core/Client/RawClientTest.php b/seed/php-sdk/no-environment/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/no-environment/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/no-environment/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/no-environment/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/no-environment/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/no-environment/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/no-environment/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/no-environment/tests/Core/Json/EnumTest.php b/seed/php-sdk/no-environment/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/no-environment/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/no-environment/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/no-environment/tests/Core/Json/TraitTest.php b/seed/php-sdk/no-environment/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/no-environment/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/no-environment/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/no-environment/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/no-environment/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/no-environment/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/no-environment/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/no-retries/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/no-retries/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/no-retries/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/no-retries/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/no-retries/src/Core/Client/RawClient.php b/seed/php-sdk/no-retries/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/no-retries/src/Core/Client/RawClient.php +++ b/seed/php-sdk/no-retries/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/no-retries/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/no-retries/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/no-retries/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/no-retries/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/no-retries/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/no-retries/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/no-retries/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/no-retries/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/no-retries/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/no-retries/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/no-retries/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/no-retries/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/no-retries/src/Core/Types/ArrayType.php b/seed/php-sdk/no-retries/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/no-retries/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/no-retries/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/no-retries/src/Core/Types/Constant.php b/seed/php-sdk/no-retries/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/no-retries/src/Core/Types/Constant.php +++ b/seed/php-sdk/no-retries/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/no-retries/src/Core/Types/Union.php b/seed/php-sdk/no-retries/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/no-retries/src/Core/Types/Union.php +++ b/seed/php-sdk/no-retries/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/no-retries/src/Exceptions/SeedApiException.php b/seed/php-sdk/no-retries/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/no-retries/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/no-retries/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/no-retries/src/Exceptions/SeedException.php b/seed/php-sdk/no-retries/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/no-retries/src/Exceptions/SeedException.php +++ b/seed/php-sdk/no-retries/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/no-retries/src/Retries/RetriesClient.php b/seed/php-sdk/no-retries/src/Retries/RetriesClient.php index 097a0ec4bd07..8a8ad13775d3 100644 --- a/seed/php-sdk/no-retries/src/Retries/RetriesClient.php +++ b/seed/php-sdk/no-retries/src/Retries/RetriesClient.php @@ -14,7 +14,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class RetriesClient +class RetriesClient { /** * @var array{ @@ -42,11 +42,10 @@ class RetriesClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -64,7 +63,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getUsers(?array $options = null): array { + public function getUsers(?array $options = null): array + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -76,15 +76,15 @@ public function getUsers(?array $options = null): array { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, [User::class]); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/no-retries/src/Retries/Types/User.php b/seed/php-sdk/no-retries/src/Retries/Types/User.php index ed733395fe6d..c047d00c7831 100644 --- a/seed/php-sdk/no-retries/src/Retries/Types/User.php +++ b/seed/php-sdk/no-retries/src/Retries/Types/User.php @@ -27,15 +27,16 @@ class User extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->name = $values['name']; + ) { + $this->id = $values['id']; + $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/no-retries/src/SeedClient.php b/seed/php-sdk/no-retries/src/SeedClient.php index 0f7385c2107e..449edddc39da 100644 --- a/seed/php-sdk/no-retries/src/SeedClient.php +++ b/seed/php-sdk/no-retries/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var RetriesClient $retries @@ -40,26 +40,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->retries = new RetriesClient($this->client, $this->options); } } diff --git a/seed/php-sdk/no-retries/src/Utils/File.php b/seed/php-sdk/no-retries/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/no-retries/src/Utils/File.php +++ b/seed/php-sdk/no-retries/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/no-retries/tests/Core/Client/RawClientTest.php b/seed/php-sdk/no-retries/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/no-retries/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/no-retries/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/no-retries/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/no-retries/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/no-retries/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/no-retries/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/no-retries/tests/Core/Json/EnumTest.php b/seed/php-sdk/no-retries/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/no-retries/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/no-retries/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/no-retries/tests/Core/Json/TraitTest.php b/seed/php-sdk/no-retries/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/no-retries/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/no-retries/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/no-retries/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/no-retries/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/no-retries/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/no-retries/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/nullable-optional/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/nullable-optional/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/nullable-optional/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/nullable-optional/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/nullable-optional/src/Core/Client/RawClient.php b/seed/php-sdk/nullable-optional/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/nullable-optional/src/Core/Client/RawClient.php +++ b/seed/php-sdk/nullable-optional/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/nullable-optional/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/nullable-optional/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/nullable-optional/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/nullable-optional/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/nullable-optional/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/nullable-optional/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/nullable-optional/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/nullable-optional/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/nullable-optional/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/nullable-optional/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/nullable-optional/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/nullable-optional/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/nullable-optional/src/Core/Types/ArrayType.php b/seed/php-sdk/nullable-optional/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/nullable-optional/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/nullable-optional/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/nullable-optional/src/Core/Types/Constant.php b/seed/php-sdk/nullable-optional/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/nullable-optional/src/Core/Types/Constant.php +++ b/seed/php-sdk/nullable-optional/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/nullable-optional/src/Core/Types/Union.php b/seed/php-sdk/nullable-optional/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/nullable-optional/src/Core/Types/Union.php +++ b/seed/php-sdk/nullable-optional/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/nullable-optional/src/Exceptions/SeedApiException.php b/seed/php-sdk/nullable-optional/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/nullable-optional/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/nullable-optional/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/nullable-optional/src/Exceptions/SeedException.php b/seed/php-sdk/nullable-optional/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/nullable-optional/src/Exceptions/SeedException.php +++ b/seed/php-sdk/nullable-optional/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/nullable-optional/src/NullableOptional/NullableOptionalClient.php b/seed/php-sdk/nullable-optional/src/NullableOptional/NullableOptionalClient.php index 6883b85fe8a3..49980889b880 100644 --- a/seed/php-sdk/nullable-optional/src/NullableOptional/NullableOptionalClient.php +++ b/seed/php-sdk/nullable-optional/src/NullableOptional/NullableOptionalClient.php @@ -27,7 +27,7 @@ use Seed\NullableOptional\Requests\SearchRequest; use Seed\NullableOptional\Types\SearchResult; -class NullableOptionalClient +class NullableOptionalClient { /** * @var array{ @@ -55,11 +55,10 @@ class NullableOptionalClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -80,7 +79,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getUser(string $userId, ?array $options = null): UserResponse { + public function getUser(string $userId, ?array $options = null): UserResponse + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -92,15 +92,15 @@ public function getUser(string $userId, ?array $options = null): UserResponse { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return UserResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -134,7 +134,8 @@ public function getUser(string $userId, ?array $options = null): UserResponse { * @throws SeedException * @throws SeedApiException */ - public function createUser(CreateUserRequest $request, ?array $options = null): UserResponse { + public function createUser(CreateUserRequest $request, ?array $options = null): UserResponse + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -147,15 +148,15 @@ public function createUser(CreateUserRequest $request, ?array $options = null): $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return UserResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -190,7 +191,8 @@ public function createUser(CreateUserRequest $request, ?array $options = null): * @throws SeedException * @throws SeedApiException */ - public function updateUser(string $userId, UpdateUserRequest $request, ?array $options = null): UserResponse { + public function updateUser(string $userId, UpdateUserRequest $request, ?array $options = null): UserResponse + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -203,15 +205,15 @@ public function updateUser(string $userId, UpdateUserRequest $request, ?array $o $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return UserResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -245,19 +247,20 @@ public function updateUser(string $userId, UpdateUserRequest $request, ?array $o * @throws SeedException * @throws SeedApiException */ - public function listUsers(ListUsersRequest $request = new ListUsersRequest(), ?array $options = null): array { + public function listUsers(ListUsersRequest $request = new ListUsersRequest(), ?array $options = null): array + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->limit != null){ + if ($request->limit != null) { $query['limit'] = $request->limit; } - if ($request->offset != null){ + if ($request->offset != null) { $query['offset'] = $request->offset; } - if ($request->includeDeleted != null){ + if ($request->includeDeleted != null) { $query['includeDeleted'] = $request->includeDeleted; } - if ($request->sortBy != null){ + if ($request->sortBy != null) { $query['sortBy'] = $request->sortBy; } try { @@ -271,15 +274,15 @@ public function listUsers(ListUsersRequest $request = new ListUsersRequest(), ?a $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, [UserResponse::class]); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -313,15 +316,16 @@ public function listUsers(ListUsersRequest $request = new ListUsersRequest(), ?a * @throws SeedException * @throws SeedApiException */ - public function searchUsers(SearchUsersRequest $request, ?array $options = null): array { + public function searchUsers(SearchUsersRequest $request, ?array $options = null): array + { $options = array_merge($this->options, $options ?? []); $query = []; $query['query'] = $request->query; $query['department'] = $request->department; - if ($request->role != null){ + if ($request->role != null) { $query['role'] = $request->role; } - if ($request->isActive != null){ + if ($request->isActive != null) { $query['isActive'] = $request->isActive; } try { @@ -335,15 +339,15 @@ public function searchUsers(SearchUsersRequest $request, ?array $options = null) $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, [UserResponse::class]); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -377,7 +381,8 @@ public function searchUsers(SearchUsersRequest $request, ?array $options = null) * @throws SeedException * @throws SeedApiException */ - public function createComplexProfile(ComplexProfile $request, ?array $options = null): ComplexProfile { + public function createComplexProfile(ComplexProfile $request, ?array $options = null): ComplexProfile + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -390,15 +395,15 @@ public function createComplexProfile(ComplexProfile $request, ?array $options = $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ComplexProfile::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -432,7 +437,8 @@ public function createComplexProfile(ComplexProfile $request, ?array $options = * @throws SeedException * @throws SeedApiException */ - public function getComplexProfile(string $profileId, ?array $options = null): ComplexProfile { + public function getComplexProfile(string $profileId, ?array $options = null): ComplexProfile + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -444,15 +450,15 @@ public function getComplexProfile(string $profileId, ?array $options = null): Co $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ComplexProfile::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -487,7 +493,8 @@ public function getComplexProfile(string $profileId, ?array $options = null): Co * @throws SeedException * @throws SeedApiException */ - public function updateComplexProfile(string $profileId, UpdateComplexProfileRequest $request = new UpdateComplexProfileRequest(), ?array $options = null): ComplexProfile { + public function updateComplexProfile(string $profileId, UpdateComplexProfileRequest $request = new UpdateComplexProfileRequest(), ?array $options = null): ComplexProfile + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -500,15 +507,15 @@ public function updateComplexProfile(string $profileId, UpdateComplexProfileRequ $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ComplexProfile::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -542,7 +549,8 @@ public function updateComplexProfile(string $profileId, UpdateComplexProfileRequ * @throws SeedException * @throws SeedApiException */ - public function testDeserialization(DeserializationTestRequest $request, ?array $options = null): DeserializationTestResponse { + public function testDeserialization(DeserializationTestRequest $request, ?array $options = null): DeserializationTestResponse + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -555,15 +563,15 @@ public function testDeserialization(DeserializationTestRequest $request, ?array $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return DeserializationTestResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -597,14 +605,15 @@ public function testDeserialization(DeserializationTestRequest $request, ?array * @throws SeedException * @throws SeedApiException */ - public function filterByRole(FilterByRoleRequest $request, ?array $options = null): array { + public function filterByRole(FilterByRoleRequest $request, ?array $options = null): array + { $options = array_merge($this->options, $options ?? []); $query = []; $query['role'] = $request->role; - if ($request->status != null){ + if ($request->status != null) { $query['status'] = $request->status; } - if ($request->secondaryRole != null){ + if ($request->secondaryRole != null) { $query['secondaryRole'] = $request->secondaryRole; } try { @@ -618,15 +627,15 @@ public function filterByRole(FilterByRoleRequest $request, ?array $options = nul $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, [UserResponse::class]); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -660,7 +669,8 @@ public function filterByRole(FilterByRoleRequest $request, ?array $options = nul * @throws SeedException * @throws SeedApiException */ - public function getNotificationSettings(string $userId, ?array $options = null): ?NotificationMethod { + public function getNotificationSettings(string $userId, ?array $options = null): ?NotificationMethod + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -672,18 +682,18 @@ public function getNotificationSettings(string $userId, ?array $options = null): $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); - if (empty($json)){ + if (empty($json)) { return null; } return NotificationMethod::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -718,7 +728,8 @@ public function getNotificationSettings(string $userId, ?array $options = null): * @throws SeedException * @throws SeedApiException */ - public function updateTags(string $userId, UpdateTagsRequest $request, ?array $options = null): array { + public function updateTags(string $userId, UpdateTagsRequest $request, ?array $options = null): array + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -731,15 +742,15 @@ public function updateTags(string $userId, UpdateTagsRequest $request, ?array $o $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, ['string']); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -773,7 +784,8 @@ public function updateTags(string $userId, UpdateTagsRequest $request, ?array $o * @throws SeedException * @throws SeedApiException */ - public function getSearchResults(SearchRequest $request, ?array $options = null): ?array { + public function getSearchResults(SearchRequest $request, ?array $options = null): ?array + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -786,18 +798,18 @@ public function getSearchResults(SearchRequest $request, ?array $options = null) $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); - if (empty($json)){ + if (empty($json)) { return null; } return JsonDecoder::decodeArray($json, [SearchResult::class]); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/nullable-optional/src/NullableOptional/Requests/FilterByRoleRequest.php b/seed/php-sdk/nullable-optional/src/NullableOptional/Requests/FilterByRoleRequest.php index ebf9466c5aa5..eff7d7b225a4 100644 --- a/seed/php-sdk/nullable-optional/src/NullableOptional/Requests/FilterByRoleRequest.php +++ b/seed/php-sdk/nullable-optional/src/NullableOptional/Requests/FilterByRoleRequest.php @@ -32,8 +32,9 @@ class FilterByRoleRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->role = $values['role'] ?? null;$this->status = $values['status'] ?? null;$this->secondaryRole = $values['secondaryRole'] ?? null; + ) { + $this->role = $values['role'] ?? null; + $this->status = $values['status'] ?? null; + $this->secondaryRole = $values['secondaryRole'] ?? null; } } diff --git a/seed/php-sdk/nullable-optional/src/NullableOptional/Requests/ListUsersRequest.php b/seed/php-sdk/nullable-optional/src/NullableOptional/Requests/ListUsersRequest.php index 5431d54f7272..976b2f76a24f 100644 --- a/seed/php-sdk/nullable-optional/src/NullableOptional/Requests/ListUsersRequest.php +++ b/seed/php-sdk/nullable-optional/src/NullableOptional/Requests/ListUsersRequest.php @@ -36,8 +36,10 @@ class ListUsersRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->limit = $values['limit'] ?? null;$this->offset = $values['offset'] ?? null;$this->includeDeleted = $values['includeDeleted'] ?? null;$this->sortBy = $values['sortBy'] ?? null; + ) { + $this->limit = $values['limit'] ?? null; + $this->offset = $values['offset'] ?? null; + $this->includeDeleted = $values['includeDeleted'] ?? null; + $this->sortBy = $values['sortBy'] ?? null; } } diff --git a/seed/php-sdk/nullable-optional/src/NullableOptional/Requests/SearchRequest.php b/seed/php-sdk/nullable-optional/src/NullableOptional/Requests/SearchRequest.php index dea010e8680e..cb3f7f5c02de 100644 --- a/seed/php-sdk/nullable-optional/src/NullableOptional/Requests/SearchRequest.php +++ b/seed/php-sdk/nullable-optional/src/NullableOptional/Requests/SearchRequest.php @@ -36,8 +36,9 @@ class SearchRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->query = $values['query'];$this->filters = $values['filters'] ?? null;$this->includeTypes = $values['includeTypes'] ?? null; + ) { + $this->query = $values['query']; + $this->filters = $values['filters'] ?? null; + $this->includeTypes = $values['includeTypes'] ?? null; } } diff --git a/seed/php-sdk/nullable-optional/src/NullableOptional/Requests/SearchUsersRequest.php b/seed/php-sdk/nullable-optional/src/NullableOptional/Requests/SearchUsersRequest.php index 3e5dff5c57a0..ee4b8a95ab29 100644 --- a/seed/php-sdk/nullable-optional/src/NullableOptional/Requests/SearchUsersRequest.php +++ b/seed/php-sdk/nullable-optional/src/NullableOptional/Requests/SearchUsersRequest.php @@ -36,8 +36,10 @@ class SearchUsersRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->query = $values['query'];$this->department = $values['department'] ?? null;$this->role = $values['role'] ?? null;$this->isActive = $values['isActive'] ?? null; + ) { + $this->query = $values['query']; + $this->department = $values['department'] ?? null; + $this->role = $values['role'] ?? null; + $this->isActive = $values['isActive'] ?? null; } } diff --git a/seed/php-sdk/nullable-optional/src/NullableOptional/Requests/UpdateComplexProfileRequest.php b/seed/php-sdk/nullable-optional/src/NullableOptional/Requests/UpdateComplexProfileRequest.php index ba6c60492ceb..215de10f58a8 100644 --- a/seed/php-sdk/nullable-optional/src/NullableOptional/Requests/UpdateComplexProfileRequest.php +++ b/seed/php-sdk/nullable-optional/src/NullableOptional/Requests/UpdateComplexProfileRequest.php @@ -53,8 +53,11 @@ class UpdateComplexProfileRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->nullableRole = $values['nullableRole'] ?? null;$this->nullableStatus = $values['nullableStatus'] ?? null;$this->nullableNotification = $values['nullableNotification'] ?? null;$this->nullableSearchResult = $values['nullableSearchResult'] ?? null;$this->nullableArray = $values['nullableArray'] ?? null; + ) { + $this->nullableRole = $values['nullableRole'] ?? null; + $this->nullableStatus = $values['nullableStatus'] ?? null; + $this->nullableNotification = $values['nullableNotification'] ?? null; + $this->nullableSearchResult = $values['nullableSearchResult'] ?? null; + $this->nullableArray = $values['nullableArray'] ?? null; } } diff --git a/seed/php-sdk/nullable-optional/src/NullableOptional/Requests/UpdateTagsRequest.php b/seed/php-sdk/nullable-optional/src/NullableOptional/Requests/UpdateTagsRequest.php index 86f871d9ddd1..fce81bd1d0cb 100644 --- a/seed/php-sdk/nullable-optional/src/NullableOptional/Requests/UpdateTagsRequest.php +++ b/seed/php-sdk/nullable-optional/src/NullableOptional/Requests/UpdateTagsRequest.php @@ -35,8 +35,9 @@ class UpdateTagsRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->tags = $values['tags'] ?? null;$this->categories = $values['categories'] ?? null;$this->labels = $values['labels'] ?? null; + ) { + $this->tags = $values['tags'] ?? null; + $this->categories = $values['categories'] ?? null; + $this->labels = $values['labels'] ?? null; } } diff --git a/seed/php-sdk/nullable-optional/src/NullableOptional/Types/Address.php b/seed/php-sdk/nullable-optional/src/NullableOptional/Types/Address.php index dec4bdd6e779..dbf8bbb6e2fd 100644 --- a/seed/php-sdk/nullable-optional/src/NullableOptional/Types/Address.php +++ b/seed/php-sdk/nullable-optional/src/NullableOptional/Types/Address.php @@ -65,15 +65,21 @@ class Address extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->street = $values['street'];$this->city = $values['city'] ?? null;$this->state = $values['state'] ?? null;$this->zipCode = $values['zipCode'];$this->country = $values['country'] ?? null;$this->buildingId = $values['buildingId'] ?? null;$this->tenantId = $values['tenantId'] ?? null; + ) { + $this->street = $values['street']; + $this->city = $values['city'] ?? null; + $this->state = $values['state'] ?? null; + $this->zipCode = $values['zipCode']; + $this->country = $values['country'] ?? null; + $this->buildingId = $values['buildingId'] ?? null; + $this->tenantId = $values['tenantId'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/nullable-optional/src/NullableOptional/Types/ComplexProfile.php b/seed/php-sdk/nullable-optional/src/NullableOptional/Types/ComplexProfile.php index e9de5d5e7e11..ddf738e715fc 100644 --- a/seed/php-sdk/nullable-optional/src/NullableOptional/Types/ComplexProfile.php +++ b/seed/php-sdk/nullable-optional/src/NullableOptional/Types/ComplexProfile.php @@ -151,15 +151,33 @@ class ComplexProfile extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->nullableRole = $values['nullableRole'] ?? null;$this->optionalRole = $values['optionalRole'] ?? null;$this->optionalNullableRole = $values['optionalNullableRole'] ?? null;$this->nullableStatus = $values['nullableStatus'] ?? null;$this->optionalStatus = $values['optionalStatus'] ?? null;$this->optionalNullableStatus = $values['optionalNullableStatus'] ?? null;$this->nullableNotification = $values['nullableNotification'] ?? null;$this->optionalNotification = $values['optionalNotification'] ?? null;$this->optionalNullableNotification = $values['optionalNullableNotification'] ?? null;$this->nullableSearchResult = $values['nullableSearchResult'] ?? null;$this->optionalSearchResult = $values['optionalSearchResult'] ?? null;$this->nullableArray = $values['nullableArray'] ?? null;$this->optionalArray = $values['optionalArray'] ?? null;$this->optionalNullableArray = $values['optionalNullableArray'] ?? null;$this->nullableListOfNullables = $values['nullableListOfNullables'] ?? null;$this->nullableMapOfNullables = $values['nullableMapOfNullables'] ?? null;$this->nullableListOfUnions = $values['nullableListOfUnions'] ?? null;$this->optionalMapOfEnums = $values['optionalMapOfEnums'] ?? null; + ) { + $this->id = $values['id']; + $this->nullableRole = $values['nullableRole'] ?? null; + $this->optionalRole = $values['optionalRole'] ?? null; + $this->optionalNullableRole = $values['optionalNullableRole'] ?? null; + $this->nullableStatus = $values['nullableStatus'] ?? null; + $this->optionalStatus = $values['optionalStatus'] ?? null; + $this->optionalNullableStatus = $values['optionalNullableStatus'] ?? null; + $this->nullableNotification = $values['nullableNotification'] ?? null; + $this->optionalNotification = $values['optionalNotification'] ?? null; + $this->optionalNullableNotification = $values['optionalNullableNotification'] ?? null; + $this->nullableSearchResult = $values['nullableSearchResult'] ?? null; + $this->optionalSearchResult = $values['optionalSearchResult'] ?? null; + $this->nullableArray = $values['nullableArray'] ?? null; + $this->optionalArray = $values['optionalArray'] ?? null; + $this->optionalNullableArray = $values['optionalNullableArray'] ?? null; + $this->nullableListOfNullables = $values['nullableListOfNullables'] ?? null; + $this->nullableMapOfNullables = $values['nullableMapOfNullables'] ?? null; + $this->nullableListOfUnions = $values['nullableListOfUnions'] ?? null; + $this->optionalMapOfEnums = $values['optionalMapOfEnums'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/nullable-optional/src/NullableOptional/Types/CreateUserRequest.php b/seed/php-sdk/nullable-optional/src/NullableOptional/Types/CreateUserRequest.php index ac2100ef9013..20aa836aabff 100644 --- a/seed/php-sdk/nullable-optional/src/NullableOptional/Types/CreateUserRequest.php +++ b/seed/php-sdk/nullable-optional/src/NullableOptional/Types/CreateUserRequest.php @@ -41,15 +41,18 @@ class CreateUserRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->username = $values['username'];$this->email = $values['email'] ?? null;$this->phone = $values['phone'] ?? null;$this->address = $values['address'] ?? null; + ) { + $this->username = $values['username']; + $this->email = $values['email'] ?? null; + $this->phone = $values['phone'] ?? null; + $this->address = $values['address'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/nullable-optional/src/NullableOptional/Types/DeserializationTestRequest.php b/seed/php-sdk/nullable-optional/src/NullableOptional/Types/DeserializationTestRequest.php index bd5f4b0a31c2..d065dfe03721 100644 --- a/seed/php-sdk/nullable-optional/src/NullableOptional/Types/DeserializationTestRequest.php +++ b/seed/php-sdk/nullable-optional/src/NullableOptional/Types/DeserializationTestRequest.php @@ -101,15 +101,26 @@ class DeserializationTestRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->requiredString = $values['requiredString'];$this->nullableString = $values['nullableString'] ?? null;$this->optionalString = $values['optionalString'] ?? null;$this->optionalNullableString = $values['optionalNullableString'] ?? null;$this->nullableEnum = $values['nullableEnum'] ?? null;$this->optionalEnum = $values['optionalEnum'] ?? null;$this->nullableUnion = $values['nullableUnion'] ?? null;$this->optionalUnion = $values['optionalUnion'] ?? null;$this->nullableList = $values['nullableList'] ?? null;$this->nullableMap = $values['nullableMap'] ?? null;$this->nullableObject = $values['nullableObject'] ?? null;$this->optionalObject = $values['optionalObject'] ?? null; + ) { + $this->requiredString = $values['requiredString']; + $this->nullableString = $values['nullableString'] ?? null; + $this->optionalString = $values['optionalString'] ?? null; + $this->optionalNullableString = $values['optionalNullableString'] ?? null; + $this->nullableEnum = $values['nullableEnum'] ?? null; + $this->optionalEnum = $values['optionalEnum'] ?? null; + $this->nullableUnion = $values['nullableUnion'] ?? null; + $this->optionalUnion = $values['optionalUnion'] ?? null; + $this->nullableList = $values['nullableList'] ?? null; + $this->nullableMap = $values['nullableMap'] ?? null; + $this->nullableObject = $values['nullableObject'] ?? null; + $this->optionalObject = $values['optionalObject'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/nullable-optional/src/NullableOptional/Types/DeserializationTestResponse.php b/seed/php-sdk/nullable-optional/src/NullableOptional/Types/DeserializationTestResponse.php index a06fc99a7070..5dfdd4f180ea 100644 --- a/seed/php-sdk/nullable-optional/src/NullableOptional/Types/DeserializationTestResponse.php +++ b/seed/php-sdk/nullable-optional/src/NullableOptional/Types/DeserializationTestResponse.php @@ -46,15 +46,18 @@ class DeserializationTestResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->echo = $values['echo'];$this->processedAt = $values['processedAt'];$this->nullCount = $values['nullCount'];$this->presentFieldsCount = $values['presentFieldsCount']; + ) { + $this->echo = $values['echo']; + $this->processedAt = $values['processedAt']; + $this->nullCount = $values['nullCount']; + $this->presentFieldsCount = $values['presentFieldsCount']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/nullable-optional/src/NullableOptional/Types/Document.php b/seed/php-sdk/nullable-optional/src/NullableOptional/Types/Document.php index cc12b1ca76d1..e9b6f1bda25e 100644 --- a/seed/php-sdk/nullable-optional/src/NullableOptional/Types/Document.php +++ b/seed/php-sdk/nullable-optional/src/NullableOptional/Types/Document.php @@ -49,15 +49,19 @@ class Document extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->title = $values['title'];$this->content = $values['content'];$this->author = $values['author'] ?? null;$this->tags = $values['tags'] ?? null; + ) { + $this->id = $values['id']; + $this->title = $values['title']; + $this->content = $values['content']; + $this->author = $values['author'] ?? null; + $this->tags = $values['tags'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/nullable-optional/src/NullableOptional/Types/EmailNotification.php b/seed/php-sdk/nullable-optional/src/NullableOptional/Types/EmailNotification.php index c968176e16ae..20ed4e74923a 100644 --- a/seed/php-sdk/nullable-optional/src/NullableOptional/Types/EmailNotification.php +++ b/seed/php-sdk/nullable-optional/src/NullableOptional/Types/EmailNotification.php @@ -34,15 +34,17 @@ class EmailNotification extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->emailAddress = $values['emailAddress'];$this->subject = $values['subject'];$this->htmlContent = $values['htmlContent'] ?? null; + ) { + $this->emailAddress = $values['emailAddress']; + $this->subject = $values['subject']; + $this->htmlContent = $values['htmlContent'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/nullable-optional/src/NullableOptional/Types/NotificationMethod.php b/seed/php-sdk/nullable-optional/src/NullableOptional/Types/NotificationMethod.php index d8589b89f723..46b1c7819021 100644 --- a/seed/php-sdk/nullable-optional/src/NullableOptional/Types/NotificationMethod.php +++ b/seed/php-sdk/nullable-optional/src/NullableOptional/Types/NotificationMethod.php @@ -49,16 +49,17 @@ class NotificationMethod extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param EmailNotification $email * @return NotificationMethod */ - public static function email(EmailNotification $email): NotificationMethod { + public static function email(EmailNotification $email): NotificationMethod + { return new NotificationMethod([ 'type' => 'email', 'value' => $email, @@ -69,7 +70,8 @@ public static function email(EmailNotification $email): NotificationMethod { * @param SmsNotification $sms * @return NotificationMethod */ - public static function sms(SmsNotification $sms): NotificationMethod { + public static function sms(SmsNotification $sms): NotificationMethod + { return new NotificationMethod([ 'type' => 'sms', 'value' => $sms, @@ -80,7 +82,8 @@ public static function sms(SmsNotification $sms): NotificationMethod { * @param PushNotification $push * @return NotificationMethod */ - public static function push(PushNotification $push): NotificationMethod { + public static function push(PushNotification $push): NotificationMethod + { return new NotificationMethod([ 'type' => 'push', 'value' => $push, @@ -90,81 +93,89 @@ public static function push(PushNotification $push): NotificationMethod { /** * @return bool */ - public function isEmail(): bool { - return $this->value instanceof EmailNotification&& $this->type === 'email'; + public function isEmail(): bool + { + return $this->value instanceof EmailNotification && $this->type === 'email'; } /** * @return EmailNotification */ - public function asEmail(): EmailNotification { - if (!($this->value instanceof EmailNotification&& $this->type === 'email')){ + public function asEmail(): EmailNotification + { + if (!($this->value instanceof EmailNotification && $this->type === 'email')) { throw new Exception( "Expected email; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isSms(): bool { - return $this->value instanceof SmsNotification&& $this->type === 'sms'; + public function isSms(): bool + { + return $this->value instanceof SmsNotification && $this->type === 'sms'; } /** * @return SmsNotification */ - public function asSms(): SmsNotification { - if (!($this->value instanceof SmsNotification&& $this->type === 'sms')){ + public function asSms(): SmsNotification + { + if (!($this->value instanceof SmsNotification && $this->type === 'sms')) { throw new Exception( "Expected sms; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isPush(): bool { - return $this->value instanceof PushNotification&& $this->type === 'push'; + public function isPush(): bool + { + return $this->value instanceof PushNotification && $this->type === 'push'; } /** * @return PushNotification */ - public function asPush(): PushNotification { - if (!($this->value instanceof PushNotification&& $this->type === 'push')){ + public function asPush(): PushNotification + { + if (!($this->value instanceof PushNotification && $this->type === 'push')) { throw new Exception( "Expected push; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'email': $value = $this->asEmail()->jsonSerialize(); $result = array_merge($value, $result); @@ -179,26 +190,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -207,22 +219,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'email': $args['value'] = EmailNotification::jsonDeserialize($data); break; @@ -237,7 +250,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/nullable-optional/src/NullableOptional/Types/Organization.php b/seed/php-sdk/nullable-optional/src/NullableOptional/Types/Organization.php index 7caf1bde8204..60fadcf0f5c3 100644 --- a/seed/php-sdk/nullable-optional/src/NullableOptional/Types/Organization.php +++ b/seed/php-sdk/nullable-optional/src/NullableOptional/Types/Organization.php @@ -41,15 +41,18 @@ class Organization extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->name = $values['name'];$this->domain = $values['domain'] ?? null;$this->employeeCount = $values['employeeCount'] ?? null; + ) { + $this->id = $values['id']; + $this->name = $values['name']; + $this->domain = $values['domain'] ?? null; + $this->employeeCount = $values['employeeCount'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/nullable-optional/src/NullableOptional/Types/PushNotification.php b/seed/php-sdk/nullable-optional/src/NullableOptional/Types/PushNotification.php index 7d14e50880ff..ea966caba330 100644 --- a/seed/php-sdk/nullable-optional/src/NullableOptional/Types/PushNotification.php +++ b/seed/php-sdk/nullable-optional/src/NullableOptional/Types/PushNotification.php @@ -41,15 +41,18 @@ class PushNotification extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->deviceToken = $values['deviceToken'];$this->title = $values['title'];$this->body = $values['body'];$this->badge = $values['badge'] ?? null; + ) { + $this->deviceToken = $values['deviceToken']; + $this->title = $values['title']; + $this->body = $values['body']; + $this->badge = $values['badge'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/nullable-optional/src/NullableOptional/Types/SearchResult.php b/seed/php-sdk/nullable-optional/src/NullableOptional/Types/SearchResult.php index 03c68ba5a720..65b289d75d43 100644 --- a/seed/php-sdk/nullable-optional/src/NullableOptional/Types/SearchResult.php +++ b/seed/php-sdk/nullable-optional/src/NullableOptional/Types/SearchResult.php @@ -49,16 +49,17 @@ class SearchResult extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param UserResponse $user * @return SearchResult */ - public static function user(UserResponse $user): SearchResult { + public static function user(UserResponse $user): SearchResult + { return new SearchResult([ 'type' => 'user', 'value' => $user, @@ -69,7 +70,8 @@ public static function user(UserResponse $user): SearchResult { * @param Organization $organization * @return SearchResult */ - public static function organization(Organization $organization): SearchResult { + public static function organization(Organization $organization): SearchResult + { return new SearchResult([ 'type' => 'organization', 'value' => $organization, @@ -80,7 +82,8 @@ public static function organization(Organization $organization): SearchResult { * @param Document $document * @return SearchResult */ - public static function document(Document $document): SearchResult { + public static function document(Document $document): SearchResult + { return new SearchResult([ 'type' => 'document', 'value' => $document, @@ -90,81 +93,89 @@ public static function document(Document $document): SearchResult { /** * @return bool */ - public function isUser(): bool { - return $this->value instanceof UserResponse&& $this->type === 'user'; + public function isUser(): bool + { + return $this->value instanceof UserResponse && $this->type === 'user'; } /** * @return UserResponse */ - public function asUser(): UserResponse { - if (!($this->value instanceof UserResponse&& $this->type === 'user')){ + public function asUser(): UserResponse + { + if (!($this->value instanceof UserResponse && $this->type === 'user')) { throw new Exception( "Expected user; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isOrganization(): bool { - return $this->value instanceof Organization&& $this->type === 'organization'; + public function isOrganization(): bool + { + return $this->value instanceof Organization && $this->type === 'organization'; } /** * @return Organization */ - public function asOrganization(): Organization { - if (!($this->value instanceof Organization&& $this->type === 'organization')){ + public function asOrganization(): Organization + { + if (!($this->value instanceof Organization && $this->type === 'organization')) { throw new Exception( "Expected organization; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isDocument(): bool { - return $this->value instanceof Document&& $this->type === 'document'; + public function isDocument(): bool + { + return $this->value instanceof Document && $this->type === 'document'; } /** * @return Document */ - public function asDocument(): Document { - if (!($this->value instanceof Document&& $this->type === 'document')){ + public function asDocument(): Document + { + if (!($this->value instanceof Document && $this->type === 'document')) { throw new Exception( "Expected document; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'user': $value = $this->asUser()->jsonSerialize(); $result = array_merge($value, $result); @@ -179,26 +190,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -207,22 +219,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'user': $args['value'] = UserResponse::jsonDeserialize($data); break; @@ -237,7 +250,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/nullable-optional/src/NullableOptional/Types/SmsNotification.php b/seed/php-sdk/nullable-optional/src/NullableOptional/Types/SmsNotification.php index 430b3dfe2c3b..67d9afda1cbe 100644 --- a/seed/php-sdk/nullable-optional/src/NullableOptional/Types/SmsNotification.php +++ b/seed/php-sdk/nullable-optional/src/NullableOptional/Types/SmsNotification.php @@ -34,15 +34,17 @@ class SmsNotification extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->phoneNumber = $values['phoneNumber'];$this->message = $values['message'];$this->shortCode = $values['shortCode'] ?? null; + ) { + $this->phoneNumber = $values['phoneNumber']; + $this->message = $values['message']; + $this->shortCode = $values['shortCode'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/nullable-optional/src/NullableOptional/Types/UpdateUserRequest.php b/seed/php-sdk/nullable-optional/src/NullableOptional/Types/UpdateUserRequest.php index f7fe4207ff49..79b9f09507bb 100644 --- a/seed/php-sdk/nullable-optional/src/NullableOptional/Types/UpdateUserRequest.php +++ b/seed/php-sdk/nullable-optional/src/NullableOptional/Types/UpdateUserRequest.php @@ -44,15 +44,18 @@ class UpdateUserRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->username = $values['username'] ?? null;$this->email = $values['email'] ?? null;$this->phone = $values['phone'] ?? null;$this->address = $values['address'] ?? null; + ) { + $this->username = $values['username'] ?? null; + $this->email = $values['email'] ?? null; + $this->phone = $values['phone'] ?? null; + $this->address = $values['address'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/nullable-optional/src/NullableOptional/Types/UserProfile.php b/seed/php-sdk/nullable-optional/src/NullableOptional/Types/UserProfile.php index 9765b7af2956..a793d5a9981c 100644 --- a/seed/php-sdk/nullable-optional/src/NullableOptional/Types/UserProfile.php +++ b/seed/php-sdk/nullable-optional/src/NullableOptional/Types/UserProfile.php @@ -145,15 +145,32 @@ class UserProfile extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->username = $values['username'];$this->nullableString = $values['nullableString'] ?? null;$this->nullableInteger = $values['nullableInteger'] ?? null;$this->nullableBoolean = $values['nullableBoolean'] ?? null;$this->nullableDate = $values['nullableDate'] ?? null;$this->nullableObject = $values['nullableObject'] ?? null;$this->nullableList = $values['nullableList'] ?? null;$this->nullableMap = $values['nullableMap'] ?? null;$this->optionalString = $values['optionalString'] ?? null;$this->optionalInteger = $values['optionalInteger'] ?? null;$this->optionalBoolean = $values['optionalBoolean'] ?? null;$this->optionalDate = $values['optionalDate'] ?? null;$this->optionalObject = $values['optionalObject'] ?? null;$this->optionalList = $values['optionalList'] ?? null;$this->optionalMap = $values['optionalMap'] ?? null;$this->optionalNullableString = $values['optionalNullableString'] ?? null;$this->optionalNullableObject = $values['optionalNullableObject'] ?? null; + ) { + $this->id = $values['id']; + $this->username = $values['username']; + $this->nullableString = $values['nullableString'] ?? null; + $this->nullableInteger = $values['nullableInteger'] ?? null; + $this->nullableBoolean = $values['nullableBoolean'] ?? null; + $this->nullableDate = $values['nullableDate'] ?? null; + $this->nullableObject = $values['nullableObject'] ?? null; + $this->nullableList = $values['nullableList'] ?? null; + $this->nullableMap = $values['nullableMap'] ?? null; + $this->optionalString = $values['optionalString'] ?? null; + $this->optionalInteger = $values['optionalInteger'] ?? null; + $this->optionalBoolean = $values['optionalBoolean'] ?? null; + $this->optionalDate = $values['optionalDate'] ?? null; + $this->optionalObject = $values['optionalObject'] ?? null; + $this->optionalList = $values['optionalList'] ?? null; + $this->optionalMap = $values['optionalMap'] ?? null; + $this->optionalNullableString = $values['optionalNullableString'] ?? null; + $this->optionalNullableObject = $values['optionalNullableObject'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/nullable-optional/src/NullableOptional/Types/UserResponse.php b/seed/php-sdk/nullable-optional/src/NullableOptional/Types/UserResponse.php index 13b80dbd3637..839e3d062d31 100644 --- a/seed/php-sdk/nullable-optional/src/NullableOptional/Types/UserResponse.php +++ b/seed/php-sdk/nullable-optional/src/NullableOptional/Types/UserResponse.php @@ -64,15 +64,21 @@ class UserResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->username = $values['username'];$this->email = $values['email'] ?? null;$this->phone = $values['phone'] ?? null;$this->createdAt = $values['createdAt'];$this->updatedAt = $values['updatedAt'] ?? null;$this->address = $values['address'] ?? null; + ) { + $this->id = $values['id']; + $this->username = $values['username']; + $this->email = $values['email'] ?? null; + $this->phone = $values['phone'] ?? null; + $this->createdAt = $values['createdAt']; + $this->updatedAt = $values['updatedAt'] ?? null; + $this->address = $values['address'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/nullable-optional/src/NullableOptional/Types/UserRole.php b/seed/php-sdk/nullable-optional/src/NullableOptional/Types/UserRole.php index 0a3ba86a4472..abe9bfe1dc46 100644 --- a/seed/php-sdk/nullable-optional/src/NullableOptional/Types/UserRole.php +++ b/seed/php-sdk/nullable-optional/src/NullableOptional/Types/UserRole.php @@ -2,8 +2,8 @@ namespace Seed\NullableOptional\Types; -enum UserRole - : string { +enum UserRole: string +{ case Admin = "ADMIN"; case User = "USER"; case Guest = "GUEST"; diff --git a/seed/php-sdk/nullable-optional/src/NullableOptional/Types/UserStatus.php b/seed/php-sdk/nullable-optional/src/NullableOptional/Types/UserStatus.php index ece9224067b5..be10c5fe7227 100644 --- a/seed/php-sdk/nullable-optional/src/NullableOptional/Types/UserStatus.php +++ b/seed/php-sdk/nullable-optional/src/NullableOptional/Types/UserStatus.php @@ -2,8 +2,8 @@ namespace Seed\NullableOptional\Types; -enum UserStatus - : string { +enum UserStatus: string +{ case Active = "active"; case Inactive = "inactive"; case Suspended = "suspended"; diff --git a/seed/php-sdk/nullable-optional/src/SeedClient.php b/seed/php-sdk/nullable-optional/src/SeedClient.php index 5fa952437e51..26f3f2d50195 100644 --- a/seed/php-sdk/nullable-optional/src/SeedClient.php +++ b/seed/php-sdk/nullable-optional/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var NullableOptionalClient $nullableOptional @@ -40,26 +40,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->nullableOptional = new NullableOptionalClient($this->client, $this->options); } } diff --git a/seed/php-sdk/nullable-optional/src/Utils/File.php b/seed/php-sdk/nullable-optional/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/nullable-optional/src/Utils/File.php +++ b/seed/php-sdk/nullable-optional/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/nullable-optional/tests/Core/Client/RawClientTest.php b/seed/php-sdk/nullable-optional/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/nullable-optional/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/nullable-optional/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/nullable-optional/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/nullable-optional/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/nullable-optional/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/nullable-optional/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/nullable-optional/tests/Core/Json/EnumTest.php b/seed/php-sdk/nullable-optional/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/nullable-optional/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/nullable-optional/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/nullable-optional/tests/Core/Json/TraitTest.php b/seed/php-sdk/nullable-optional/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/nullable-optional/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/nullable-optional/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/nullable-optional/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/nullable-optional/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/nullable-optional/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/nullable-optional/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/nullable-request-body/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/nullable-request-body/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/nullable-request-body/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/nullable-request-body/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/nullable-request-body/src/Core/Client/RawClient.php b/seed/php-sdk/nullable-request-body/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/nullable-request-body/src/Core/Client/RawClient.php +++ b/seed/php-sdk/nullable-request-body/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/nullable-request-body/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/nullable-request-body/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/nullable-request-body/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/nullable-request-body/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/nullable-request-body/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/nullable-request-body/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/nullable-request-body/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/nullable-request-body/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/nullable-request-body/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/nullable-request-body/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/nullable-request-body/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/nullable-request-body/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/nullable-request-body/src/Core/Types/ArrayType.php b/seed/php-sdk/nullable-request-body/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/nullable-request-body/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/nullable-request-body/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/nullable-request-body/src/Core/Types/Constant.php b/seed/php-sdk/nullable-request-body/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/nullable-request-body/src/Core/Types/Constant.php +++ b/seed/php-sdk/nullable-request-body/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/nullable-request-body/src/Core/Types/Union.php b/seed/php-sdk/nullable-request-body/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/nullable-request-body/src/Core/Types/Union.php +++ b/seed/php-sdk/nullable-request-body/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/nullable-request-body/src/Exceptions/SeedApiException.php b/seed/php-sdk/nullable-request-body/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/nullable-request-body/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/nullable-request-body/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/nullable-request-body/src/Exceptions/SeedException.php b/seed/php-sdk/nullable-request-body/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/nullable-request-body/src/Exceptions/SeedException.php +++ b/seed/php-sdk/nullable-request-body/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/nullable-request-body/src/SeedClient.php b/seed/php-sdk/nullable-request-body/src/SeedClient.php index 43ce51f01ea8..43f287310694 100644 --- a/seed/php-sdk/nullable-request-body/src/SeedClient.php +++ b/seed/php-sdk/nullable-request-body/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var TestGroupClient $testGroup @@ -40,26 +40,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->testGroup = new TestGroupClient($this->client, $this->options); } } diff --git a/seed/php-sdk/nullable-request-body/src/TestGroup/Requests/TestMethodNameTestGroupRequest.php b/seed/php-sdk/nullable-request-body/src/TestGroup/Requests/TestMethodNameTestGroupRequest.php index 75b090349332..9199a86c54ce 100644 --- a/seed/php-sdk/nullable-request-body/src/TestGroup/Requests/TestMethodNameTestGroupRequest.php +++ b/seed/php-sdk/nullable-request-body/src/TestGroup/Requests/TestMethodNameTestGroupRequest.php @@ -31,8 +31,9 @@ class TestMethodNameTestGroupRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->queryParamObject = $values['queryParamObject'] ?? null;$this->queryParamInteger = $values['queryParamInteger'] ?? null;$this->body = $values['body'] ?? null; + ) { + $this->queryParamObject = $values['queryParamObject'] ?? null; + $this->queryParamInteger = $values['queryParamInteger'] ?? null; + $this->body = $values['body'] ?? null; } } diff --git a/seed/php-sdk/nullable-request-body/src/TestGroup/TestGroupClient.php b/seed/php-sdk/nullable-request-body/src/TestGroup/TestGroupClient.php index 46b592ae9b99..b26890de3aba 100644 --- a/seed/php-sdk/nullable-request-body/src/TestGroup/TestGroupClient.php +++ b/seed/php-sdk/nullable-request-body/src/TestGroup/TestGroupClient.php @@ -14,7 +14,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class TestGroupClient +class TestGroupClient { /** * @var array{ @@ -42,11 +42,10 @@ class TestGroupClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -68,13 +67,14 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function testMethodName(string $pathParam, TestMethodNameTestGroupRequest $request, ?array $options = null): mixed { + public function testMethodName(string $pathParam, TestMethodNameTestGroupRequest $request, ?array $options = null): mixed + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->queryParamObject != null){ + if ($request->queryParamObject != null) { $query['query_param_object'] = $request->queryParamObject; } - if ($request->queryParamInteger != null){ + if ($request->queryParamInteger != null) { $query['query_param_integer'] = $request->queryParamInteger; } try { @@ -89,15 +89,15 @@ public function testMethodName(string $pathParam, TestMethodNameTestGroupRequest $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeMixed($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/nullable-request-body/src/Types/PlainObject.php b/seed/php-sdk/nullable-request-body/src/Types/PlainObject.php index 607c7be3affa..0506401ebc16 100644 --- a/seed/php-sdk/nullable-request-body/src/Types/PlainObject.php +++ b/seed/php-sdk/nullable-request-body/src/Types/PlainObject.php @@ -27,15 +27,16 @@ class PlainObject extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->id = $values['id'] ?? null;$this->name = $values['name'] ?? null; + ) { + $this->id = $values['id'] ?? null; + $this->name = $values['name'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/nullable-request-body/src/Utils/File.php b/seed/php-sdk/nullable-request-body/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/nullable-request-body/src/Utils/File.php +++ b/seed/php-sdk/nullable-request-body/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/nullable-request-body/tests/Core/Client/RawClientTest.php b/seed/php-sdk/nullable-request-body/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/nullable-request-body/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/nullable-request-body/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/nullable-request-body/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/nullable-request-body/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/nullable-request-body/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/nullable-request-body/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/nullable-request-body/tests/Core/Json/EnumTest.php b/seed/php-sdk/nullable-request-body/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/nullable-request-body/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/nullable-request-body/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/nullable-request-body/tests/Core/Json/TraitTest.php b/seed/php-sdk/nullable-request-body/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/nullable-request-body/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/nullable-request-body/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/nullable-request-body/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/nullable-request-body/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/nullable-request-body/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/nullable-request-body/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/nullable/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/nullable/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/nullable/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/nullable/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/nullable/src/Core/Client/RawClient.php b/seed/php-sdk/nullable/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/nullable/src/Core/Client/RawClient.php +++ b/seed/php-sdk/nullable/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/nullable/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/nullable/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/nullable/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/nullable/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/nullable/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/nullable/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/nullable/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/nullable/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/nullable/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/nullable/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/nullable/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/nullable/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/nullable/src/Core/Types/ArrayType.php b/seed/php-sdk/nullable/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/nullable/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/nullable/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/nullable/src/Core/Types/Constant.php b/seed/php-sdk/nullable/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/nullable/src/Core/Types/Constant.php +++ b/seed/php-sdk/nullable/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/nullable/src/Core/Types/Union.php b/seed/php-sdk/nullable/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/nullable/src/Core/Types/Union.php +++ b/seed/php-sdk/nullable/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/nullable/src/Exceptions/SeedApiException.php b/seed/php-sdk/nullable/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/nullable/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/nullable/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/nullable/src/Exceptions/SeedException.php b/seed/php-sdk/nullable/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/nullable/src/Exceptions/SeedException.php +++ b/seed/php-sdk/nullable/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/nullable/src/Nullable/NullableClient.php b/seed/php-sdk/nullable/src/Nullable/NullableClient.php index fedf0bde3451..1e1344f062e2 100644 --- a/seed/php-sdk/nullable/src/Nullable/NullableClient.php +++ b/seed/php-sdk/nullable/src/Nullable/NullableClient.php @@ -17,7 +17,7 @@ use Seed\Nullable\Requests\CreateUserRequest; use Seed\Nullable\Requests\DeleteUserRequest; -class NullableClient +class NullableClient { /** * @var array{ @@ -45,11 +45,10 @@ class NullableClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -68,22 +67,23 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getUsers(GetUsersRequest $request = new GetUsersRequest(), ?array $options = null): array { + public function getUsers(GetUsersRequest $request = new GetUsersRequest(), ?array $options = null): array + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->usernames != null){ + if ($request->usernames != null) { $query['usernames'] = $request->usernames; } - if ($request->avatar != null){ + if ($request->avatar != null) { $query['avatar'] = $request->avatar; } - if ($request->activated != null){ + if ($request->activated != null) { $query['activated'] = $request->activated; } - if ($request->tags != null){ + if ($request->tags != null) { $query['tags'] = $request->tags; } - if ($request->extra != null){ + if ($request->extra != null) { $query['extra'] = $request->extra; } try { @@ -97,15 +97,15 @@ public function getUsers(GetUsersRequest $request = new GetUsersRequest(), ?arra $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, [User::class]); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -137,7 +137,8 @@ public function getUsers(GetUsersRequest $request = new GetUsersRequest(), ?arra * @throws SeedException * @throws SeedApiException */ - public function createUser(CreateUserRequest $request, ?array $options = null): User { + public function createUser(CreateUserRequest $request, ?array $options = null): User + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -150,15 +151,15 @@ public function createUser(CreateUserRequest $request, ?array $options = null): $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return User::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -190,7 +191,8 @@ public function createUser(CreateUserRequest $request, ?array $options = null): * @throws SeedException * @throws SeedApiException */ - public function deleteUser(DeleteUserRequest $request = new DeleteUserRequest(), ?array $options = null): bool { + public function deleteUser(DeleteUserRequest $request = new DeleteUserRequest(), ?array $options = null): bool + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -203,15 +205,15 @@ public function deleteUser(DeleteUserRequest $request = new DeleteUserRequest(), $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeBool($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/nullable/src/Nullable/Requests/CreateUserRequest.php b/seed/php-sdk/nullable/src/Nullable/Requests/CreateUserRequest.php index 9a6b49d52463..99faee578670 100644 --- a/seed/php-sdk/nullable/src/Nullable/Requests/CreateUserRequest.php +++ b/seed/php-sdk/nullable/src/Nullable/Requests/CreateUserRequest.php @@ -43,8 +43,10 @@ class CreateUserRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->username = $values['username'];$this->tags = $values['tags'] ?? null;$this->metadata = $values['metadata'] ?? null;$this->avatar = $values['avatar'] ?? null; + ) { + $this->username = $values['username']; + $this->tags = $values['tags'] ?? null; + $this->metadata = $values['metadata'] ?? null; + $this->avatar = $values['avatar'] ?? null; } } diff --git a/seed/php-sdk/nullable/src/Nullable/Requests/DeleteUserRequest.php b/seed/php-sdk/nullable/src/Nullable/Requests/DeleteUserRequest.php index bcc93aadbc8b..9baa55ecd105 100644 --- a/seed/php-sdk/nullable/src/Nullable/Requests/DeleteUserRequest.php +++ b/seed/php-sdk/nullable/src/Nullable/Requests/DeleteUserRequest.php @@ -20,8 +20,7 @@ class DeleteUserRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->username = $values['username'] ?? null; } } diff --git a/seed/php-sdk/nullable/src/Nullable/Requests/GetUsersRequest.php b/seed/php-sdk/nullable/src/Nullable/Requests/GetUsersRequest.php index 5c6fc87a07ee..e2a321940060 100644 --- a/seed/php-sdk/nullable/src/Nullable/Requests/GetUsersRequest.php +++ b/seed/php-sdk/nullable/src/Nullable/Requests/GetUsersRequest.php @@ -42,8 +42,11 @@ class GetUsersRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->usernames = $values['usernames'] ?? null;$this->avatar = $values['avatar'] ?? null;$this->activated = $values['activated'] ?? null;$this->tags = $values['tags'] ?? null;$this->extra = $values['extra'] ?? null; + ) { + $this->usernames = $values['usernames'] ?? null; + $this->avatar = $values['avatar'] ?? null; + $this->activated = $values['activated'] ?? null; + $this->tags = $values['tags'] ?? null; + $this->extra = $values['extra'] ?? null; } } diff --git a/seed/php-sdk/nullable/src/Nullable/Types/Metadata.php b/seed/php-sdk/nullable/src/Nullable/Types/Metadata.php index f49cbc407096..db1853d290de 100644 --- a/seed/php-sdk/nullable/src/Nullable/Types/Metadata.php +++ b/seed/php-sdk/nullable/src/Nullable/Types/Metadata.php @@ -59,15 +59,20 @@ class Metadata extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->createdAt = $values['createdAt'];$this->updatedAt = $values['updatedAt'];$this->avatar = $values['avatar'] ?? null;$this->activated = $values['activated'] ?? null;$this->status = $values['status'];$this->values = $values['values'] ?? null; + ) { + $this->createdAt = $values['createdAt']; + $this->updatedAt = $values['updatedAt']; + $this->avatar = $values['avatar'] ?? null; + $this->activated = $values['activated'] ?? null; + $this->status = $values['status']; + $this->values = $values['values'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/nullable/src/Nullable/Types/Status.php b/seed/php-sdk/nullable/src/Nullable/Types/Status.php index 60f79bcc7851..605a48e07733 100644 --- a/seed/php-sdk/nullable/src/Nullable/Types/Status.php +++ b/seed/php-sdk/nullable/src/Nullable/Types/Status.php @@ -48,15 +48,16 @@ class Status extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @return Status */ - public static function active(): Status { + public static function active(): Status + { return new Status([ 'type' => 'active', 'value' => null, @@ -67,7 +68,8 @@ public static function active(): Status { * @param ?DateTime $archived * @return Status */ - public static function archived(?DateTime $archived = null): Status { + public static function archived(?DateTime $archived = null): Status + { return new Status([ 'type' => 'archived', 'value' => $archived, @@ -78,7 +80,8 @@ public static function archived(?DateTime $archived = null): Status { * @param ?DateTime $softDeleted * @return Status */ - public static function softDeleted(?DateTime $softDeleted = null): Status { + public static function softDeleted(?DateTime $softDeleted = null): Status + { return new Status([ 'type' => 'soft-deleted', 'value' => $softDeleted, @@ -88,107 +91,115 @@ public static function softDeleted(?DateTime $softDeleted = null): Status { /** * @return bool */ - public function isActive(): bool { - return is_null($this->value)&& $this->type === 'active'; + public function isActive(): bool + { + return is_null($this->value) && $this->type === 'active'; } /** * @return bool */ - public function isArchived(): bool { - return (is_null($this->value) || $this->value instanceof DateTime)&& $this->type === 'archived'; + public function isArchived(): bool + { + return (is_null($this->value) || $this->value instanceof DateTime) && $this->type === 'archived'; } /** * @return ?DateTime */ - public function asArchived(): ?DateTime { - if (!((is_null($this->value) || $this->value instanceof DateTime)&& $this->type === 'archived')){ + public function asArchived(): ?DateTime + { + if (!((is_null($this->value) || $this->value instanceof DateTime) && $this->type === 'archived')) { throw new Exception( "Expected archived; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isSoftDeleted(): bool { - return (is_null($this->value) || $this->value instanceof DateTime)&& $this->type === 'soft-deleted'; + public function isSoftDeleted(): bool + { + return (is_null($this->value) || $this->value instanceof DateTime) && $this->type === 'soft-deleted'; } /** * @return ?DateTime */ - public function asSoftDeleted(): ?DateTime { - if (!((is_null($this->value) || $this->value instanceof DateTime)&& $this->type === 'soft-deleted')){ + public function asSoftDeleted(): ?DateTime + { + if (!((is_null($this->value) || $this->value instanceof DateTime) && $this->type === 'soft-deleted')) { throw new Exception( "Expected soft-deleted; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'active': $result['active'] = []; break; case 'archived': $value = $this->asArchived(); - if (!is_null($value)){ + if (!is_null($value)) { $value = JsonSerializer::serializeDateTime($value); } $result['archived'] = $value; break; case 'soft-deleted': $value = $this->asSoftDeleted(); - if (!is_null($value)){ + if (!is_null($value)) { $value = JsonSerializer::serializeDateTime($value); } $result['soft-deleted'] = $value; break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -197,41 +208,42 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'active': $args['value'] = null; break; case 'archived': - if (!array_key_exists('archived', $data)){ + if (!array_key_exists('archived', $data)) { throw new Exception( "JSON data is missing property 'archived'", ); } - + $args['value'] = $data['archived']; break; case 'soft-deleted': - if (!array_key_exists('soft-deleted', $data)){ + if (!array_key_exists('soft-deleted', $data)) { throw new Exception( "JSON data is missing property 'soft-deleted'", ); } - + $args['value'] = $data['soft-deleted']; break; case '_unknown': @@ -239,7 +251,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/nullable/src/Nullable/Types/User.php b/seed/php-sdk/nullable/src/Nullable/Types/User.php index 9ba5aa35dcaf..4c443b242d3b 100644 --- a/seed/php-sdk/nullable/src/Nullable/Types/User.php +++ b/seed/php-sdk/nullable/src/Nullable/Types/User.php @@ -48,7 +48,7 @@ class User extends JsonSerializableType * |null * ) $favoriteNumber */ - #[JsonProperty('favorite-number'), Union('integer',new Union('float', 'null'),new Union('string', 'null'),'float')] + #[JsonProperty('favorite-number'), Union('integer', new Union('float', 'null'), new Union('string', 'null'), 'float')] public int|float|string|float|null $favoriteNumber; /** @@ -83,15 +83,22 @@ class User extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->id = $values['id'];$this->tags = $values['tags'] ?? null;$this->metadata = $values['metadata'] ?? null;$this->email = $values['email'] ?? null;$this->favoriteNumber = $values['favoriteNumber'];$this->numbers = $values['numbers'] ?? null;$this->strings = $values['strings'] ?? null; + ) { + $this->name = $values['name']; + $this->id = $values['id']; + $this->tags = $values['tags'] ?? null; + $this->metadata = $values['metadata'] ?? null; + $this->email = $values['email'] ?? null; + $this->favoriteNumber = $values['favoriteNumber']; + $this->numbers = $values['numbers'] ?? null; + $this->strings = $values['strings'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/nullable/src/SeedClient.php b/seed/php-sdk/nullable/src/SeedClient.php index 1542e7644f1a..131e2b5ec132 100644 --- a/seed/php-sdk/nullable/src/SeedClient.php +++ b/seed/php-sdk/nullable/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var NullableClient $nullable @@ -40,26 +40,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->nullable = new NullableClient($this->client, $this->options); } } diff --git a/seed/php-sdk/nullable/src/Utils/File.php b/seed/php-sdk/nullable/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/nullable/src/Utils/File.php +++ b/seed/php-sdk/nullable/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/nullable/tests/Core/Client/RawClientTest.php b/seed/php-sdk/nullable/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/nullable/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/nullable/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/nullable/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/nullable/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/nullable/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/nullable/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/nullable/tests/Core/Json/EnumTest.php b/seed/php-sdk/nullable/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/nullable/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/nullable/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/nullable/tests/Core/Json/TraitTest.php b/seed/php-sdk/nullable/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/nullable/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/nullable/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/nullable/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/nullable/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/nullable/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/nullable/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-custom/src/Auth/AuthClient.php b/seed/php-sdk/oauth-client-credentials-custom/src/Auth/AuthClient.php index 10031485bd29..54af167b9931 100644 --- a/seed/php-sdk/oauth-client-credentials-custom/src/Auth/AuthClient.php +++ b/seed/php-sdk/oauth-client-credentials-custom/src/Auth/AuthClient.php @@ -15,7 +15,7 @@ use Psr\Http\Client\ClientExceptionInterface; use Seed\Auth\Requests\RefreshTokenRequest; -class AuthClient +class AuthClient { /** * @var array{ @@ -43,11 +43,10 @@ class AuthClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -66,7 +65,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getTokenWithClientCredentials(GetTokenRequest $request, ?array $options = null): TokenResponse { + public function getTokenWithClientCredentials(GetTokenRequest $request, ?array $options = null): TokenResponse + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -79,15 +79,15 @@ public function getTokenWithClientCredentials(GetTokenRequest $request, ?array $ $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return TokenResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -119,7 +119,8 @@ public function getTokenWithClientCredentials(GetTokenRequest $request, ?array $ * @throws SeedException * @throws SeedApiException */ - public function refreshToken(RefreshTokenRequest $request, ?array $options = null): TokenResponse { + public function refreshToken(RefreshTokenRequest $request, ?array $options = null): TokenResponse + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -132,15 +133,15 @@ public function refreshToken(RefreshTokenRequest $request, ?array $options = nul $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return TokenResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/oauth-client-credentials-custom/src/Auth/Requests/GetTokenRequest.php b/seed/php-sdk/oauth-client-credentials-custom/src/Auth/Requests/GetTokenRequest.php index 3501ca324f4d..f38edf13b5b5 100644 --- a/seed/php-sdk/oauth-client-credentials-custom/src/Auth/Requests/GetTokenRequest.php +++ b/seed/php-sdk/oauth-client-credentials-custom/src/Auth/Requests/GetTokenRequest.php @@ -62,8 +62,13 @@ class GetTokenRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->cid = $values['cid'];$this->csr = $values['csr'];$this->scp = $values['scp'];$this->entityId = $values['entityId'];$this->audience = $values['audience'];$this->grantType = $values['grantType'];$this->scope = $values['scope'] ?? null; + ) { + $this->cid = $values['cid']; + $this->csr = $values['csr']; + $this->scp = $values['scp']; + $this->entityId = $values['entityId']; + $this->audience = $values['audience']; + $this->grantType = $values['grantType']; + $this->scope = $values['scope'] ?? null; } } diff --git a/seed/php-sdk/oauth-client-credentials-custom/src/Auth/Requests/RefreshTokenRequest.php b/seed/php-sdk/oauth-client-credentials-custom/src/Auth/Requests/RefreshTokenRequest.php index 356e12500b21..e9c9a6d94ec5 100644 --- a/seed/php-sdk/oauth-client-credentials-custom/src/Auth/Requests/RefreshTokenRequest.php +++ b/seed/php-sdk/oauth-client-credentials-custom/src/Auth/Requests/RefreshTokenRequest.php @@ -55,8 +55,12 @@ class RefreshTokenRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->clientId = $values['clientId'];$this->clientSecret = $values['clientSecret'];$this->refreshToken = $values['refreshToken'];$this->audience = $values['audience'];$this->grantType = $values['grantType'];$this->scope = $values['scope'] ?? null; + ) { + $this->clientId = $values['clientId']; + $this->clientSecret = $values['clientSecret']; + $this->refreshToken = $values['refreshToken']; + $this->audience = $values['audience']; + $this->grantType = $values['grantType']; + $this->scope = $values['scope'] ?? null; } } diff --git a/seed/php-sdk/oauth-client-credentials-custom/src/Auth/Types/TokenResponse.php b/seed/php-sdk/oauth-client-credentials-custom/src/Auth/Types/TokenResponse.php index 2a7c2ec254a1..f5379b9051d4 100644 --- a/seed/php-sdk/oauth-client-credentials-custom/src/Auth/Types/TokenResponse.php +++ b/seed/php-sdk/oauth-client-credentials-custom/src/Auth/Types/TokenResponse.php @@ -37,15 +37,17 @@ class TokenResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->accessToken = $values['accessToken'];$this->expiresIn = $values['expiresIn'];$this->refreshToken = $values['refreshToken'] ?? null; + ) { + $this->accessToken = $values['accessToken']; + $this->expiresIn = $values['expiresIn']; + $this->refreshToken = $values['refreshToken'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/oauth-client-credentials-custom/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/oauth-client-credentials-custom/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/oauth-client-credentials-custom/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/oauth-client-credentials-custom/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-custom/src/Core/Client/RawClient.php b/seed/php-sdk/oauth-client-credentials-custom/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/oauth-client-credentials-custom/src/Core/Client/RawClient.php +++ b/seed/php-sdk/oauth-client-credentials-custom/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/oauth-client-credentials-custom/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/oauth-client-credentials-custom/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/oauth-client-credentials-custom/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/oauth-client-credentials-custom/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/oauth-client-credentials-custom/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/oauth-client-credentials-custom/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/oauth-client-credentials-custom/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/oauth-client-credentials-custom/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-custom/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/oauth-client-credentials-custom/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/oauth-client-credentials-custom/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/oauth-client-credentials-custom/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-custom/src/Core/OAuthTokenProvider.php b/seed/php-sdk/oauth-client-credentials-custom/src/Core/OAuthTokenProvider.php index 6e342d3f07db..0620cffe4d29 100644 --- a/seed/php-sdk/oauth-client-credentials-custom/src/Core/OAuthTokenProvider.php +++ b/seed/php-sdk/oauth-client-credentials-custom/src/Core/OAuthTokenProvider.php @@ -10,7 +10,7 @@ * The OAuthTokenProvider retrieves an OAuth access token, refreshing it as needed. * The access token is then used as the bearer token in every authenticated request. */ -class OAuthTokenProvider +class OAuthTokenProvider { /** * @var int $BUFFER_IN_MINUTES @@ -47,12 +47,11 @@ class OAuthTokenProvider * @param string $clientSecret The client secret for OAuth authentication. * @param AuthClient $authClient The client used to retrieve the OAuth token. */ - function __construct( + public function __construct( string $clientId, string $clientSecret, AuthClient $authClient, - ) - { + ) { $this->clientId = $clientId; $this->clientSecret = $clientSecret; $this->authClient = $authClient; @@ -65,8 +64,9 @@ function __construct( * * @return string */ - public function getToken(): string { - if ($this->accessToken !== null && ($this->expiresAt === null || $this->expiresAt > new DateTime())){ + public function getToken(): string + { + if ($this->accessToken !== null && ($this->expiresAt === null || $this->expiresAt > new DateTime())) { return $this->accessToken; } return $this->refresh(); @@ -77,7 +77,8 @@ public function getToken(): string { * * @return string */ - private function refresh(): string { + private function refresh(): string + { $request = new GetTokenRequest([ 'cid' => $this->clientId, 'csr' => $this->clientSecret, @@ -100,7 +101,8 @@ private function refresh(): string { * @param int $bufferInMinutes The buffer time in minutes to subtract from the expiration. * @return DateTime */ - private function getExpiresAt(int $expiresInSeconds, int $bufferInMinutes): DateTime { + private function getExpiresAt(int $expiresInSeconds, int $bufferInMinutes): DateTime + { $now = new DateTime(); $expiresInSecondsWithBuffer = $expiresInSeconds - ($bufferInMinutes * 60); $now->modify("+{$expiresInSecondsWithBuffer} seconds"); diff --git a/seed/php-sdk/oauth-client-credentials-custom/src/Core/Types/ArrayType.php b/seed/php-sdk/oauth-client-credentials-custom/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/oauth-client-credentials-custom/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/oauth-client-credentials-custom/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/oauth-client-credentials-custom/src/Core/Types/Constant.php b/seed/php-sdk/oauth-client-credentials-custom/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/oauth-client-credentials-custom/src/Core/Types/Constant.php +++ b/seed/php-sdk/oauth-client-credentials-custom/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-custom/src/Core/Types/Union.php b/seed/php-sdk/oauth-client-credentials-custom/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/oauth-client-credentials-custom/src/Core/Types/Union.php +++ b/seed/php-sdk/oauth-client-credentials-custom/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-custom/src/Exceptions/SeedApiException.php b/seed/php-sdk/oauth-client-credentials-custom/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/oauth-client-credentials-custom/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/oauth-client-credentials-custom/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/oauth-client-credentials-custom/src/Exceptions/SeedException.php b/seed/php-sdk/oauth-client-credentials-custom/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/oauth-client-credentials-custom/src/Exceptions/SeedException.php +++ b/seed/php-sdk/oauth-client-credentials-custom/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/oauth-client-credentials-custom/src/Nested/Api/ApiClient.php b/seed/php-sdk/oauth-client-credentials-custom/src/Nested/Api/ApiClient.php index 55aa05a36ce7..ce6b4aec2e42 100644 --- a/seed/php-sdk/oauth-client-credentials-custom/src/Nested/Api/ApiClient.php +++ b/seed/php-sdk/oauth-client-credentials-custom/src/Nested/Api/ApiClient.php @@ -11,7 +11,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class ApiClient +class ApiClient { /** * @var array{ @@ -39,11 +39,10 @@ class ApiClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -60,7 +59,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getSomething(?array $options = null): void { + public function getSomething(?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -72,12 +72,12 @@ public function getSomething(?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/oauth-client-credentials-custom/src/Nested/NestedClient.php b/seed/php-sdk/oauth-client-credentials-custom/src/Nested/NestedClient.php index 7857c019672d..26f17dd0c9ac 100644 --- a/seed/php-sdk/oauth-client-credentials-custom/src/Nested/NestedClient.php +++ b/seed/php-sdk/oauth-client-credentials-custom/src/Nested/NestedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class NestedClient +class NestedClient { /** * @var ApiClient $api @@ -39,11 +39,10 @@ class NestedClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; $this->api = new ApiClient($this->client, $this->options); diff --git a/seed/php-sdk/oauth-client-credentials-custom/src/NestedNoAuth/Api/ApiClient.php b/seed/php-sdk/oauth-client-credentials-custom/src/NestedNoAuth/Api/ApiClient.php index 6141523e925e..2bd84c067499 100644 --- a/seed/php-sdk/oauth-client-credentials-custom/src/NestedNoAuth/Api/ApiClient.php +++ b/seed/php-sdk/oauth-client-credentials-custom/src/NestedNoAuth/Api/ApiClient.php @@ -11,7 +11,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class ApiClient +class ApiClient { /** * @var array{ @@ -39,11 +39,10 @@ class ApiClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -60,7 +59,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getSomething(?array $options = null): void { + public function getSomething(?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -72,12 +72,12 @@ public function getSomething(?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/oauth-client-credentials-custom/src/NestedNoAuth/NestedNoAuthClient.php b/seed/php-sdk/oauth-client-credentials-custom/src/NestedNoAuth/NestedNoAuthClient.php index c9f5659a49b0..f7c33619f24c 100644 --- a/seed/php-sdk/oauth-client-credentials-custom/src/NestedNoAuth/NestedNoAuthClient.php +++ b/seed/php-sdk/oauth-client-credentials-custom/src/NestedNoAuth/NestedNoAuthClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class NestedNoAuthClient +class NestedNoAuthClient { /** * @var ApiClient $api @@ -39,11 +39,10 @@ class NestedNoAuthClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; $this->api = new ApiClient($this->client, $this->options); diff --git a/seed/php-sdk/oauth-client-credentials-custom/src/SeedClient.php b/seed/php-sdk/oauth-client-credentials-custom/src/SeedClient.php index b5f6e7a8a94c..b91741fbc970 100644 --- a/seed/php-sdk/oauth-client-credentials-custom/src/SeedClient.php +++ b/seed/php-sdk/oauth-client-credentials-custom/src/SeedClient.php @@ -10,7 +10,7 @@ use Seed\Core\Client\RawClient; use Seed\Core\OAuthTokenProvider; -class SeedClient +class SeedClient { /** * @var AuthClient $auth @@ -48,6 +48,11 @@ class SeedClient */ private RawClient $client; + /** + * @var OAuthTokenProvider $oauthTokenProvider + */ + private OAuthTokenProvider $oauthTokenProvider; + /** * @param ?string $clientId The client ID for OAuth authentication. * @param ?string $clientSecret The client secret for OAuth authentication. @@ -63,32 +68,32 @@ public function __construct( ?string $clientId = null, ?string $clientSecret = null, ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $authRawClient = new RawClient(['headers' => []]); $authClient = new AuthClient($authRawClient); - $oauthTokenProvider = new OAuthTokenProvider($clientId ?? '', $clientSecret ?? '', $authClient); - $token = $oauthTokenProvider->getToken(); - - $defaultHeaders['Authorization'] = "Bearer $token"; + $this->oauthTokenProvider = new OAuthTokenProvider($clientId ?? '', $clientSecret ?? '', $authClient); + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + + $this->options['getAuthHeaders'] = fn () => + ['Authorization' => "Bearer " . $this->oauthTokenProvider->getToken()]; + $this->client = new RawClient( options: $this->options, ); - + $this->auth = new AuthClient($this->client, $this->options); $this->nestedNoAuth = new NestedNoAuthClient($this->client, $this->options); $this->nested = new NestedClient($this->client, $this->options); diff --git a/seed/php-sdk/oauth-client-credentials-custom/src/Simple/SimpleClient.php b/seed/php-sdk/oauth-client-credentials-custom/src/Simple/SimpleClient.php index 50b6b54f52c0..7a8df43cf28d 100644 --- a/seed/php-sdk/oauth-client-credentials-custom/src/Simple/SimpleClient.php +++ b/seed/php-sdk/oauth-client-credentials-custom/src/Simple/SimpleClient.php @@ -11,7 +11,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class SimpleClient +class SimpleClient { /** * @var array{ @@ -39,11 +39,10 @@ class SimpleClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -60,7 +59,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getSomething(?array $options = null): void { + public function getSomething(?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -72,12 +72,12 @@ public function getSomething(?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/oauth-client-credentials-custom/src/Utils/File.php b/seed/php-sdk/oauth-client-credentials-custom/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/oauth-client-credentials-custom/src/Utils/File.php +++ b/seed/php-sdk/oauth-client-credentials-custom/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-custom/tests/Core/Client/RawClientTest.php b/seed/php-sdk/oauth-client-credentials-custom/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/oauth-client-credentials-custom/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/oauth-client-credentials-custom/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/oauth-client-credentials-custom/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/oauth-client-credentials-custom/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/oauth-client-credentials-custom/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/oauth-client-credentials-custom/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-custom/tests/Core/Json/EnumTest.php b/seed/php-sdk/oauth-client-credentials-custom/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/oauth-client-credentials-custom/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/oauth-client-credentials-custom/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-custom/tests/Core/Json/TraitTest.php b/seed/php-sdk/oauth-client-credentials-custom/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/oauth-client-credentials-custom/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/oauth-client-credentials-custom/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-custom/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/oauth-client-credentials-custom/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/oauth-client-credentials-custom/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/oauth-client-credentials-custom/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-default/src/Auth/AuthClient.php b/seed/php-sdk/oauth-client-credentials-default/src/Auth/AuthClient.php index 684754285971..d66079e55918 100644 --- a/seed/php-sdk/oauth-client-credentials-default/src/Auth/AuthClient.php +++ b/seed/php-sdk/oauth-client-credentials-default/src/Auth/AuthClient.php @@ -14,7 +14,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class AuthClient +class AuthClient { /** * @var array{ @@ -42,11 +42,10 @@ class AuthClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -65,7 +64,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getToken(GetTokenRequest $request, ?array $options = null): TokenResponse { + public function getToken(GetTokenRequest $request, ?array $options = null): TokenResponse + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -78,15 +78,15 @@ public function getToken(GetTokenRequest $request, ?array $options = null): Toke $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return TokenResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/oauth-client-credentials-default/src/Auth/Requests/GetTokenRequest.php b/seed/php-sdk/oauth-client-credentials-default/src/Auth/Requests/GetTokenRequest.php index 59841bd49b29..6f8d73262bc7 100644 --- a/seed/php-sdk/oauth-client-credentials-default/src/Auth/Requests/GetTokenRequest.php +++ b/seed/php-sdk/oauth-client-credentials-default/src/Auth/Requests/GetTokenRequest.php @@ -34,8 +34,9 @@ class GetTokenRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->clientId = $values['clientId'];$this->clientSecret = $values['clientSecret'];$this->grantType = $values['grantType']; + ) { + $this->clientId = $values['clientId']; + $this->clientSecret = $values['clientSecret']; + $this->grantType = $values['grantType']; } } diff --git a/seed/php-sdk/oauth-client-credentials-default/src/Auth/Types/TokenResponse.php b/seed/php-sdk/oauth-client-credentials-default/src/Auth/Types/TokenResponse.php index f188da9e0f78..c85c2a578df5 100644 --- a/seed/php-sdk/oauth-client-credentials-default/src/Auth/Types/TokenResponse.php +++ b/seed/php-sdk/oauth-client-credentials-default/src/Auth/Types/TokenResponse.php @@ -30,15 +30,16 @@ class TokenResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->accessToken = $values['accessToken'];$this->expiresIn = $values['expiresIn']; + ) { + $this->accessToken = $values['accessToken']; + $this->expiresIn = $values['expiresIn']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/oauth-client-credentials-default/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/oauth-client-credentials-default/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/oauth-client-credentials-default/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/oauth-client-credentials-default/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-default/src/Core/Client/RawClient.php b/seed/php-sdk/oauth-client-credentials-default/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/oauth-client-credentials-default/src/Core/Client/RawClient.php +++ b/seed/php-sdk/oauth-client-credentials-default/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/oauth-client-credentials-default/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/oauth-client-credentials-default/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/oauth-client-credentials-default/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/oauth-client-credentials-default/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/oauth-client-credentials-default/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/oauth-client-credentials-default/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/oauth-client-credentials-default/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/oauth-client-credentials-default/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-default/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/oauth-client-credentials-default/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/oauth-client-credentials-default/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/oauth-client-credentials-default/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-default/src/Core/OAuthTokenProvider.php b/seed/php-sdk/oauth-client-credentials-default/src/Core/OAuthTokenProvider.php index ffcfb3a30d0e..47ac8b77abab 100644 --- a/seed/php-sdk/oauth-client-credentials-default/src/Core/OAuthTokenProvider.php +++ b/seed/php-sdk/oauth-client-credentials-default/src/Core/OAuthTokenProvider.php @@ -10,7 +10,7 @@ * The OAuthTokenProvider retrieves an OAuth access token, refreshing it as needed. * The access token is then used as the bearer token in every authenticated request. */ -class OAuthTokenProvider +class OAuthTokenProvider { /** * @var int $BUFFER_IN_MINUTES @@ -47,12 +47,11 @@ class OAuthTokenProvider * @param string $clientSecret The client secret for OAuth authentication. * @param AuthClient $authClient The client used to retrieve the OAuth token. */ - function __construct( + public function __construct( string $clientId, string $clientSecret, AuthClient $authClient, - ) - { + ) { $this->clientId = $clientId; $this->clientSecret = $clientSecret; $this->authClient = $authClient; @@ -65,8 +64,9 @@ function __construct( * * @return string */ - public function getToken(): string { - if ($this->accessToken !== null && ($this->expiresAt === null || $this->expiresAt > new DateTime())){ + public function getToken(): string + { + if ($this->accessToken !== null && ($this->expiresAt === null || $this->expiresAt > new DateTime())) { return $this->accessToken; } return $this->refresh(); @@ -77,7 +77,8 @@ public function getToken(): string { * * @return string */ - private function refresh(): string { + private function refresh(): string + { $request = new GetTokenRequest([ 'clientId' => $this->clientId, 'clientSecret' => $this->clientSecret, @@ -99,7 +100,8 @@ private function refresh(): string { * @param int $bufferInMinutes The buffer time in minutes to subtract from the expiration. * @return DateTime */ - private function getExpiresAt(int $expiresInSeconds, int $bufferInMinutes): DateTime { + private function getExpiresAt(int $expiresInSeconds, int $bufferInMinutes): DateTime + { $now = new DateTime(); $expiresInSecondsWithBuffer = $expiresInSeconds - ($bufferInMinutes * 60); $now->modify("+{$expiresInSecondsWithBuffer} seconds"); diff --git a/seed/php-sdk/oauth-client-credentials-default/src/Core/Types/ArrayType.php b/seed/php-sdk/oauth-client-credentials-default/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/oauth-client-credentials-default/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/oauth-client-credentials-default/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/oauth-client-credentials-default/src/Core/Types/Constant.php b/seed/php-sdk/oauth-client-credentials-default/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/oauth-client-credentials-default/src/Core/Types/Constant.php +++ b/seed/php-sdk/oauth-client-credentials-default/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-default/src/Core/Types/Union.php b/seed/php-sdk/oauth-client-credentials-default/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/oauth-client-credentials-default/src/Core/Types/Union.php +++ b/seed/php-sdk/oauth-client-credentials-default/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-default/src/Exceptions/SeedApiException.php b/seed/php-sdk/oauth-client-credentials-default/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/oauth-client-credentials-default/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/oauth-client-credentials-default/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/oauth-client-credentials-default/src/Exceptions/SeedException.php b/seed/php-sdk/oauth-client-credentials-default/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/oauth-client-credentials-default/src/Exceptions/SeedException.php +++ b/seed/php-sdk/oauth-client-credentials-default/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/oauth-client-credentials-default/src/Nested/Api/ApiClient.php b/seed/php-sdk/oauth-client-credentials-default/src/Nested/Api/ApiClient.php index 55aa05a36ce7..ce6b4aec2e42 100644 --- a/seed/php-sdk/oauth-client-credentials-default/src/Nested/Api/ApiClient.php +++ b/seed/php-sdk/oauth-client-credentials-default/src/Nested/Api/ApiClient.php @@ -11,7 +11,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class ApiClient +class ApiClient { /** * @var array{ @@ -39,11 +39,10 @@ class ApiClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -60,7 +59,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getSomething(?array $options = null): void { + public function getSomething(?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -72,12 +72,12 @@ public function getSomething(?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/oauth-client-credentials-default/src/Nested/NestedClient.php b/seed/php-sdk/oauth-client-credentials-default/src/Nested/NestedClient.php index 7857c019672d..26f17dd0c9ac 100644 --- a/seed/php-sdk/oauth-client-credentials-default/src/Nested/NestedClient.php +++ b/seed/php-sdk/oauth-client-credentials-default/src/Nested/NestedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class NestedClient +class NestedClient { /** * @var ApiClient $api @@ -39,11 +39,10 @@ class NestedClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; $this->api = new ApiClient($this->client, $this->options); diff --git a/seed/php-sdk/oauth-client-credentials-default/src/NestedNoAuth/Api/ApiClient.php b/seed/php-sdk/oauth-client-credentials-default/src/NestedNoAuth/Api/ApiClient.php index 6141523e925e..2bd84c067499 100644 --- a/seed/php-sdk/oauth-client-credentials-default/src/NestedNoAuth/Api/ApiClient.php +++ b/seed/php-sdk/oauth-client-credentials-default/src/NestedNoAuth/Api/ApiClient.php @@ -11,7 +11,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class ApiClient +class ApiClient { /** * @var array{ @@ -39,11 +39,10 @@ class ApiClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -60,7 +59,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getSomething(?array $options = null): void { + public function getSomething(?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -72,12 +72,12 @@ public function getSomething(?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/oauth-client-credentials-default/src/NestedNoAuth/NestedNoAuthClient.php b/seed/php-sdk/oauth-client-credentials-default/src/NestedNoAuth/NestedNoAuthClient.php index c9f5659a49b0..f7c33619f24c 100644 --- a/seed/php-sdk/oauth-client-credentials-default/src/NestedNoAuth/NestedNoAuthClient.php +++ b/seed/php-sdk/oauth-client-credentials-default/src/NestedNoAuth/NestedNoAuthClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class NestedNoAuthClient +class NestedNoAuthClient { /** * @var ApiClient $api @@ -39,11 +39,10 @@ class NestedNoAuthClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; $this->api = new ApiClient($this->client, $this->options); diff --git a/seed/php-sdk/oauth-client-credentials-default/src/SeedClient.php b/seed/php-sdk/oauth-client-credentials-default/src/SeedClient.php index b5f6e7a8a94c..b91741fbc970 100644 --- a/seed/php-sdk/oauth-client-credentials-default/src/SeedClient.php +++ b/seed/php-sdk/oauth-client-credentials-default/src/SeedClient.php @@ -10,7 +10,7 @@ use Seed\Core\Client\RawClient; use Seed\Core\OAuthTokenProvider; -class SeedClient +class SeedClient { /** * @var AuthClient $auth @@ -48,6 +48,11 @@ class SeedClient */ private RawClient $client; + /** + * @var OAuthTokenProvider $oauthTokenProvider + */ + private OAuthTokenProvider $oauthTokenProvider; + /** * @param ?string $clientId The client ID for OAuth authentication. * @param ?string $clientSecret The client secret for OAuth authentication. @@ -63,32 +68,32 @@ public function __construct( ?string $clientId = null, ?string $clientSecret = null, ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $authRawClient = new RawClient(['headers' => []]); $authClient = new AuthClient($authRawClient); - $oauthTokenProvider = new OAuthTokenProvider($clientId ?? '', $clientSecret ?? '', $authClient); - $token = $oauthTokenProvider->getToken(); - - $defaultHeaders['Authorization'] = "Bearer $token"; + $this->oauthTokenProvider = new OAuthTokenProvider($clientId ?? '', $clientSecret ?? '', $authClient); + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + + $this->options['getAuthHeaders'] = fn () => + ['Authorization' => "Bearer " . $this->oauthTokenProvider->getToken()]; + $this->client = new RawClient( options: $this->options, ); - + $this->auth = new AuthClient($this->client, $this->options); $this->nestedNoAuth = new NestedNoAuthClient($this->client, $this->options); $this->nested = new NestedClient($this->client, $this->options); diff --git a/seed/php-sdk/oauth-client-credentials-default/src/Simple/SimpleClient.php b/seed/php-sdk/oauth-client-credentials-default/src/Simple/SimpleClient.php index 50b6b54f52c0..7a8df43cf28d 100644 --- a/seed/php-sdk/oauth-client-credentials-default/src/Simple/SimpleClient.php +++ b/seed/php-sdk/oauth-client-credentials-default/src/Simple/SimpleClient.php @@ -11,7 +11,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class SimpleClient +class SimpleClient { /** * @var array{ @@ -39,11 +39,10 @@ class SimpleClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -60,7 +59,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getSomething(?array $options = null): void { + public function getSomething(?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -72,12 +72,12 @@ public function getSomething(?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/oauth-client-credentials-default/src/Utils/File.php b/seed/php-sdk/oauth-client-credentials-default/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/oauth-client-credentials-default/src/Utils/File.php +++ b/seed/php-sdk/oauth-client-credentials-default/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-default/tests/Core/Client/RawClientTest.php b/seed/php-sdk/oauth-client-credentials-default/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/oauth-client-credentials-default/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/oauth-client-credentials-default/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/oauth-client-credentials-default/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/oauth-client-credentials-default/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/oauth-client-credentials-default/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/oauth-client-credentials-default/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-default/tests/Core/Json/EnumTest.php b/seed/php-sdk/oauth-client-credentials-default/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/oauth-client-credentials-default/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/oauth-client-credentials-default/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-default/tests/Core/Json/TraitTest.php b/seed/php-sdk/oauth-client-credentials-default/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/oauth-client-credentials-default/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/oauth-client-credentials-default/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-default/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/oauth-client-credentials-default/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/oauth-client-credentials-default/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/oauth-client-credentials-default/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-environment-variables/src/Auth/AuthClient.php b/seed/php-sdk/oauth-client-credentials-environment-variables/src/Auth/AuthClient.php index 10031485bd29..54af167b9931 100644 --- a/seed/php-sdk/oauth-client-credentials-environment-variables/src/Auth/AuthClient.php +++ b/seed/php-sdk/oauth-client-credentials-environment-variables/src/Auth/AuthClient.php @@ -15,7 +15,7 @@ use Psr\Http\Client\ClientExceptionInterface; use Seed\Auth\Requests\RefreshTokenRequest; -class AuthClient +class AuthClient { /** * @var array{ @@ -43,11 +43,10 @@ class AuthClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -66,7 +65,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getTokenWithClientCredentials(GetTokenRequest $request, ?array $options = null): TokenResponse { + public function getTokenWithClientCredentials(GetTokenRequest $request, ?array $options = null): TokenResponse + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -79,15 +79,15 @@ public function getTokenWithClientCredentials(GetTokenRequest $request, ?array $ $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return TokenResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -119,7 +119,8 @@ public function getTokenWithClientCredentials(GetTokenRequest $request, ?array $ * @throws SeedException * @throws SeedApiException */ - public function refreshToken(RefreshTokenRequest $request, ?array $options = null): TokenResponse { + public function refreshToken(RefreshTokenRequest $request, ?array $options = null): TokenResponse + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -132,15 +133,15 @@ public function refreshToken(RefreshTokenRequest $request, ?array $options = nul $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return TokenResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/oauth-client-credentials-environment-variables/src/Auth/Requests/GetTokenRequest.php b/seed/php-sdk/oauth-client-credentials-environment-variables/src/Auth/Requests/GetTokenRequest.php index 4b7e15ee540f..87cdf357e742 100644 --- a/seed/php-sdk/oauth-client-credentials-environment-variables/src/Auth/Requests/GetTokenRequest.php +++ b/seed/php-sdk/oauth-client-credentials-environment-variables/src/Auth/Requests/GetTokenRequest.php @@ -48,8 +48,11 @@ class GetTokenRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->clientId = $values['clientId'];$this->clientSecret = $values['clientSecret'];$this->audience = $values['audience'];$this->grantType = $values['grantType'];$this->scope = $values['scope'] ?? null; + ) { + $this->clientId = $values['clientId']; + $this->clientSecret = $values['clientSecret']; + $this->audience = $values['audience']; + $this->grantType = $values['grantType']; + $this->scope = $values['scope'] ?? null; } } diff --git a/seed/php-sdk/oauth-client-credentials-environment-variables/src/Auth/Requests/RefreshTokenRequest.php b/seed/php-sdk/oauth-client-credentials-environment-variables/src/Auth/Requests/RefreshTokenRequest.php index 356e12500b21..e9c9a6d94ec5 100644 --- a/seed/php-sdk/oauth-client-credentials-environment-variables/src/Auth/Requests/RefreshTokenRequest.php +++ b/seed/php-sdk/oauth-client-credentials-environment-variables/src/Auth/Requests/RefreshTokenRequest.php @@ -55,8 +55,12 @@ class RefreshTokenRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->clientId = $values['clientId'];$this->clientSecret = $values['clientSecret'];$this->refreshToken = $values['refreshToken'];$this->audience = $values['audience'];$this->grantType = $values['grantType'];$this->scope = $values['scope'] ?? null; + ) { + $this->clientId = $values['clientId']; + $this->clientSecret = $values['clientSecret']; + $this->refreshToken = $values['refreshToken']; + $this->audience = $values['audience']; + $this->grantType = $values['grantType']; + $this->scope = $values['scope'] ?? null; } } diff --git a/seed/php-sdk/oauth-client-credentials-environment-variables/src/Auth/Types/TokenResponse.php b/seed/php-sdk/oauth-client-credentials-environment-variables/src/Auth/Types/TokenResponse.php index 2a7c2ec254a1..f5379b9051d4 100644 --- a/seed/php-sdk/oauth-client-credentials-environment-variables/src/Auth/Types/TokenResponse.php +++ b/seed/php-sdk/oauth-client-credentials-environment-variables/src/Auth/Types/TokenResponse.php @@ -37,15 +37,17 @@ class TokenResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->accessToken = $values['accessToken'];$this->expiresIn = $values['expiresIn'];$this->refreshToken = $values['refreshToken'] ?? null; + ) { + $this->accessToken = $values['accessToken']; + $this->expiresIn = $values['expiresIn']; + $this->refreshToken = $values['refreshToken'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/oauth-client-credentials-environment-variables/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/oauth-client-credentials-environment-variables/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/oauth-client-credentials-environment-variables/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/oauth-client-credentials-environment-variables/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-environment-variables/src/Core/Client/RawClient.php b/seed/php-sdk/oauth-client-credentials-environment-variables/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/oauth-client-credentials-environment-variables/src/Core/Client/RawClient.php +++ b/seed/php-sdk/oauth-client-credentials-environment-variables/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/oauth-client-credentials-environment-variables/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/oauth-client-credentials-environment-variables/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/oauth-client-credentials-environment-variables/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/oauth-client-credentials-environment-variables/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/oauth-client-credentials-environment-variables/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/oauth-client-credentials-environment-variables/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/oauth-client-credentials-environment-variables/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/oauth-client-credentials-environment-variables/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-environment-variables/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/oauth-client-credentials-environment-variables/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/oauth-client-credentials-environment-variables/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/oauth-client-credentials-environment-variables/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-environment-variables/src/Core/OAuthTokenProvider.php b/seed/php-sdk/oauth-client-credentials-environment-variables/src/Core/OAuthTokenProvider.php index 6f95aeb521f1..42621574a6f4 100644 --- a/seed/php-sdk/oauth-client-credentials-environment-variables/src/Core/OAuthTokenProvider.php +++ b/seed/php-sdk/oauth-client-credentials-environment-variables/src/Core/OAuthTokenProvider.php @@ -10,7 +10,7 @@ * The OAuthTokenProvider retrieves an OAuth access token, refreshing it as needed. * The access token is then used as the bearer token in every authenticated request. */ -class OAuthTokenProvider +class OAuthTokenProvider { /** * @var int $BUFFER_IN_MINUTES @@ -47,12 +47,11 @@ class OAuthTokenProvider * @param string $clientSecret The client secret for OAuth authentication. * @param AuthClient $authClient The client used to retrieve the OAuth token. */ - function __construct( + public function __construct( string $clientId, string $clientSecret, AuthClient $authClient, - ) - { + ) { $this->clientId = $clientId; $this->clientSecret = $clientSecret; $this->authClient = $authClient; @@ -65,8 +64,9 @@ function __construct( * * @return string */ - public function getToken(): string { - if ($this->accessToken !== null && ($this->expiresAt === null || $this->expiresAt > new DateTime())){ + public function getToken(): string + { + if ($this->accessToken !== null && ($this->expiresAt === null || $this->expiresAt > new DateTime())) { return $this->accessToken; } return $this->refresh(); @@ -77,7 +77,8 @@ public function getToken(): string { * * @return string */ - private function refresh(): string { + private function refresh(): string + { $request = new GetTokenRequest([ 'clientId' => $this->clientId, 'clientSecret' => $this->clientSecret, @@ -100,7 +101,8 @@ private function refresh(): string { * @param int $bufferInMinutes The buffer time in minutes to subtract from the expiration. * @return DateTime */ - private function getExpiresAt(int $expiresInSeconds, int $bufferInMinutes): DateTime { + private function getExpiresAt(int $expiresInSeconds, int $bufferInMinutes): DateTime + { $now = new DateTime(); $expiresInSecondsWithBuffer = $expiresInSeconds - ($bufferInMinutes * 60); $now->modify("+{$expiresInSecondsWithBuffer} seconds"); diff --git a/seed/php-sdk/oauth-client-credentials-environment-variables/src/Core/Types/ArrayType.php b/seed/php-sdk/oauth-client-credentials-environment-variables/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/oauth-client-credentials-environment-variables/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/oauth-client-credentials-environment-variables/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/oauth-client-credentials-environment-variables/src/Core/Types/Constant.php b/seed/php-sdk/oauth-client-credentials-environment-variables/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/oauth-client-credentials-environment-variables/src/Core/Types/Constant.php +++ b/seed/php-sdk/oauth-client-credentials-environment-variables/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-environment-variables/src/Core/Types/Union.php b/seed/php-sdk/oauth-client-credentials-environment-variables/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/oauth-client-credentials-environment-variables/src/Core/Types/Union.php +++ b/seed/php-sdk/oauth-client-credentials-environment-variables/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-environment-variables/src/Exceptions/SeedApiException.php b/seed/php-sdk/oauth-client-credentials-environment-variables/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/oauth-client-credentials-environment-variables/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/oauth-client-credentials-environment-variables/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/oauth-client-credentials-environment-variables/src/Exceptions/SeedException.php b/seed/php-sdk/oauth-client-credentials-environment-variables/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/oauth-client-credentials-environment-variables/src/Exceptions/SeedException.php +++ b/seed/php-sdk/oauth-client-credentials-environment-variables/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/oauth-client-credentials-environment-variables/src/Nested/Api/ApiClient.php b/seed/php-sdk/oauth-client-credentials-environment-variables/src/Nested/Api/ApiClient.php index 55aa05a36ce7..ce6b4aec2e42 100644 --- a/seed/php-sdk/oauth-client-credentials-environment-variables/src/Nested/Api/ApiClient.php +++ b/seed/php-sdk/oauth-client-credentials-environment-variables/src/Nested/Api/ApiClient.php @@ -11,7 +11,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class ApiClient +class ApiClient { /** * @var array{ @@ -39,11 +39,10 @@ class ApiClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -60,7 +59,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getSomething(?array $options = null): void { + public function getSomething(?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -72,12 +72,12 @@ public function getSomething(?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/oauth-client-credentials-environment-variables/src/Nested/NestedClient.php b/seed/php-sdk/oauth-client-credentials-environment-variables/src/Nested/NestedClient.php index 7857c019672d..26f17dd0c9ac 100644 --- a/seed/php-sdk/oauth-client-credentials-environment-variables/src/Nested/NestedClient.php +++ b/seed/php-sdk/oauth-client-credentials-environment-variables/src/Nested/NestedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class NestedClient +class NestedClient { /** * @var ApiClient $api @@ -39,11 +39,10 @@ class NestedClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; $this->api = new ApiClient($this->client, $this->options); diff --git a/seed/php-sdk/oauth-client-credentials-environment-variables/src/NestedNoAuth/Api/ApiClient.php b/seed/php-sdk/oauth-client-credentials-environment-variables/src/NestedNoAuth/Api/ApiClient.php index 6141523e925e..2bd84c067499 100644 --- a/seed/php-sdk/oauth-client-credentials-environment-variables/src/NestedNoAuth/Api/ApiClient.php +++ b/seed/php-sdk/oauth-client-credentials-environment-variables/src/NestedNoAuth/Api/ApiClient.php @@ -11,7 +11,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class ApiClient +class ApiClient { /** * @var array{ @@ -39,11 +39,10 @@ class ApiClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -60,7 +59,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getSomething(?array $options = null): void { + public function getSomething(?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -72,12 +72,12 @@ public function getSomething(?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/oauth-client-credentials-environment-variables/src/NestedNoAuth/NestedNoAuthClient.php b/seed/php-sdk/oauth-client-credentials-environment-variables/src/NestedNoAuth/NestedNoAuthClient.php index c9f5659a49b0..f7c33619f24c 100644 --- a/seed/php-sdk/oauth-client-credentials-environment-variables/src/NestedNoAuth/NestedNoAuthClient.php +++ b/seed/php-sdk/oauth-client-credentials-environment-variables/src/NestedNoAuth/NestedNoAuthClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class NestedNoAuthClient +class NestedNoAuthClient { /** * @var ApiClient $api @@ -39,11 +39,10 @@ class NestedNoAuthClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; $this->api = new ApiClient($this->client, $this->options); diff --git a/seed/php-sdk/oauth-client-credentials-environment-variables/src/SeedClient.php b/seed/php-sdk/oauth-client-credentials-environment-variables/src/SeedClient.php index 79e86ab774e6..28ee5928c1e7 100644 --- a/seed/php-sdk/oauth-client-credentials-environment-variables/src/SeedClient.php +++ b/seed/php-sdk/oauth-client-credentials-environment-variables/src/SeedClient.php @@ -11,7 +11,7 @@ use Seed\Core\OAuthTokenProvider; use Exception; -class SeedClient +class SeedClient { /** * @var AuthClient $auth @@ -49,6 +49,11 @@ class SeedClient */ private RawClient $client; + /** + * @var OAuthTokenProvider $oauthTokenProvider + */ + private OAuthTokenProvider $oauthTokenProvider; + /** * @param ?string $clientId The client ID for OAuth authentication. * @param ?string $clientSecret The client secret for OAuth authentication. @@ -64,8 +69,7 @@ public function __construct( ?string $clientId = null, ?string $clientSecret = null, ?array $options = null, - ) - { + ) { $clientId ??= $this->getFromEnvOrThrow('CLIENT_ID', 'Please pass in clientId or set the environment variable CLIENT_ID.'); $clientSecret ??= $this->getFromEnvOrThrow('CLIENT_SECRET', 'Please pass in clientSecret or set the environment variable CLIENT_SECRET.'); $defaultHeaders = [ @@ -74,24 +78,25 @@ public function __construct( 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $authRawClient = new RawClient(['headers' => []]); $authClient = new AuthClient($authRawClient); - $oauthTokenProvider = new OAuthTokenProvider($clientId ?? '', $clientSecret ?? '', $authClient); - $token = $oauthTokenProvider->getToken(); - - $defaultHeaders['Authorization'] = "Bearer $token"; + $this->oauthTokenProvider = new OAuthTokenProvider($clientId ?? '', $clientSecret ?? '', $authClient); + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + + $this->options['getAuthHeaders'] = fn () => + ['Authorization' => "Bearer " . $this->oauthTokenProvider->getToken()]; + $this->client = new RawClient( options: $this->options, ); - + $this->auth = new AuthClient($this->client, $this->options); $this->nestedNoAuth = new NestedNoAuthClient($this->client, $this->options); $this->nested = new NestedClient($this->client, $this->options); @@ -103,7 +108,8 @@ public function __construct( * @param string $message * @return string */ - private function getFromEnvOrThrow(string $env, string $message): string { + private function getFromEnvOrThrow(string $env, string $message): string + { $value = getenv($env); return $value ? (string) $value : throw new Exception($message); } diff --git a/seed/php-sdk/oauth-client-credentials-environment-variables/src/Simple/SimpleClient.php b/seed/php-sdk/oauth-client-credentials-environment-variables/src/Simple/SimpleClient.php index 50b6b54f52c0..7a8df43cf28d 100644 --- a/seed/php-sdk/oauth-client-credentials-environment-variables/src/Simple/SimpleClient.php +++ b/seed/php-sdk/oauth-client-credentials-environment-variables/src/Simple/SimpleClient.php @@ -11,7 +11,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class SimpleClient +class SimpleClient { /** * @var array{ @@ -39,11 +39,10 @@ class SimpleClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -60,7 +59,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getSomething(?array $options = null): void { + public function getSomething(?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -72,12 +72,12 @@ public function getSomething(?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/oauth-client-credentials-environment-variables/src/Utils/File.php b/seed/php-sdk/oauth-client-credentials-environment-variables/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/oauth-client-credentials-environment-variables/src/Utils/File.php +++ b/seed/php-sdk/oauth-client-credentials-environment-variables/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-environment-variables/tests/Core/Client/RawClientTest.php b/seed/php-sdk/oauth-client-credentials-environment-variables/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/oauth-client-credentials-environment-variables/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/oauth-client-credentials-environment-variables/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/oauth-client-credentials-environment-variables/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/oauth-client-credentials-environment-variables/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/oauth-client-credentials-environment-variables/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/oauth-client-credentials-environment-variables/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-environment-variables/tests/Core/Json/EnumTest.php b/seed/php-sdk/oauth-client-credentials-environment-variables/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/oauth-client-credentials-environment-variables/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/oauth-client-credentials-environment-variables/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-environment-variables/tests/Core/Json/TraitTest.php b/seed/php-sdk/oauth-client-credentials-environment-variables/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/oauth-client-credentials-environment-variables/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/oauth-client-credentials-environment-variables/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-environment-variables/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/oauth-client-credentials-environment-variables/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/oauth-client-credentials-environment-variables/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/oauth-client-credentials-environment-variables/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-nested-root/src/Auth/AuthClient.php b/seed/php-sdk/oauth-client-credentials-nested-root/src/Auth/AuthClient.php index 684754285971..d66079e55918 100644 --- a/seed/php-sdk/oauth-client-credentials-nested-root/src/Auth/AuthClient.php +++ b/seed/php-sdk/oauth-client-credentials-nested-root/src/Auth/AuthClient.php @@ -14,7 +14,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class AuthClient +class AuthClient { /** * @var array{ @@ -42,11 +42,10 @@ class AuthClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -65,7 +64,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getToken(GetTokenRequest $request, ?array $options = null): TokenResponse { + public function getToken(GetTokenRequest $request, ?array $options = null): TokenResponse + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -78,15 +78,15 @@ public function getToken(GetTokenRequest $request, ?array $options = null): Toke $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return TokenResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/oauth-client-credentials-nested-root/src/Auth/Requests/GetTokenRequest.php b/seed/php-sdk/oauth-client-credentials-nested-root/src/Auth/Requests/GetTokenRequest.php index 4b7e15ee540f..87cdf357e742 100644 --- a/seed/php-sdk/oauth-client-credentials-nested-root/src/Auth/Requests/GetTokenRequest.php +++ b/seed/php-sdk/oauth-client-credentials-nested-root/src/Auth/Requests/GetTokenRequest.php @@ -48,8 +48,11 @@ class GetTokenRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->clientId = $values['clientId'];$this->clientSecret = $values['clientSecret'];$this->audience = $values['audience'];$this->grantType = $values['grantType'];$this->scope = $values['scope'] ?? null; + ) { + $this->clientId = $values['clientId']; + $this->clientSecret = $values['clientSecret']; + $this->audience = $values['audience']; + $this->grantType = $values['grantType']; + $this->scope = $values['scope'] ?? null; } } diff --git a/seed/php-sdk/oauth-client-credentials-nested-root/src/Auth/Types/TokenResponse.php b/seed/php-sdk/oauth-client-credentials-nested-root/src/Auth/Types/TokenResponse.php index 2a7c2ec254a1..f5379b9051d4 100644 --- a/seed/php-sdk/oauth-client-credentials-nested-root/src/Auth/Types/TokenResponse.php +++ b/seed/php-sdk/oauth-client-credentials-nested-root/src/Auth/Types/TokenResponse.php @@ -37,15 +37,17 @@ class TokenResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->accessToken = $values['accessToken'];$this->expiresIn = $values['expiresIn'];$this->refreshToken = $values['refreshToken'] ?? null; + ) { + $this->accessToken = $values['accessToken']; + $this->expiresIn = $values['expiresIn']; + $this->refreshToken = $values['refreshToken'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/oauth-client-credentials-nested-root/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/oauth-client-credentials-nested-root/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/oauth-client-credentials-nested-root/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/oauth-client-credentials-nested-root/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-nested-root/src/Core/Client/RawClient.php b/seed/php-sdk/oauth-client-credentials-nested-root/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/oauth-client-credentials-nested-root/src/Core/Client/RawClient.php +++ b/seed/php-sdk/oauth-client-credentials-nested-root/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/oauth-client-credentials-nested-root/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/oauth-client-credentials-nested-root/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/oauth-client-credentials-nested-root/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/oauth-client-credentials-nested-root/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/oauth-client-credentials-nested-root/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/oauth-client-credentials-nested-root/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/oauth-client-credentials-nested-root/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/oauth-client-credentials-nested-root/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-nested-root/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/oauth-client-credentials-nested-root/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/oauth-client-credentials-nested-root/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/oauth-client-credentials-nested-root/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-nested-root/src/Core/OAuthTokenProvider.php b/seed/php-sdk/oauth-client-credentials-nested-root/src/Core/OAuthTokenProvider.php index dc2f22ca5408..47463ef3deb8 100644 --- a/seed/php-sdk/oauth-client-credentials-nested-root/src/Core/OAuthTokenProvider.php +++ b/seed/php-sdk/oauth-client-credentials-nested-root/src/Core/OAuthTokenProvider.php @@ -10,7 +10,7 @@ * The OAuthTokenProvider retrieves an OAuth access token, refreshing it as needed. * The access token is then used as the bearer token in every authenticated request. */ -class OAuthTokenProvider +class OAuthTokenProvider { /** * @var int $BUFFER_IN_MINUTES @@ -47,12 +47,11 @@ class OAuthTokenProvider * @param string $clientSecret The client secret for OAuth authentication. * @param AuthClient $authClient The client used to retrieve the OAuth token. */ - function __construct( + public function __construct( string $clientId, string $clientSecret, AuthClient $authClient, - ) - { + ) { $this->clientId = $clientId; $this->clientSecret = $clientSecret; $this->authClient = $authClient; @@ -65,8 +64,9 @@ function __construct( * * @return string */ - public function getToken(): string { - if ($this->accessToken !== null && ($this->expiresAt === null || $this->expiresAt > new DateTime())){ + public function getToken(): string + { + if ($this->accessToken !== null && ($this->expiresAt === null || $this->expiresAt > new DateTime())) { return $this->accessToken; } return $this->refresh(); @@ -77,7 +77,8 @@ public function getToken(): string { * * @return string */ - private function refresh(): string { + private function refresh(): string + { $request = new GetTokenRequest([ 'clientId' => $this->clientId, 'clientSecret' => $this->clientSecret, @@ -100,7 +101,8 @@ private function refresh(): string { * @param int $bufferInMinutes The buffer time in minutes to subtract from the expiration. * @return DateTime */ - private function getExpiresAt(int $expiresInSeconds, int $bufferInMinutes): DateTime { + private function getExpiresAt(int $expiresInSeconds, int $bufferInMinutes): DateTime + { $now = new DateTime(); $expiresInSecondsWithBuffer = $expiresInSeconds - ($bufferInMinutes * 60); $now->modify("+{$expiresInSecondsWithBuffer} seconds"); diff --git a/seed/php-sdk/oauth-client-credentials-nested-root/src/Core/Types/ArrayType.php b/seed/php-sdk/oauth-client-credentials-nested-root/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/oauth-client-credentials-nested-root/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/oauth-client-credentials-nested-root/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/oauth-client-credentials-nested-root/src/Core/Types/Constant.php b/seed/php-sdk/oauth-client-credentials-nested-root/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/oauth-client-credentials-nested-root/src/Core/Types/Constant.php +++ b/seed/php-sdk/oauth-client-credentials-nested-root/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-nested-root/src/Core/Types/Union.php b/seed/php-sdk/oauth-client-credentials-nested-root/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/oauth-client-credentials-nested-root/src/Core/Types/Union.php +++ b/seed/php-sdk/oauth-client-credentials-nested-root/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-nested-root/src/Exceptions/SeedApiException.php b/seed/php-sdk/oauth-client-credentials-nested-root/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/oauth-client-credentials-nested-root/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/oauth-client-credentials-nested-root/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/oauth-client-credentials-nested-root/src/Exceptions/SeedException.php b/seed/php-sdk/oauth-client-credentials-nested-root/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/oauth-client-credentials-nested-root/src/Exceptions/SeedException.php +++ b/seed/php-sdk/oauth-client-credentials-nested-root/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/oauth-client-credentials-nested-root/src/Nested/Api/ApiClient.php b/seed/php-sdk/oauth-client-credentials-nested-root/src/Nested/Api/ApiClient.php index 55aa05a36ce7..ce6b4aec2e42 100644 --- a/seed/php-sdk/oauth-client-credentials-nested-root/src/Nested/Api/ApiClient.php +++ b/seed/php-sdk/oauth-client-credentials-nested-root/src/Nested/Api/ApiClient.php @@ -11,7 +11,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class ApiClient +class ApiClient { /** * @var array{ @@ -39,11 +39,10 @@ class ApiClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -60,7 +59,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getSomething(?array $options = null): void { + public function getSomething(?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -72,12 +72,12 @@ public function getSomething(?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/oauth-client-credentials-nested-root/src/Nested/NestedClient.php b/seed/php-sdk/oauth-client-credentials-nested-root/src/Nested/NestedClient.php index 7857c019672d..26f17dd0c9ac 100644 --- a/seed/php-sdk/oauth-client-credentials-nested-root/src/Nested/NestedClient.php +++ b/seed/php-sdk/oauth-client-credentials-nested-root/src/Nested/NestedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class NestedClient +class NestedClient { /** * @var ApiClient $api @@ -39,11 +39,10 @@ class NestedClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; $this->api = new ApiClient($this->client, $this->options); diff --git a/seed/php-sdk/oauth-client-credentials-nested-root/src/NestedNoAuth/Api/ApiClient.php b/seed/php-sdk/oauth-client-credentials-nested-root/src/NestedNoAuth/Api/ApiClient.php index 6141523e925e..2bd84c067499 100644 --- a/seed/php-sdk/oauth-client-credentials-nested-root/src/NestedNoAuth/Api/ApiClient.php +++ b/seed/php-sdk/oauth-client-credentials-nested-root/src/NestedNoAuth/Api/ApiClient.php @@ -11,7 +11,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class ApiClient +class ApiClient { /** * @var array{ @@ -39,11 +39,10 @@ class ApiClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -60,7 +59,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getSomething(?array $options = null): void { + public function getSomething(?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -72,12 +72,12 @@ public function getSomething(?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/oauth-client-credentials-nested-root/src/NestedNoAuth/NestedNoAuthClient.php b/seed/php-sdk/oauth-client-credentials-nested-root/src/NestedNoAuth/NestedNoAuthClient.php index c9f5659a49b0..f7c33619f24c 100644 --- a/seed/php-sdk/oauth-client-credentials-nested-root/src/NestedNoAuth/NestedNoAuthClient.php +++ b/seed/php-sdk/oauth-client-credentials-nested-root/src/NestedNoAuth/NestedNoAuthClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class NestedNoAuthClient +class NestedNoAuthClient { /** * @var ApiClient $api @@ -39,11 +39,10 @@ class NestedNoAuthClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; $this->api = new ApiClient($this->client, $this->options); diff --git a/seed/php-sdk/oauth-client-credentials-nested-root/src/SeedClient.php b/seed/php-sdk/oauth-client-credentials-nested-root/src/SeedClient.php index b5f6e7a8a94c..b91741fbc970 100644 --- a/seed/php-sdk/oauth-client-credentials-nested-root/src/SeedClient.php +++ b/seed/php-sdk/oauth-client-credentials-nested-root/src/SeedClient.php @@ -10,7 +10,7 @@ use Seed\Core\Client\RawClient; use Seed\Core\OAuthTokenProvider; -class SeedClient +class SeedClient { /** * @var AuthClient $auth @@ -48,6 +48,11 @@ class SeedClient */ private RawClient $client; + /** + * @var OAuthTokenProvider $oauthTokenProvider + */ + private OAuthTokenProvider $oauthTokenProvider; + /** * @param ?string $clientId The client ID for OAuth authentication. * @param ?string $clientSecret The client secret for OAuth authentication. @@ -63,32 +68,32 @@ public function __construct( ?string $clientId = null, ?string $clientSecret = null, ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $authRawClient = new RawClient(['headers' => []]); $authClient = new AuthClient($authRawClient); - $oauthTokenProvider = new OAuthTokenProvider($clientId ?? '', $clientSecret ?? '', $authClient); - $token = $oauthTokenProvider->getToken(); - - $defaultHeaders['Authorization'] = "Bearer $token"; + $this->oauthTokenProvider = new OAuthTokenProvider($clientId ?? '', $clientSecret ?? '', $authClient); + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + + $this->options['getAuthHeaders'] = fn () => + ['Authorization' => "Bearer " . $this->oauthTokenProvider->getToken()]; + $this->client = new RawClient( options: $this->options, ); - + $this->auth = new AuthClient($this->client, $this->options); $this->nestedNoAuth = new NestedNoAuthClient($this->client, $this->options); $this->nested = new NestedClient($this->client, $this->options); diff --git a/seed/php-sdk/oauth-client-credentials-nested-root/src/Simple/SimpleClient.php b/seed/php-sdk/oauth-client-credentials-nested-root/src/Simple/SimpleClient.php index 50b6b54f52c0..7a8df43cf28d 100644 --- a/seed/php-sdk/oauth-client-credentials-nested-root/src/Simple/SimpleClient.php +++ b/seed/php-sdk/oauth-client-credentials-nested-root/src/Simple/SimpleClient.php @@ -11,7 +11,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class SimpleClient +class SimpleClient { /** * @var array{ @@ -39,11 +39,10 @@ class SimpleClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -60,7 +59,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getSomething(?array $options = null): void { + public function getSomething(?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -72,12 +72,12 @@ public function getSomething(?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/oauth-client-credentials-nested-root/src/Utils/File.php b/seed/php-sdk/oauth-client-credentials-nested-root/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/oauth-client-credentials-nested-root/src/Utils/File.php +++ b/seed/php-sdk/oauth-client-credentials-nested-root/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-nested-root/tests/Core/Client/RawClientTest.php b/seed/php-sdk/oauth-client-credentials-nested-root/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/oauth-client-credentials-nested-root/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/oauth-client-credentials-nested-root/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/oauth-client-credentials-nested-root/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/oauth-client-credentials-nested-root/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/oauth-client-credentials-nested-root/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/oauth-client-credentials-nested-root/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-nested-root/tests/Core/Json/EnumTest.php b/seed/php-sdk/oauth-client-credentials-nested-root/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/oauth-client-credentials-nested-root/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/oauth-client-credentials-nested-root/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-nested-root/tests/Core/Json/TraitTest.php b/seed/php-sdk/oauth-client-credentials-nested-root/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/oauth-client-credentials-nested-root/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/oauth-client-credentials-nested-root/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-nested-root/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/oauth-client-credentials-nested-root/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/oauth-client-credentials-nested-root/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/oauth-client-credentials-nested-root/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-with-variables/src/Auth/AuthClient.php b/seed/php-sdk/oauth-client-credentials-with-variables/src/Auth/AuthClient.php index 10031485bd29..54af167b9931 100644 --- a/seed/php-sdk/oauth-client-credentials-with-variables/src/Auth/AuthClient.php +++ b/seed/php-sdk/oauth-client-credentials-with-variables/src/Auth/AuthClient.php @@ -15,7 +15,7 @@ use Psr\Http\Client\ClientExceptionInterface; use Seed\Auth\Requests\RefreshTokenRequest; -class AuthClient +class AuthClient { /** * @var array{ @@ -43,11 +43,10 @@ class AuthClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -66,7 +65,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getTokenWithClientCredentials(GetTokenRequest $request, ?array $options = null): TokenResponse { + public function getTokenWithClientCredentials(GetTokenRequest $request, ?array $options = null): TokenResponse + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -79,15 +79,15 @@ public function getTokenWithClientCredentials(GetTokenRequest $request, ?array $ $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return TokenResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -119,7 +119,8 @@ public function getTokenWithClientCredentials(GetTokenRequest $request, ?array $ * @throws SeedException * @throws SeedApiException */ - public function refreshToken(RefreshTokenRequest $request, ?array $options = null): TokenResponse { + public function refreshToken(RefreshTokenRequest $request, ?array $options = null): TokenResponse + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -132,15 +133,15 @@ public function refreshToken(RefreshTokenRequest $request, ?array $options = nul $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return TokenResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/oauth-client-credentials-with-variables/src/Auth/Requests/GetTokenRequest.php b/seed/php-sdk/oauth-client-credentials-with-variables/src/Auth/Requests/GetTokenRequest.php index 4b7e15ee540f..87cdf357e742 100644 --- a/seed/php-sdk/oauth-client-credentials-with-variables/src/Auth/Requests/GetTokenRequest.php +++ b/seed/php-sdk/oauth-client-credentials-with-variables/src/Auth/Requests/GetTokenRequest.php @@ -48,8 +48,11 @@ class GetTokenRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->clientId = $values['clientId'];$this->clientSecret = $values['clientSecret'];$this->audience = $values['audience'];$this->grantType = $values['grantType'];$this->scope = $values['scope'] ?? null; + ) { + $this->clientId = $values['clientId']; + $this->clientSecret = $values['clientSecret']; + $this->audience = $values['audience']; + $this->grantType = $values['grantType']; + $this->scope = $values['scope'] ?? null; } } diff --git a/seed/php-sdk/oauth-client-credentials-with-variables/src/Auth/Requests/RefreshTokenRequest.php b/seed/php-sdk/oauth-client-credentials-with-variables/src/Auth/Requests/RefreshTokenRequest.php index 356e12500b21..e9c9a6d94ec5 100644 --- a/seed/php-sdk/oauth-client-credentials-with-variables/src/Auth/Requests/RefreshTokenRequest.php +++ b/seed/php-sdk/oauth-client-credentials-with-variables/src/Auth/Requests/RefreshTokenRequest.php @@ -55,8 +55,12 @@ class RefreshTokenRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->clientId = $values['clientId'];$this->clientSecret = $values['clientSecret'];$this->refreshToken = $values['refreshToken'];$this->audience = $values['audience'];$this->grantType = $values['grantType'];$this->scope = $values['scope'] ?? null; + ) { + $this->clientId = $values['clientId']; + $this->clientSecret = $values['clientSecret']; + $this->refreshToken = $values['refreshToken']; + $this->audience = $values['audience']; + $this->grantType = $values['grantType']; + $this->scope = $values['scope'] ?? null; } } diff --git a/seed/php-sdk/oauth-client-credentials-with-variables/src/Auth/Types/TokenResponse.php b/seed/php-sdk/oauth-client-credentials-with-variables/src/Auth/Types/TokenResponse.php index 2a7c2ec254a1..f5379b9051d4 100644 --- a/seed/php-sdk/oauth-client-credentials-with-variables/src/Auth/Types/TokenResponse.php +++ b/seed/php-sdk/oauth-client-credentials-with-variables/src/Auth/Types/TokenResponse.php @@ -37,15 +37,17 @@ class TokenResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->accessToken = $values['accessToken'];$this->expiresIn = $values['expiresIn'];$this->refreshToken = $values['refreshToken'] ?? null; + ) { + $this->accessToken = $values['accessToken']; + $this->expiresIn = $values['expiresIn']; + $this->refreshToken = $values['refreshToken'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/oauth-client-credentials-with-variables/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/oauth-client-credentials-with-variables/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/oauth-client-credentials-with-variables/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/oauth-client-credentials-with-variables/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-with-variables/src/Core/Client/RawClient.php b/seed/php-sdk/oauth-client-credentials-with-variables/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/oauth-client-credentials-with-variables/src/Core/Client/RawClient.php +++ b/seed/php-sdk/oauth-client-credentials-with-variables/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/oauth-client-credentials-with-variables/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/oauth-client-credentials-with-variables/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/oauth-client-credentials-with-variables/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/oauth-client-credentials-with-variables/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/oauth-client-credentials-with-variables/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/oauth-client-credentials-with-variables/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/oauth-client-credentials-with-variables/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/oauth-client-credentials-with-variables/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-with-variables/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/oauth-client-credentials-with-variables/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/oauth-client-credentials-with-variables/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/oauth-client-credentials-with-variables/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-with-variables/src/Core/OAuthTokenProvider.php b/seed/php-sdk/oauth-client-credentials-with-variables/src/Core/OAuthTokenProvider.php index 6f95aeb521f1..42621574a6f4 100644 --- a/seed/php-sdk/oauth-client-credentials-with-variables/src/Core/OAuthTokenProvider.php +++ b/seed/php-sdk/oauth-client-credentials-with-variables/src/Core/OAuthTokenProvider.php @@ -10,7 +10,7 @@ * The OAuthTokenProvider retrieves an OAuth access token, refreshing it as needed. * The access token is then used as the bearer token in every authenticated request. */ -class OAuthTokenProvider +class OAuthTokenProvider { /** * @var int $BUFFER_IN_MINUTES @@ -47,12 +47,11 @@ class OAuthTokenProvider * @param string $clientSecret The client secret for OAuth authentication. * @param AuthClient $authClient The client used to retrieve the OAuth token. */ - function __construct( + public function __construct( string $clientId, string $clientSecret, AuthClient $authClient, - ) - { + ) { $this->clientId = $clientId; $this->clientSecret = $clientSecret; $this->authClient = $authClient; @@ -65,8 +64,9 @@ function __construct( * * @return string */ - public function getToken(): string { - if ($this->accessToken !== null && ($this->expiresAt === null || $this->expiresAt > new DateTime())){ + public function getToken(): string + { + if ($this->accessToken !== null && ($this->expiresAt === null || $this->expiresAt > new DateTime())) { return $this->accessToken; } return $this->refresh(); @@ -77,7 +77,8 @@ public function getToken(): string { * * @return string */ - private function refresh(): string { + private function refresh(): string + { $request = new GetTokenRequest([ 'clientId' => $this->clientId, 'clientSecret' => $this->clientSecret, @@ -100,7 +101,8 @@ private function refresh(): string { * @param int $bufferInMinutes The buffer time in minutes to subtract from the expiration. * @return DateTime */ - private function getExpiresAt(int $expiresInSeconds, int $bufferInMinutes): DateTime { + private function getExpiresAt(int $expiresInSeconds, int $bufferInMinutes): DateTime + { $now = new DateTime(); $expiresInSecondsWithBuffer = $expiresInSeconds - ($bufferInMinutes * 60); $now->modify("+{$expiresInSecondsWithBuffer} seconds"); diff --git a/seed/php-sdk/oauth-client-credentials-with-variables/src/Core/Types/ArrayType.php b/seed/php-sdk/oauth-client-credentials-with-variables/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/oauth-client-credentials-with-variables/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/oauth-client-credentials-with-variables/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/oauth-client-credentials-with-variables/src/Core/Types/Constant.php b/seed/php-sdk/oauth-client-credentials-with-variables/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/oauth-client-credentials-with-variables/src/Core/Types/Constant.php +++ b/seed/php-sdk/oauth-client-credentials-with-variables/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-with-variables/src/Core/Types/Union.php b/seed/php-sdk/oauth-client-credentials-with-variables/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/oauth-client-credentials-with-variables/src/Core/Types/Union.php +++ b/seed/php-sdk/oauth-client-credentials-with-variables/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-with-variables/src/Exceptions/SeedApiException.php b/seed/php-sdk/oauth-client-credentials-with-variables/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/oauth-client-credentials-with-variables/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/oauth-client-credentials-with-variables/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/oauth-client-credentials-with-variables/src/Exceptions/SeedException.php b/seed/php-sdk/oauth-client-credentials-with-variables/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/oauth-client-credentials-with-variables/src/Exceptions/SeedException.php +++ b/seed/php-sdk/oauth-client-credentials-with-variables/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/oauth-client-credentials-with-variables/src/Nested/Api/ApiClient.php b/seed/php-sdk/oauth-client-credentials-with-variables/src/Nested/Api/ApiClient.php index 55aa05a36ce7..ce6b4aec2e42 100644 --- a/seed/php-sdk/oauth-client-credentials-with-variables/src/Nested/Api/ApiClient.php +++ b/seed/php-sdk/oauth-client-credentials-with-variables/src/Nested/Api/ApiClient.php @@ -11,7 +11,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class ApiClient +class ApiClient { /** * @var array{ @@ -39,11 +39,10 @@ class ApiClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -60,7 +59,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getSomething(?array $options = null): void { + public function getSomething(?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -72,12 +72,12 @@ public function getSomething(?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/oauth-client-credentials-with-variables/src/Nested/NestedClient.php b/seed/php-sdk/oauth-client-credentials-with-variables/src/Nested/NestedClient.php index 7857c019672d..26f17dd0c9ac 100644 --- a/seed/php-sdk/oauth-client-credentials-with-variables/src/Nested/NestedClient.php +++ b/seed/php-sdk/oauth-client-credentials-with-variables/src/Nested/NestedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class NestedClient +class NestedClient { /** * @var ApiClient $api @@ -39,11 +39,10 @@ class NestedClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; $this->api = new ApiClient($this->client, $this->options); diff --git a/seed/php-sdk/oauth-client-credentials-with-variables/src/NestedNoAuth/Api/ApiClient.php b/seed/php-sdk/oauth-client-credentials-with-variables/src/NestedNoAuth/Api/ApiClient.php index 6141523e925e..2bd84c067499 100644 --- a/seed/php-sdk/oauth-client-credentials-with-variables/src/NestedNoAuth/Api/ApiClient.php +++ b/seed/php-sdk/oauth-client-credentials-with-variables/src/NestedNoAuth/Api/ApiClient.php @@ -11,7 +11,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class ApiClient +class ApiClient { /** * @var array{ @@ -39,11 +39,10 @@ class ApiClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -60,7 +59,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getSomething(?array $options = null): void { + public function getSomething(?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -72,12 +72,12 @@ public function getSomething(?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/oauth-client-credentials-with-variables/src/NestedNoAuth/NestedNoAuthClient.php b/seed/php-sdk/oauth-client-credentials-with-variables/src/NestedNoAuth/NestedNoAuthClient.php index c9f5659a49b0..f7c33619f24c 100644 --- a/seed/php-sdk/oauth-client-credentials-with-variables/src/NestedNoAuth/NestedNoAuthClient.php +++ b/seed/php-sdk/oauth-client-credentials-with-variables/src/NestedNoAuth/NestedNoAuthClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class NestedNoAuthClient +class NestedNoAuthClient { /** * @var ApiClient $api @@ -39,11 +39,10 @@ class NestedNoAuthClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; $this->api = new ApiClient($this->client, $this->options); diff --git a/seed/php-sdk/oauth-client-credentials-with-variables/src/SeedClient.php b/seed/php-sdk/oauth-client-credentials-with-variables/src/SeedClient.php index fb16c26edc06..3112ace8af69 100644 --- a/seed/php-sdk/oauth-client-credentials-with-variables/src/SeedClient.php +++ b/seed/php-sdk/oauth-client-credentials-with-variables/src/SeedClient.php @@ -11,7 +11,7 @@ use Seed\Core\Client\RawClient; use Seed\Core\OAuthTokenProvider; -class SeedClient +class SeedClient { /** * @var AuthClient $auth @@ -54,6 +54,11 @@ class SeedClient */ private RawClient $client; + /** + * @var OAuthTokenProvider $oauthTokenProvider + */ + private OAuthTokenProvider $oauthTokenProvider; + /** * @param ?string $clientId The client ID for OAuth authentication. * @param ?string $clientSecret The client secret for OAuth authentication. @@ -69,32 +74,32 @@ public function __construct( ?string $clientId = null, ?string $clientSecret = null, ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $authRawClient = new RawClient(['headers' => []]); $authClient = new AuthClient($authRawClient); - $oauthTokenProvider = new OAuthTokenProvider($clientId ?? '', $clientSecret ?? '', $authClient); - $token = $oauthTokenProvider->getToken(); - - $defaultHeaders['Authorization'] = "Bearer $token"; + $this->oauthTokenProvider = new OAuthTokenProvider($clientId ?? '', $clientSecret ?? '', $authClient); + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + + $this->options['getAuthHeaders'] = fn () => + ['Authorization' => "Bearer " . $this->oauthTokenProvider->getToken()]; + $this->client = new RawClient( options: $this->options, ); - + $this->auth = new AuthClient($this->client, $this->options); $this->nestedNoAuth = new NestedNoAuthClient($this->client, $this->options); $this->nested = new NestedClient($this->client, $this->options); diff --git a/seed/php-sdk/oauth-client-credentials-with-variables/src/Service/ServiceClient.php b/seed/php-sdk/oauth-client-credentials-with-variables/src/Service/ServiceClient.php index a0d35d44ae63..06e754346759 100644 --- a/seed/php-sdk/oauth-client-credentials-with-variables/src/Service/ServiceClient.php +++ b/seed/php-sdk/oauth-client-credentials-with-variables/src/Service/ServiceClient.php @@ -11,7 +11,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class ServiceClient +class ServiceClient { /** * @var array{ @@ -39,11 +39,10 @@ class ServiceClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -61,7 +60,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function post(string $endpointParam, ?array $options = null): void { + public function post(string $endpointParam, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -73,12 +73,12 @@ public function post(string $endpointParam, ?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/oauth-client-credentials-with-variables/src/Simple/SimpleClient.php b/seed/php-sdk/oauth-client-credentials-with-variables/src/Simple/SimpleClient.php index 50b6b54f52c0..7a8df43cf28d 100644 --- a/seed/php-sdk/oauth-client-credentials-with-variables/src/Simple/SimpleClient.php +++ b/seed/php-sdk/oauth-client-credentials-with-variables/src/Simple/SimpleClient.php @@ -11,7 +11,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class SimpleClient +class SimpleClient { /** * @var array{ @@ -39,11 +39,10 @@ class SimpleClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -60,7 +59,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getSomething(?array $options = null): void { + public function getSomething(?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -72,12 +72,12 @@ public function getSomething(?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/oauth-client-credentials-with-variables/src/Utils/File.php b/seed/php-sdk/oauth-client-credentials-with-variables/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/oauth-client-credentials-with-variables/src/Utils/File.php +++ b/seed/php-sdk/oauth-client-credentials-with-variables/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-with-variables/tests/Core/Client/RawClientTest.php b/seed/php-sdk/oauth-client-credentials-with-variables/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/oauth-client-credentials-with-variables/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/oauth-client-credentials-with-variables/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/oauth-client-credentials-with-variables/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/oauth-client-credentials-with-variables/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/oauth-client-credentials-with-variables/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/oauth-client-credentials-with-variables/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-with-variables/tests/Core/Json/EnumTest.php b/seed/php-sdk/oauth-client-credentials-with-variables/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/oauth-client-credentials-with-variables/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/oauth-client-credentials-with-variables/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-with-variables/tests/Core/Json/TraitTest.php b/seed/php-sdk/oauth-client-credentials-with-variables/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/oauth-client-credentials-with-variables/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/oauth-client-credentials-with-variables/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/oauth-client-credentials-with-variables/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/oauth-client-credentials-with-variables/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/oauth-client-credentials-with-variables/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/oauth-client-credentials-with-variables/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/object/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/object/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/object/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/object/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/object/src/Core/Client/RawClient.php b/seed/php-sdk/object/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/object/src/Core/Client/RawClient.php +++ b/seed/php-sdk/object/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/object/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/object/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/object/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/object/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/object/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/object/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/object/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/object/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/object/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/object/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/object/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/object/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/object/src/Core/Types/ArrayType.php b/seed/php-sdk/object/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/object/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/object/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/object/src/Core/Types/Constant.php b/seed/php-sdk/object/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/object/src/Core/Types/Constant.php +++ b/seed/php-sdk/object/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/object/src/Core/Types/Union.php b/seed/php-sdk/object/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/object/src/Core/Types/Union.php +++ b/seed/php-sdk/object/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/object/src/Exceptions/SeedApiException.php b/seed/php-sdk/object/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/object/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/object/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/object/src/Exceptions/SeedException.php b/seed/php-sdk/object/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/object/src/Exceptions/SeedException.php +++ b/seed/php-sdk/object/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/object/src/SeedClient.php b/seed/php-sdk/object/src/SeedClient.php index efe6e7438b31..37a1c4dd76aa 100644 --- a/seed/php-sdk/object/src/SeedClient.php +++ b/seed/php-sdk/object/src/SeedClient.php @@ -2,7 +2,6 @@ namespace Seed; -class SeedClient +class SeedClient { - } diff --git a/seed/php-sdk/object/src/Types/Name.php b/seed/php-sdk/object/src/Types/Name.php index 9a063a4759fc..148099205c3e 100644 --- a/seed/php-sdk/object/src/Types/Name.php +++ b/seed/php-sdk/object/src/Types/Name.php @@ -27,15 +27,16 @@ class Name extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->value = $values['value']; + ) { + $this->id = $values['id']; + $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/object/src/Types/Type.php b/seed/php-sdk/object/src/Types/Type.php index d283dcbd6526..916f3336324b 100644 --- a/seed/php-sdk/object/src/Types/Type.php +++ b/seed/php-sdk/object/src/Types/Type.php @@ -195,15 +195,39 @@ class Type extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->one = $values['one'];$this->two = $values['two'];$this->three = $values['three'];$this->four = $values['four'];$this->five = $values['five'];$this->six = $values['six'];$this->seven = $values['seven'];$this->eight = $values['eight'];$this->nine = $values['nine'];$this->ten = $values['ten'];$this->eleven = $values['eleven'];$this->twelve = $values['twelve'];$this->thirteen = $values['thirteen'] ?? null;$this->fourteen = $values['fourteen'];$this->fifteen = $values['fifteen'];$this->sixteen = $values['sixteen'];$this->seventeen = $values['seventeen'];$this->eighteen = $values['eighteen'];$this->nineteen = $values['nineteen'];$this->twenty = $values['twenty'];$this->twentyone = $values['twentyone'];$this->twentytwo = $values['twentytwo'];$this->twentythree = $values['twentythree'];$this->twentyfour = $values['twentyfour'] ?? null;$this->twentyfive = $values['twentyfive'] ?? null; + ) { + $this->one = $values['one']; + $this->two = $values['two']; + $this->three = $values['three']; + $this->four = $values['four']; + $this->five = $values['five']; + $this->six = $values['six']; + $this->seven = $values['seven']; + $this->eight = $values['eight']; + $this->nine = $values['nine']; + $this->ten = $values['ten']; + $this->eleven = $values['eleven']; + $this->twelve = $values['twelve']; + $this->thirteen = $values['thirteen'] ?? null; + $this->fourteen = $values['fourteen']; + $this->fifteen = $values['fifteen']; + $this->sixteen = $values['sixteen']; + $this->seventeen = $values['seventeen']; + $this->eighteen = $values['eighteen']; + $this->nineteen = $values['nineteen']; + $this->twenty = $values['twenty']; + $this->twentyone = $values['twentyone']; + $this->twentytwo = $values['twentytwo']; + $this->twentythree = $values['twentythree']; + $this->twentyfour = $values['twentyfour'] ?? null; + $this->twentyfive = $values['twentyfive'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/object/src/Utils/File.php b/seed/php-sdk/object/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/object/src/Utils/File.php +++ b/seed/php-sdk/object/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/object/tests/Core/Client/RawClientTest.php b/seed/php-sdk/object/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/object/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/object/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/object/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/object/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/object/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/object/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/object/tests/Core/Json/EnumTest.php b/seed/php-sdk/object/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/object/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/object/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/object/tests/Core/Json/TraitTest.php b/seed/php-sdk/object/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/object/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/object/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/object/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/object/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/object/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/object/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/objects-with-imports/src/Commons/Metadata/Types/Metadata.php b/seed/php-sdk/objects-with-imports/src/Commons/Metadata/Types/Metadata.php index 36930e42e859..7c30e6aead21 100644 --- a/seed/php-sdk/objects-with-imports/src/Commons/Metadata/Types/Metadata.php +++ b/seed/php-sdk/objects-with-imports/src/Commons/Metadata/Types/Metadata.php @@ -28,15 +28,16 @@ class Metadata extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->data = $values['data'] ?? null; + ) { + $this->id = $values['id']; + $this->data = $values['data'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/objects-with-imports/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/objects-with-imports/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/objects-with-imports/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/objects-with-imports/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/objects-with-imports/src/Core/Client/RawClient.php b/seed/php-sdk/objects-with-imports/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/objects-with-imports/src/Core/Client/RawClient.php +++ b/seed/php-sdk/objects-with-imports/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/objects-with-imports/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/objects-with-imports/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/objects-with-imports/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/objects-with-imports/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/objects-with-imports/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/objects-with-imports/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/objects-with-imports/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/objects-with-imports/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/objects-with-imports/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/objects-with-imports/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/objects-with-imports/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/objects-with-imports/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/objects-with-imports/src/Core/Types/ArrayType.php b/seed/php-sdk/objects-with-imports/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/objects-with-imports/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/objects-with-imports/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/objects-with-imports/src/Core/Types/Constant.php b/seed/php-sdk/objects-with-imports/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/objects-with-imports/src/Core/Types/Constant.php +++ b/seed/php-sdk/objects-with-imports/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/objects-with-imports/src/Core/Types/Union.php b/seed/php-sdk/objects-with-imports/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/objects-with-imports/src/Core/Types/Union.php +++ b/seed/php-sdk/objects-with-imports/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/objects-with-imports/src/Exceptions/SeedApiException.php b/seed/php-sdk/objects-with-imports/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/objects-with-imports/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/objects-with-imports/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/objects-with-imports/src/Exceptions/SeedException.php b/seed/php-sdk/objects-with-imports/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/objects-with-imports/src/Exceptions/SeedException.php +++ b/seed/php-sdk/objects-with-imports/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/objects-with-imports/src/File/Directory/Types/Directory.php b/seed/php-sdk/objects-with-imports/src/File/Directory/Types/Directory.php index 7eb6f6bb3d26..537785b33a10 100644 --- a/seed/php-sdk/objects-with-imports/src/File/Directory/Types/Directory.php +++ b/seed/php-sdk/objects-with-imports/src/File/Directory/Types/Directory.php @@ -36,15 +36,17 @@ class Directory extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->files = $values['files'] ?? null;$this->directories = $values['directories'] ?? null; + ) { + $this->name = $values['name']; + $this->files = $values['files'] ?? null; + $this->directories = $values['directories'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/objects-with-imports/src/File/Types/File.php b/seed/php-sdk/objects-with-imports/src/File/Types/File.php index c86fcc606932..7a21cfccec51 100644 --- a/seed/php-sdk/objects-with-imports/src/File/Types/File.php +++ b/seed/php-sdk/objects-with-imports/src/File/Types/File.php @@ -34,15 +34,17 @@ class File extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->contents = $values['contents'];$this->info = $values['info']; + ) { + $this->name = $values['name']; + $this->contents = $values['contents']; + $this->info = $values['info']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/objects-with-imports/src/File/Types/FileInfo.php b/seed/php-sdk/objects-with-imports/src/File/Types/FileInfo.php index 9bd16f31b6da..1c5d05d9e0d9 100644 --- a/seed/php-sdk/objects-with-imports/src/File/Types/FileInfo.php +++ b/seed/php-sdk/objects-with-imports/src/File/Types/FileInfo.php @@ -2,8 +2,8 @@ namespace Seed\File\Types; -enum FileInfo - : string { +enum FileInfo: string +{ case Regular = "REGULAR"; case Directory = "DIRECTORY"; } diff --git a/seed/php-sdk/objects-with-imports/src/SeedClient.php b/seed/php-sdk/objects-with-imports/src/SeedClient.php index efe6e7438b31..37a1c4dd76aa 100644 --- a/seed/php-sdk/objects-with-imports/src/SeedClient.php +++ b/seed/php-sdk/objects-with-imports/src/SeedClient.php @@ -2,7 +2,6 @@ namespace Seed; -class SeedClient +class SeedClient { - } diff --git a/seed/php-sdk/objects-with-imports/src/Types/Node.php b/seed/php-sdk/objects-with-imports/src/Types/Node.php index 37c26874bd8d..ecc01726bdd0 100644 --- a/seed/php-sdk/objects-with-imports/src/Types/Node.php +++ b/seed/php-sdk/objects-with-imports/src/Types/Node.php @@ -35,15 +35,17 @@ class Node extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->label = $values['label'] ?? null;$this->metadata = $values['metadata'] ?? null; + ) { + $this->id = $values['id']; + $this->label = $values['label'] ?? null; + $this->metadata = $values['metadata'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/objects-with-imports/src/Types/Tree.php b/seed/php-sdk/objects-with-imports/src/Types/Tree.php index 8f56631ed813..7a7374066325 100644 --- a/seed/php-sdk/objects-with-imports/src/Types/Tree.php +++ b/seed/php-sdk/objects-with-imports/src/Types/Tree.php @@ -21,15 +21,15 @@ class Tree extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->nodes = $values['nodes'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/objects-with-imports/src/Utils/File.php b/seed/php-sdk/objects-with-imports/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/objects-with-imports/src/Utils/File.php +++ b/seed/php-sdk/objects-with-imports/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/objects-with-imports/tests/Core/Client/RawClientTest.php b/seed/php-sdk/objects-with-imports/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/objects-with-imports/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/objects-with-imports/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/objects-with-imports/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/objects-with-imports/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/objects-with-imports/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/objects-with-imports/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/objects-with-imports/tests/Core/Json/EnumTest.php b/seed/php-sdk/objects-with-imports/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/objects-with-imports/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/objects-with-imports/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/objects-with-imports/tests/Core/Json/TraitTest.php b/seed/php-sdk/objects-with-imports/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/objects-with-imports/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/objects-with-imports/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/objects-with-imports/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/objects-with-imports/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/objects-with-imports/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/objects-with-imports/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/optional/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/optional/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/optional/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/optional/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/optional/src/Core/Client/RawClient.php b/seed/php-sdk/optional/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/optional/src/Core/Client/RawClient.php +++ b/seed/php-sdk/optional/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/optional/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/optional/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/optional/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/optional/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/optional/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/optional/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/optional/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/optional/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/optional/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/optional/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/optional/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/optional/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/optional/src/Core/Types/ArrayType.php b/seed/php-sdk/optional/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/optional/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/optional/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/optional/src/Core/Types/Constant.php b/seed/php-sdk/optional/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/optional/src/Core/Types/Constant.php +++ b/seed/php-sdk/optional/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/optional/src/Core/Types/Union.php b/seed/php-sdk/optional/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/optional/src/Core/Types/Union.php +++ b/seed/php-sdk/optional/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/optional/src/Exceptions/SeedApiException.php b/seed/php-sdk/optional/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/optional/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/optional/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/optional/src/Exceptions/SeedException.php b/seed/php-sdk/optional/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/optional/src/Exceptions/SeedException.php +++ b/seed/php-sdk/optional/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/optional/src/Optional/OptionalClient.php b/seed/php-sdk/optional/src/Optional/OptionalClient.php index f15737517a2a..414cf1a2072c 100644 --- a/seed/php-sdk/optional/src/Optional/OptionalClient.php +++ b/seed/php-sdk/optional/src/Optional/OptionalClient.php @@ -17,7 +17,7 @@ use Seed\Optional\Types\DeployParams; use Seed\Optional\Types\DeployResponse; -class OptionalClient +class OptionalClient { /** * @var array{ @@ -45,11 +45,10 @@ class OptionalClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -68,7 +67,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function sendOptionalBody(?array $request = null, ?array $options = null): string { + public function sendOptionalBody(?array $request = null, ?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -81,15 +81,15 @@ public function sendOptionalBody(?array $request = null, ?array $options = null) $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -121,7 +121,8 @@ public function sendOptionalBody(?array $request = null, ?array $options = null) * @throws SeedException * @throws SeedApiException */ - public function sendOptionalTypedBody(?SendOptionalBodyRequest $request = null, ?array $options = null): string { + public function sendOptionalTypedBody(?SendOptionalBodyRequest $request = null, ?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -134,15 +135,15 @@ public function sendOptionalTypedBody(?SendOptionalBodyRequest $request = null, $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -179,7 +180,8 @@ public function sendOptionalTypedBody(?SendOptionalBodyRequest $request = null, * @throws SeedException * @throws SeedApiException */ - public function sendOptionalNullableWithAllOptionalProperties(string $actionId, string $id, ?DeployParams $request = null, ?array $options = null): DeployResponse { + public function sendOptionalNullableWithAllOptionalProperties(string $actionId, string $id, ?DeployParams $request = null, ?array $options = null): DeployResponse + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -192,15 +194,15 @@ public function sendOptionalNullableWithAllOptionalProperties(string $actionId, $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return DeployResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/optional/src/Optional/Types/DeployParams.php b/seed/php-sdk/optional/src/Optional/Types/DeployParams.php index 929c58b2c575..71e89b307275 100644 --- a/seed/php-sdk/optional/src/Optional/Types/DeployParams.php +++ b/seed/php-sdk/optional/src/Optional/Types/DeployParams.php @@ -20,15 +20,15 @@ class DeployParams extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->updateDraft = $values['updateDraft'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/optional/src/Optional/Types/DeployResponse.php b/seed/php-sdk/optional/src/Optional/Types/DeployResponse.php index 87f60118fd79..172231ef3c75 100644 --- a/seed/php-sdk/optional/src/Optional/Types/DeployResponse.php +++ b/seed/php-sdk/optional/src/Optional/Types/DeployResponse.php @@ -20,15 +20,15 @@ class DeployResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->success = $values['success']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/optional/src/Optional/Types/SendOptionalBodyRequest.php b/seed/php-sdk/optional/src/Optional/Types/SendOptionalBodyRequest.php index 4d88f551f2c8..09bbd10d3496 100644 --- a/seed/php-sdk/optional/src/Optional/Types/SendOptionalBodyRequest.php +++ b/seed/php-sdk/optional/src/Optional/Types/SendOptionalBodyRequest.php @@ -20,15 +20,15 @@ class SendOptionalBodyRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->message = $values['message']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/optional/src/SeedClient.php b/seed/php-sdk/optional/src/SeedClient.php index 895bbf5c02fb..3c8c251730a6 100644 --- a/seed/php-sdk/optional/src/SeedClient.php +++ b/seed/php-sdk/optional/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var OptionalClient $optional @@ -40,26 +40,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->optional = new OptionalClient($this->client, $this->options); } } diff --git a/seed/php-sdk/optional/src/Utils/File.php b/seed/php-sdk/optional/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/optional/src/Utils/File.php +++ b/seed/php-sdk/optional/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/optional/tests/Core/Client/RawClientTest.php b/seed/php-sdk/optional/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/optional/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/optional/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/optional/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/optional/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/optional/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/optional/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/optional/tests/Core/Json/EnumTest.php b/seed/php-sdk/optional/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/optional/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/optional/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/optional/tests/Core/Json/TraitTest.php b/seed/php-sdk/optional/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/optional/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/optional/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/optional/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/optional/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/optional/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/optional/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/package-yml/no-custom-config/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/package-yml/no-custom-config/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/package-yml/no-custom-config/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/package-yml/no-custom-config/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/package-yml/no-custom-config/src/Core/Client/RawClient.php b/seed/php-sdk/package-yml/no-custom-config/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/package-yml/no-custom-config/src/Core/Client/RawClient.php +++ b/seed/php-sdk/package-yml/no-custom-config/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/package-yml/no-custom-config/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/package-yml/no-custom-config/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/package-yml/no-custom-config/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/package-yml/no-custom-config/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/package-yml/no-custom-config/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/package-yml/no-custom-config/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/package-yml/no-custom-config/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/package-yml/no-custom-config/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/package-yml/no-custom-config/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/package-yml/no-custom-config/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/package-yml/no-custom-config/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/package-yml/no-custom-config/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/package-yml/no-custom-config/src/Core/Types/ArrayType.php b/seed/php-sdk/package-yml/no-custom-config/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/package-yml/no-custom-config/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/package-yml/no-custom-config/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/package-yml/no-custom-config/src/Core/Types/Constant.php b/seed/php-sdk/package-yml/no-custom-config/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/package-yml/no-custom-config/src/Core/Types/Constant.php +++ b/seed/php-sdk/package-yml/no-custom-config/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/package-yml/no-custom-config/src/Core/Types/Union.php b/seed/php-sdk/package-yml/no-custom-config/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/package-yml/no-custom-config/src/Core/Types/Union.php +++ b/seed/php-sdk/package-yml/no-custom-config/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/package-yml/no-custom-config/src/Exceptions/SeedApiException.php b/seed/php-sdk/package-yml/no-custom-config/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/package-yml/no-custom-config/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/package-yml/no-custom-config/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/package-yml/no-custom-config/src/Exceptions/SeedException.php b/seed/php-sdk/package-yml/no-custom-config/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/package-yml/no-custom-config/src/Exceptions/SeedException.php +++ b/seed/php-sdk/package-yml/no-custom-config/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/package-yml/no-custom-config/src/SeedClient.php b/seed/php-sdk/package-yml/no-custom-config/src/SeedClient.php index e30477c2a848..682fc9367765 100644 --- a/seed/php-sdk/package-yml/no-custom-config/src/SeedClient.php +++ b/seed/php-sdk/package-yml/no-custom-config/src/SeedClient.php @@ -15,7 +15,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class SeedClient +class SeedClient { /** * @var ServiceClient $service @@ -49,26 +49,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->service = new ServiceClient($this->client, $this->options); } @@ -87,7 +86,8 @@ public function __construct( * @throws SeedException * @throws SeedApiException */ - public function echo_(string $id, EchoRequest $request, ?array $options = null): string { + public function echo_(string $id, EchoRequest $request, ?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -100,15 +100,15 @@ public function echo_(string $id, EchoRequest $request, ?array $options = null): $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/package-yml/no-custom-config/src/Service/ServiceClient.php b/seed/php-sdk/package-yml/no-custom-config/src/Service/ServiceClient.php index 2c2ea0254fe2..a31181156d32 100644 --- a/seed/php-sdk/package-yml/no-custom-config/src/Service/ServiceClient.php +++ b/seed/php-sdk/package-yml/no-custom-config/src/Service/ServiceClient.php @@ -11,7 +11,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class ServiceClient +class ServiceClient { /** * @var array{ @@ -39,11 +39,10 @@ class ServiceClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -62,7 +61,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function nop(string $id, string $nestedId, ?array $options = null): void { + public function nop(string $id, string $nestedId, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -74,12 +74,12 @@ public function nop(string $id, string $nestedId, ?array $options = null): void $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/package-yml/no-custom-config/src/Types/EchoRequest.php b/seed/php-sdk/package-yml/no-custom-config/src/Types/EchoRequest.php index ea33b217ce21..5aff0ee3d39d 100644 --- a/seed/php-sdk/package-yml/no-custom-config/src/Types/EchoRequest.php +++ b/seed/php-sdk/package-yml/no-custom-config/src/Types/EchoRequest.php @@ -27,15 +27,16 @@ class EchoRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->size = $values['size']; + ) { + $this->name = $values['name']; + $this->size = $values['size']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/package-yml/no-custom-config/src/Utils/File.php b/seed/php-sdk/package-yml/no-custom-config/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/package-yml/no-custom-config/src/Utils/File.php +++ b/seed/php-sdk/package-yml/no-custom-config/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/package-yml/no-custom-config/tests/Core/Client/RawClientTest.php b/seed/php-sdk/package-yml/no-custom-config/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/package-yml/no-custom-config/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/package-yml/no-custom-config/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/package-yml/no-custom-config/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/package-yml/no-custom-config/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/package-yml/no-custom-config/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/package-yml/no-custom-config/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/package-yml/no-custom-config/tests/Core/Json/EnumTest.php b/seed/php-sdk/package-yml/no-custom-config/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/package-yml/no-custom-config/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/package-yml/no-custom-config/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/package-yml/no-custom-config/tests/Core/Json/TraitTest.php b/seed/php-sdk/package-yml/no-custom-config/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/package-yml/no-custom-config/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/package-yml/no-custom-config/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/package-yml/no-custom-config/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/package-yml/no-custom-config/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/package-yml/no-custom-config/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/package-yml/no-custom-config/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/package-yml/private/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/package-yml/private/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/package-yml/private/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/package-yml/private/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/package-yml/private/src/Core/Client/RawClient.php b/seed/php-sdk/package-yml/private/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/package-yml/private/src/Core/Client/RawClient.php +++ b/seed/php-sdk/package-yml/private/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/package-yml/private/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/package-yml/private/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/package-yml/private/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/package-yml/private/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/package-yml/private/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/package-yml/private/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/package-yml/private/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/package-yml/private/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/package-yml/private/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/package-yml/private/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/package-yml/private/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/package-yml/private/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/package-yml/private/src/Core/Types/ArrayType.php b/seed/php-sdk/package-yml/private/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/package-yml/private/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/package-yml/private/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/package-yml/private/src/Core/Types/Constant.php b/seed/php-sdk/package-yml/private/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/package-yml/private/src/Core/Types/Constant.php +++ b/seed/php-sdk/package-yml/private/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/package-yml/private/src/Core/Types/Union.php b/seed/php-sdk/package-yml/private/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/package-yml/private/src/Core/Types/Union.php +++ b/seed/php-sdk/package-yml/private/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/package-yml/private/src/Exceptions/SeedApiException.php b/seed/php-sdk/package-yml/private/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/package-yml/private/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/package-yml/private/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/package-yml/private/src/Exceptions/SeedException.php b/seed/php-sdk/package-yml/private/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/package-yml/private/src/Exceptions/SeedException.php +++ b/seed/php-sdk/package-yml/private/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/package-yml/private/src/SeedClient.php b/seed/php-sdk/package-yml/private/src/SeedClient.php index e30477c2a848..682fc9367765 100644 --- a/seed/php-sdk/package-yml/private/src/SeedClient.php +++ b/seed/php-sdk/package-yml/private/src/SeedClient.php @@ -15,7 +15,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class SeedClient +class SeedClient { /** * @var ServiceClient $service @@ -49,26 +49,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->service = new ServiceClient($this->client, $this->options); } @@ -87,7 +86,8 @@ public function __construct( * @throws SeedException * @throws SeedApiException */ - public function echo_(string $id, EchoRequest $request, ?array $options = null): string { + public function echo_(string $id, EchoRequest $request, ?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -100,15 +100,15 @@ public function echo_(string $id, EchoRequest $request, ?array $options = null): $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/package-yml/private/src/Service/ServiceClient.php b/seed/php-sdk/package-yml/private/src/Service/ServiceClient.php index 2c2ea0254fe2..a31181156d32 100644 --- a/seed/php-sdk/package-yml/private/src/Service/ServiceClient.php +++ b/seed/php-sdk/package-yml/private/src/Service/ServiceClient.php @@ -11,7 +11,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class ServiceClient +class ServiceClient { /** * @var array{ @@ -39,11 +39,10 @@ class ServiceClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -62,7 +61,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function nop(string $id, string $nestedId, ?array $options = null): void { + public function nop(string $id, string $nestedId, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -74,12 +74,12 @@ public function nop(string $id, string $nestedId, ?array $options = null): void $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/package-yml/private/src/Types/EchoRequest.php b/seed/php-sdk/package-yml/private/src/Types/EchoRequest.php index ca6cdaeeff93..8408c9c29c6f 100644 --- a/seed/php-sdk/package-yml/private/src/Types/EchoRequest.php +++ b/seed/php-sdk/package-yml/private/src/Types/EchoRequest.php @@ -27,39 +27,50 @@ class EchoRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->size = $values['size']; + ) { + $this->name = $values['name']; + $this->size = $values['size']; } /** * @return string */ - public function getName(): string { - return $this->name;} + public function getName(): string + { + return $this->name; + } /** * @param string $value */ - public function setName(string $value): self { - $this->name = $value;return $this;} + public function setName(string $value): self + { + $this->name = $value; + return $this; + } /** * @return int */ - public function getSize(): int { - return $this->size;} + public function getSize(): int + { + return $this->size; + } /** * @param int $value */ - public function setSize(int $value): self { - $this->size = $value;return $this;} + public function setSize(int $value): self + { + $this->size = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/package-yml/private/src/Utils/File.php b/seed/php-sdk/package-yml/private/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/package-yml/private/src/Utils/File.php +++ b/seed/php-sdk/package-yml/private/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/package-yml/private/tests/Core/Client/RawClientTest.php b/seed/php-sdk/package-yml/private/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/package-yml/private/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/package-yml/private/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/package-yml/private/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/package-yml/private/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/package-yml/private/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/package-yml/private/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/package-yml/private/tests/Core/Json/EnumTest.php b/seed/php-sdk/package-yml/private/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/package-yml/private/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/package-yml/private/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/package-yml/private/tests/Core/Json/TraitTest.php b/seed/php-sdk/package-yml/private/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/package-yml/private/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/package-yml/private/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/package-yml/private/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/package-yml/private/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/package-yml/private/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/package-yml/private/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination-custom/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/pagination-custom/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/pagination-custom/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/pagination-custom/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination-custom/src/Core/Client/RawClient.php b/seed/php-sdk/pagination-custom/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/pagination-custom/src/Core/Client/RawClient.php +++ b/seed/php-sdk/pagination-custom/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/pagination-custom/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/pagination-custom/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/pagination-custom/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/pagination-custom/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/pagination-custom/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/pagination-custom/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/pagination-custom/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/pagination-custom/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination-custom/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/pagination-custom/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/pagination-custom/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/pagination-custom/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination-custom/src/Core/Pagination/Page.php b/seed/php-sdk/pagination-custom/src/Core/Pagination/Page.php index 9d7fe5e5f243..f38d491e667b 100644 --- a/seed/php-sdk/pagination-custom/src/Core/Pagination/Page.php +++ b/seed/php-sdk/pagination-custom/src/Core/Pagination/Page.php @@ -46,4 +46,4 @@ public function getIterator(): Generator { return yield from $this->getItems(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination-custom/src/Core/Pagination/Pager.php b/seed/php-sdk/pagination-custom/src/Core/Pagination/Pager.php index 351110574462..7897a30b307b 100644 --- a/seed/php-sdk/pagination-custom/src/Core/Pagination/Pager.php +++ b/seed/php-sdk/pagination-custom/src/Core/Pagination/Pager.php @@ -33,4 +33,4 @@ public function getIterator(): Generator yield from $page->getItems(); } } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination-custom/src/Core/Pagination/PaginationHelper.php b/seed/php-sdk/pagination-custom/src/Core/Pagination/PaginationHelper.php index b1be91f12b98..bc0fd78fe428 100644 --- a/seed/php-sdk/pagination-custom/src/Core/Pagination/PaginationHelper.php +++ b/seed/php-sdk/pagination-custom/src/Core/Pagination/PaginationHelper.php @@ -100,8 +100,7 @@ private static function createInstanceWithDefaults(string $className) $type = $property->getType(); $value = self::getDefaultValueForType($type); // if something is nullable, don't explicitly pass the null value as a parameter - if($value === null) - { + if ($value === null) { continue; } diff --git a/seed/php-sdk/pagination-custom/src/Core/Types/ArrayType.php b/seed/php-sdk/pagination-custom/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/pagination-custom/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/pagination-custom/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/pagination-custom/src/Core/Types/Constant.php b/seed/php-sdk/pagination-custom/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/pagination-custom/src/Core/Types/Constant.php +++ b/seed/php-sdk/pagination-custom/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination-custom/src/Core/Types/Union.php b/seed/php-sdk/pagination-custom/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/pagination-custom/src/Core/Types/Union.php +++ b/seed/php-sdk/pagination-custom/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination-custom/src/Exceptions/SeedApiException.php b/seed/php-sdk/pagination-custom/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/pagination-custom/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/pagination-custom/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/pagination-custom/src/Exceptions/SeedException.php b/seed/php-sdk/pagination-custom/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/pagination-custom/src/Exceptions/SeedException.php +++ b/seed/php-sdk/pagination-custom/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/pagination-custom/src/SeedClient.php b/seed/php-sdk/pagination-custom/src/SeedClient.php index 61a13a56f996..fcc432652cd5 100644 --- a/seed/php-sdk/pagination-custom/src/SeedClient.php +++ b/seed/php-sdk/pagination-custom/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var UsersClient $users @@ -42,29 +42,28 @@ class SeedClient public function __construct( ?string $token = null, ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - if ($token != null){ + if ($token != null) { $defaultHeaders['Authorization'] = "Bearer $token"; } - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->users = new UsersClient($this->client, $this->options); } } diff --git a/seed/php-sdk/pagination-custom/src/Types/UsernameCursor.php b/seed/php-sdk/pagination-custom/src/Types/UsernameCursor.php index f6fd712dff06..e64d44fa92c9 100644 --- a/seed/php-sdk/pagination-custom/src/Types/UsernameCursor.php +++ b/seed/php-sdk/pagination-custom/src/Types/UsernameCursor.php @@ -20,15 +20,15 @@ class UsernameCursor extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->cursor = $values['cursor']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination-custom/src/Types/UsernamePage.php b/seed/php-sdk/pagination-custom/src/Types/UsernamePage.php index e9520533e182..c21651929c07 100644 --- a/seed/php-sdk/pagination-custom/src/Types/UsernamePage.php +++ b/seed/php-sdk/pagination-custom/src/Types/UsernamePage.php @@ -28,15 +28,16 @@ class UsernamePage extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->after = $values['after'] ?? null;$this->data = $values['data']; + ) { + $this->after = $values['after'] ?? null; + $this->data = $values['data']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination-custom/src/Users/Requests/ListUsernamesRequestCustom.php b/seed/php-sdk/pagination-custom/src/Users/Requests/ListUsernamesRequestCustom.php index 9d0ded6ac7f1..eb47ea68e808 100644 --- a/seed/php-sdk/pagination-custom/src/Users/Requests/ListUsernamesRequestCustom.php +++ b/seed/php-sdk/pagination-custom/src/Users/Requests/ListUsernamesRequestCustom.php @@ -21,8 +21,7 @@ class ListUsernamesRequestCustom extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->startingAfter = $values['startingAfter'] ?? null; } } diff --git a/seed/php-sdk/pagination-custom/src/Users/UsersClient.php b/seed/php-sdk/pagination-custom/src/Users/UsersClient.php index cc67ae32fba1..f1b0c6f1d93e 100644 --- a/seed/php-sdk/pagination-custom/src/Users/UsersClient.php +++ b/seed/php-sdk/pagination-custom/src/Users/UsersClient.php @@ -16,7 +16,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class UsersClient +class UsersClient { /** * @var array{ @@ -44,11 +44,10 @@ class UsersClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -65,7 +64,8 @@ function __construct( * } $options * @return Pager */ - public function listUsernamesCustom(ListUsernamesRequestCustom $request = new ListUsernamesRequestCustom(), ?array $options = null): Pager { + public function listUsernamesCustom(ListUsernamesRequestCustom $request = new ListUsernamesRequestCustom(), ?array $options = null): Pager + { $response = $this->_listUsernamesCustom($request, $options); return new CustomPager(response: $response, client: $this); } @@ -84,10 +84,11 @@ public function listUsernamesCustom(ListUsernamesRequestCustom $request = new Li * @throws SeedException * @throws SeedApiException */ - private function _listUsernamesCustom(ListUsernamesRequestCustom $request = new ListUsernamesRequestCustom(), ?array $options = null): UsernameCursor { + private function _listUsernamesCustom(ListUsernamesRequestCustom $request = new ListUsernamesRequestCustom(), ?array $options = null): UsernameCursor + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->startingAfter != null){ + if ($request->startingAfter != null) { $query['starting_after'] = $request->startingAfter; } try { @@ -101,15 +102,15 @@ private function _listUsernamesCustom(ListUsernamesRequestCustom $request = new $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return UsernameCursor::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/pagination-custom/src/Utils/File.php b/seed/php-sdk/pagination-custom/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/pagination-custom/src/Utils/File.php +++ b/seed/php-sdk/pagination-custom/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination-custom/tests/Core/Client/RawClientTest.php b/seed/php-sdk/pagination-custom/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/pagination-custom/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/pagination-custom/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/pagination-custom/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/pagination-custom/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/pagination-custom/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/pagination-custom/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination-custom/tests/Core/Json/EnumTest.php b/seed/php-sdk/pagination-custom/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/pagination-custom/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/pagination-custom/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination-custom/tests/Core/Json/TraitTest.php b/seed/php-sdk/pagination-custom/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/pagination-custom/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/pagination-custom/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination-custom/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/pagination-custom/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/pagination-custom/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/pagination-custom/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination-custom/tests/Core/Pagination/CursorPagerTest/CursorPagerTest.php b/seed/php-sdk/pagination-custom/tests/Core/Pagination/CursorPagerTest/CursorPagerTest.php index fd9eda9faf6c..8c5ac6b6fd59 100644 --- a/seed/php-sdk/pagination-custom/tests/Core/Pagination/CursorPagerTest/CursorPagerTest.php +++ b/seed/php-sdk/pagination-custom/tests/Core/Pagination/CursorPagerTest/CursorPagerTest.php @@ -93,8 +93,8 @@ function (Request $request, ?string $cursor) { $request->cursor = $cursor; $this->cursorCopy = $cursor; }, - fn(Response $response) => $response->next->cursor ?? null, - fn(Response $response) => $response->items ?? [] + fn (Response $response) => $response->next->cursor ?? null, + fn (Response $response) => $response->items ?? [] ); } @@ -129,4 +129,4 @@ private function assertPager(Pager $pager): void $pages->next(); $this->assertNull($pages->current()); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination-custom/tests/Core/Pagination/DeepSetAccessorsTest.php b/seed/php-sdk/pagination-custom/tests/Core/Pagination/DeepSetAccessorsTest.php index a21e7201abb9..73ed5401292f 100644 --- a/seed/php-sdk/pagination-custom/tests/Core/Pagination/DeepSetAccessorsTest.php +++ b/seed/php-sdk/pagination-custom/tests/Core/Pagination/DeepSetAccessorsTest.php @@ -98,4 +98,4 @@ public function testSetNestedProperty(): void $this->assertEquals('testValue', $object->getLevel1()?->getLevel2()?->getLevel3()); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination-custom/tests/Core/Pagination/GeneratorPagerTest/GeneratorPagerTest.php b/seed/php-sdk/pagination-custom/tests/Core/Pagination/GeneratorPagerTest/GeneratorPagerTest.php index 8ada5c5edeb2..47c12833b1a7 100644 --- a/seed/php-sdk/pagination-custom/tests/Core/Pagination/GeneratorPagerTest/GeneratorPagerTest.php +++ b/seed/php-sdk/pagination-custom/tests/Core/Pagination/GeneratorPagerTest/GeneratorPagerTest.php @@ -69,7 +69,7 @@ private function createPager(): Pager new Response(new Data([])), ]); - return new class($responses) extends Pager { + return new class ($responses) extends Pager { /** * @var ArrayIterator */ diff --git a/seed/php-sdk/pagination-custom/tests/Core/Pagination/HasNextPageOffsetPagerTest/HasNextPageOffsetPagerTest.php b/seed/php-sdk/pagination-custom/tests/Core/Pagination/HasNextPageOffsetPagerTest/HasNextPageOffsetPagerTest.php index 7e96cb22ec9c..f04cc27903c3 100644 --- a/seed/php-sdk/pagination-custom/tests/Core/Pagination/HasNextPageOffsetPagerTest/HasNextPageOffsetPagerTest.php +++ b/seed/php-sdk/pagination-custom/tests/Core/Pagination/HasNextPageOffsetPagerTest/HasNextPageOffsetPagerTest.php @@ -81,16 +81,16 @@ function (Request $request) use ($responses) { $responses->next(); return $response; }, - fn(Request $request) => $request->pagination?->page ?? 0, + fn (Request $request) => $request->pagination?->page ?? 0, function (Request $request, int $offset) { - if($request->pagination === null) { + if ($request->pagination === null) { $request->pagination = new Pagination(0); } $request->pagination->page = $offset; }, null, - fn(Response $response) => $response->data->items, - fn(Response $response) => $response->hasNext + fn (Response $response) => $response->data->items, + fn (Response $response) => $response->hasNext ); } @@ -102,9 +102,9 @@ private function assertPager(Pager $pager): void { $pages = iterator_to_array($pager->getPages()); $pageCounter = count($pages); - $itemCounter = array_reduce($pages, fn($carry, $page) => $carry + count($page->getItems()), 0); + $itemCounter = array_reduce($pages, fn ($carry, $page) => $carry + count($page->getItems()), 0); $this->assertEquals(3, $pageCounter); $this->assertEquals(5, $itemCounter); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination-custom/tests/Core/Pagination/IntOffsetPagerTest/IntOffsetPagerTest.php b/seed/php-sdk/pagination-custom/tests/Core/Pagination/IntOffsetPagerTest/IntOffsetPagerTest.php index 71d2343f78e4..226ca279d28a 100644 --- a/seed/php-sdk/pagination-custom/tests/Core/Pagination/IntOffsetPagerTest/IntOffsetPagerTest.php +++ b/seed/php-sdk/pagination-custom/tests/Core/Pagination/IntOffsetPagerTest/IntOffsetPagerTest.php @@ -79,7 +79,7 @@ function (Request $request) use ($responses) { $responses->next(); return $response; }, - fn(Request $request) => $request->pagination?->page ?? 0, + fn (Request $request) => $request->pagination?->page ?? 0, function (Request $request, int $offset) { if ($request->pagination === null) { $request->pagination = new Pagination(0); @@ -87,7 +87,7 @@ function (Request $request, int $offset) { $request->pagination->page = $offset; }, null, - fn(Response $response) => $response->data->items, + fn (Response $response) => $response->data->items, null ); } @@ -100,9 +100,9 @@ private function assertPager(Pager $pager): void { $pages = iterator_to_array($pager->getPages()); $pageCounter = count($pages); - $itemCounter = array_reduce($pages, fn($carry, $page) => $carry + count($page->getItems()), 0); + $itemCounter = array_reduce($pages, fn ($carry, $page) => $carry + count($page->getItems()), 0); $this->assertEquals(3, $pageCounter); $this->assertEquals(3, $itemCounter); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination-custom/tests/Core/Pagination/StepOffsetPagerTest/StepOffsetPagerTest.php b/seed/php-sdk/pagination-custom/tests/Core/Pagination/StepOffsetPagerTest/StepOffsetPagerTest.php index 79b0f27868b0..d44cdc364418 100644 --- a/seed/php-sdk/pagination-custom/tests/Core/Pagination/StepOffsetPagerTest/StepOffsetPagerTest.php +++ b/seed/php-sdk/pagination-custom/tests/Core/Pagination/StepOffsetPagerTest/StepOffsetPagerTest.php @@ -85,7 +85,7 @@ function (Request $request) use ($responses) { $responses->next(); return $response; }, - fn(Request $request) => $request->pagination?->itemOffset ?? 0, + fn (Request $request) => $request->pagination?->itemOffset ?? 0, function (Request $request, int $offset) { if ($request->pagination === null) { $request->pagination = new Pagination(0, 2); @@ -93,8 +93,8 @@ function (Request $request, int $offset) { $request->pagination->itemOffset = $offset; $this->paginationCopy = $request->pagination; }, - fn(Request $request) => $request->pagination?->pageSize, - fn(Response $response) => $response->data->items, + fn (Request $request) => $request->pagination?->pageSize, + fn (Response $response) => $response->data->items, null ); } @@ -128,4 +128,4 @@ private function assertPager(Pager $pager): void $pages->next(); $this->assertNull($pages->current()); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination/no-custom-config/reference.md b/seed/php-sdk/pagination/no-custom-config/reference.md index 4a001318d75f..ef4e8820de25 100644 --- a/seed/php-sdk/pagination/no-custom-config/reference.md +++ b/seed/php-sdk/pagination/no-custom-config/reference.md @@ -1440,6 +1440,50 @@ $client->users->listWithGlobalConfig( + + + + +
$client->users->listWithOptionalData($request) -> ListUsersOptionalDataPaginationResponse +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```php +$client->users->listWithOptionalData( + new ListUsersOptionalDataRequest([ + 'page' => 1, + ]), +); +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**$page:** `?int` — Defaults to first page + +
+
+
+
+ +
diff --git a/seed/php-sdk/pagination/no-custom-config/src/Complex/ComplexClient.php b/seed/php-sdk/pagination/no-custom-config/src/Complex/ComplexClient.php index 87da41a80a1b..fc519f351020 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Complex/ComplexClient.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Complex/ComplexClient.php @@ -18,7 +18,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class ComplexClient +class ComplexClient { /** * @var array{ @@ -46,11 +46,10 @@ class ComplexClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -68,11 +67,12 @@ function __construct( * } $options * @return Pager */ - public function search(string $index, SearchRequest $request, ?array $options = null): Pager { + public function search(string $index, SearchRequest $request, ?array $options = null): Pager + { return new CursorPager( request: $request, - getNextPage: fn(SearchRequest $request) => $this->_search($index, $request, $options), - setCursor: function (SearchRequest $request, ?string $cursor) { + getNextPage: fn (SearchRequest $request) => $this->_search($index, $request, $options), + setCursor: function (SearchRequest $request, ?string $cursor) { PaginationHelper::setDeep($request, ["pagination", "startingAfter"], $cursor); }, /* @phpstan-ignore-next-line */ @@ -97,7 +97,8 @@ public function search(string $index, SearchRequest $request, ?array $options = * @throws SeedException * @throws SeedApiException */ - private function _search(string $index, SearchRequest $request, ?array $options = null): PaginatedConversationResponse { + private function _search(string $index, SearchRequest $request, ?array $options = null): PaginatedConversationResponse + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -110,15 +111,15 @@ private function _search(string $index, SearchRequest $request, ?array $options $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return PaginatedConversationResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/pagination/no-custom-config/src/Complex/Types/Conversation.php b/seed/php-sdk/pagination/no-custom-config/src/Complex/Types/Conversation.php index f4c32a3a3511..dfe60061ce99 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Complex/Types/Conversation.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Complex/Types/Conversation.php @@ -20,15 +20,15 @@ class Conversation extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->foo = $values['foo']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/Complex/Types/CursorPages.php b/seed/php-sdk/pagination/no-custom-config/src/Complex/Types/CursorPages.php index 106acc24d6a8..0aef30ba08da 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Complex/Types/CursorPages.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Complex/Types/CursorPages.php @@ -48,15 +48,19 @@ class CursorPages extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->next = $values['next'] ?? null;$this->page = $values['page'] ?? null;$this->perPage = $values['perPage'] ?? null;$this->totalPages = $values['totalPages'] ?? null;$this->type = $values['type']; + ) { + $this->next = $values['next'] ?? null; + $this->page = $values['page'] ?? null; + $this->perPage = $values['perPage'] ?? null; + $this->totalPages = $values['totalPages'] ?? null; + $this->type = $values['type']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/Complex/Types/MultipleFilterSearchRequest.php b/seed/php-sdk/pagination/no-custom-config/src/Complex/Types/MultipleFilterSearchRequest.php index edc6b3892e9d..d6787c686459 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Complex/Types/MultipleFilterSearchRequest.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Complex/Types/MultipleFilterSearchRequest.php @@ -20,7 +20,7 @@ class MultipleFilterSearchRequest extends JsonSerializableType * |array * )|null $value */ - #[JsonProperty('value'), Union([MultipleFilterSearchRequest::class],[SingleFilterSearchRequest::class],'null')] + #[JsonProperty('value'), Union([MultipleFilterSearchRequest::class], [SingleFilterSearchRequest::class], 'null')] public array|null $value; /** @@ -34,15 +34,16 @@ class MultipleFilterSearchRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->operator = $values['operator'] ?? null;$this->value = $values['value'] ?? null; + ) { + $this->operator = $values['operator'] ?? null; + $this->value = $values['value'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/Complex/Types/MultipleFilterSearchRequestOperator.php b/seed/php-sdk/pagination/no-custom-config/src/Complex/Types/MultipleFilterSearchRequestOperator.php index 0b84a2b1691d..ff40bc3326f9 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Complex/Types/MultipleFilterSearchRequestOperator.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Complex/Types/MultipleFilterSearchRequestOperator.php @@ -2,8 +2,8 @@ namespace Seed\Complex\Types; -enum MultipleFilterSearchRequestOperator - : string { +enum MultipleFilterSearchRequestOperator: string +{ case And_ = "AND"; case Or_ = "OR"; } diff --git a/seed/php-sdk/pagination/no-custom-config/src/Complex/Types/PaginatedConversationResponse.php b/seed/php-sdk/pagination/no-custom-config/src/Complex/Types/PaginatedConversationResponse.php index 87bc99e06d47..cc8dc96b1a9d 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Complex/Types/PaginatedConversationResponse.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Complex/Types/PaginatedConversationResponse.php @@ -42,15 +42,18 @@ class PaginatedConversationResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->conversations = $values['conversations'];$this->pages = $values['pages'] ?? null;$this->totalCount = $values['totalCount'];$this->type = $values['type']; + ) { + $this->conversations = $values['conversations']; + $this->pages = $values['pages'] ?? null; + $this->totalCount = $values['totalCount']; + $this->type = $values['type']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/Complex/Types/SearchRequest.php b/seed/php-sdk/pagination/no-custom-config/src/Complex/Types/SearchRequest.php index e9cc6b45b183..789c30020435 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Complex/Types/SearchRequest.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Complex/Types/SearchRequest.php @@ -20,7 +20,7 @@ class SearchRequest extends JsonSerializableType * |MultipleFilterSearchRequest * ) $query */ - #[JsonProperty('query'), Union(SingleFilterSearchRequest::class,MultipleFilterSearchRequest::class)] + #[JsonProperty('query'), Union(SingleFilterSearchRequest::class, MultipleFilterSearchRequest::class)] public SingleFilterSearchRequest|MultipleFilterSearchRequest $query; /** @@ -34,15 +34,16 @@ class SearchRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->pagination = $values['pagination'] ?? null;$this->query = $values['query']; + ) { + $this->pagination = $values['pagination'] ?? null; + $this->query = $values['query']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/Complex/Types/SingleFilterSearchRequest.php b/seed/php-sdk/pagination/no-custom-config/src/Complex/Types/SingleFilterSearchRequest.php index ea77cac18ea3..c7abdd606b54 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Complex/Types/SingleFilterSearchRequest.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Complex/Types/SingleFilterSearchRequest.php @@ -34,15 +34,17 @@ class SingleFilterSearchRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->field = $values['field'] ?? null;$this->operator = $values['operator'] ?? null;$this->value = $values['value'] ?? null; + ) { + $this->field = $values['field'] ?? null; + $this->operator = $values['operator'] ?? null; + $this->value = $values['value'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/Complex/Types/SingleFilterSearchRequestOperator.php b/seed/php-sdk/pagination/no-custom-config/src/Complex/Types/SingleFilterSearchRequestOperator.php index ad18302df3f9..6ba8c6097c55 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Complex/Types/SingleFilterSearchRequestOperator.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Complex/Types/SingleFilterSearchRequestOperator.php @@ -2,8 +2,8 @@ namespace Seed\Complex\Types; -enum SingleFilterSearchRequestOperator - : string { +enum SingleFilterSearchRequestOperator: string +{ case Equals = "="; case NotEquals = "!="; case In = "IN"; diff --git a/seed/php-sdk/pagination/no-custom-config/src/Complex/Types/StartingAfterPaging.php b/seed/php-sdk/pagination/no-custom-config/src/Complex/Types/StartingAfterPaging.php index 779f0104d786..7e68e12b98c1 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Complex/Types/StartingAfterPaging.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Complex/Types/StartingAfterPaging.php @@ -27,15 +27,16 @@ class StartingAfterPaging extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->perPage = $values['perPage'];$this->startingAfter = $values['startingAfter'] ?? null; + ) { + $this->perPage = $values['perPage']; + $this->startingAfter = $values['startingAfter'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/pagination/no-custom-config/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination/no-custom-config/src/Core/Client/RawClient.php b/seed/php-sdk/pagination/no-custom-config/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Core/Client/RawClient.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/pagination/no-custom-config/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/pagination/no-custom-config/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/pagination/no-custom-config/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/pagination/no-custom-config/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination/no-custom-config/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/pagination/no-custom-config/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination/no-custom-config/src/Core/Pagination/Page.php b/seed/php-sdk/pagination/no-custom-config/src/Core/Pagination/Page.php index 9d7fe5e5f243..f38d491e667b 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Core/Pagination/Page.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Core/Pagination/Page.php @@ -46,4 +46,4 @@ public function getIterator(): Generator { return yield from $this->getItems(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination/no-custom-config/src/Core/Pagination/Pager.php b/seed/php-sdk/pagination/no-custom-config/src/Core/Pagination/Pager.php index 351110574462..7897a30b307b 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Core/Pagination/Pager.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Core/Pagination/Pager.php @@ -33,4 +33,4 @@ public function getIterator(): Generator yield from $page->getItems(); } } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination/no-custom-config/src/Core/Pagination/PaginationHelper.php b/seed/php-sdk/pagination/no-custom-config/src/Core/Pagination/PaginationHelper.php index b1be91f12b98..bc0fd78fe428 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Core/Pagination/PaginationHelper.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Core/Pagination/PaginationHelper.php @@ -100,8 +100,7 @@ private static function createInstanceWithDefaults(string $className) $type = $property->getType(); $value = self::getDefaultValueForType($type); // if something is nullable, don't explicitly pass the null value as a parameter - if($value === null) - { + if ($value === null) { continue; } diff --git a/seed/php-sdk/pagination/no-custom-config/src/Core/Types/ArrayType.php b/seed/php-sdk/pagination/no-custom-config/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/pagination/no-custom-config/src/Core/Types/Constant.php b/seed/php-sdk/pagination/no-custom-config/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Core/Types/Constant.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination/no-custom-config/src/Core/Types/Union.php b/seed/php-sdk/pagination/no-custom-config/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Core/Types/Union.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination/no-custom-config/src/Exceptions/SeedApiException.php b/seed/php-sdk/pagination/no-custom-config/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/pagination/no-custom-config/src/Exceptions/SeedException.php b/seed/php-sdk/pagination/no-custom-config/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Exceptions/SeedException.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/InlineUsersClient.php b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/InlineUsersClient.php index 68e26ae87e6d..2f22bfec9503 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/InlineUsersClient.php +++ b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/InlineUsersClient.php @@ -35,7 +35,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class InlineUsersClient +class InlineUsersClient { /** * @var array{ @@ -63,11 +63,10 @@ class InlineUsersClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -84,11 +83,12 @@ function __construct( * } $options * @return Pager */ - public function listWithCursorPagination(ListUsersCursorPaginationRequest $request = new ListUsersCursorPaginationRequest(), ?array $options = null): Pager { + public function listWithCursorPagination(ListUsersCursorPaginationRequest $request = new ListUsersCursorPaginationRequest(), ?array $options = null): Pager + { return new CursorPager( request: $request, - getNextPage: fn(ListUsersCursorPaginationRequest $request) => $this->_listWithCursorPagination($request, $options), - setCursor: function (ListUsersCursorPaginationRequest $request, string $cursor) { + getNextPage: fn (ListUsersCursorPaginationRequest $request) => $this->_listWithCursorPagination($request, $options), + setCursor: function (ListUsersCursorPaginationRequest $request, string $cursor) { $request->startingAfter = $cursor; }, /* @phpstan-ignore-next-line */ @@ -110,11 +110,12 @@ public function listWithCursorPagination(ListUsersCursorPaginationRequest $reque * } $options * @return Pager */ - public function listWithMixedTypeCursorPagination(ListUsersMixedTypeCursorPaginationRequest $request = new ListUsersMixedTypeCursorPaginationRequest(), ?array $options = null): Pager { + public function listWithMixedTypeCursorPagination(ListUsersMixedTypeCursorPaginationRequest $request = new ListUsersMixedTypeCursorPaginationRequest(), ?array $options = null): Pager + { return new CursorPager( request: $request, - getNextPage: fn(ListUsersMixedTypeCursorPaginationRequest $request) => $this->_listWithMixedTypeCursorPagination($request, $options), - setCursor: function (ListUsersMixedTypeCursorPaginationRequest $request, string $cursor) { + getNextPage: fn (ListUsersMixedTypeCursorPaginationRequest $request) => $this->_listWithMixedTypeCursorPagination($request, $options), + setCursor: function (ListUsersMixedTypeCursorPaginationRequest $request, string $cursor) { $request->cursor = $cursor; }, /* @phpstan-ignore-next-line */ @@ -136,11 +137,12 @@ public function listWithMixedTypeCursorPagination(ListUsersMixedTypeCursorPagina * } $options * @return Pager */ - public function listWithBodyCursorPagination(ListUsersBodyCursorPaginationRequest $request = new ListUsersBodyCursorPaginationRequest(), ?array $options = null): Pager { + public function listWithBodyCursorPagination(ListUsersBodyCursorPaginationRequest $request = new ListUsersBodyCursorPaginationRequest(), ?array $options = null): Pager + { return new CursorPager( request: $request, - getNextPage: fn(ListUsersBodyCursorPaginationRequest $request) => $this->_listWithBodyCursorPagination($request, $options), - setCursor: function (ListUsersBodyCursorPaginationRequest $request, string $cursor) { + getNextPage: fn (ListUsersBodyCursorPaginationRequest $request) => $this->_listWithBodyCursorPagination($request, $options), + setCursor: function (ListUsersBodyCursorPaginationRequest $request, string $cursor) { PaginationHelper::setDeep($request, ["pagination", "cursor"], $cursor); }, /* @phpstan-ignore-next-line */ @@ -162,18 +164,19 @@ public function listWithBodyCursorPagination(ListUsersBodyCursorPaginationReques * } $options * @return Pager */ - public function listWithOffsetPagination(ListUsersOffsetPaginationRequest $request = new ListUsersOffsetPaginationRequest(), ?array $options = null): Pager { + public function listWithOffsetPagination(ListUsersOffsetPaginationRequest $request = new ListUsersOffsetPaginationRequest(), ?array $options = null): Pager + { return new OffsetPager( request: $request, - getNextPage: fn(ListUsersOffsetPaginationRequest $request) => $this->_listWithOffsetPagination($request, $options), + getNextPage: fn (ListUsersOffsetPaginationRequest $request) => $this->_listWithOffsetPagination($request, $options), /* @phpstan-ignore-next-line */ - getOffset: fn(ListUsersOffsetPaginationRequest $request) => $request?->page ?? 0, - setOffset: function (ListUsersOffsetPaginationRequest $request, int $offset) { + getOffset: fn (ListUsersOffsetPaginationRequest $request) => $request?->page ?? 0, + setOffset: function (ListUsersOffsetPaginationRequest $request, int $offset) { $request->page = $offset; }, getStep: null, /* @phpstan-ignore-next-line */ - getItems: fn(ListUsersPaginationResponse $response) => $response?->data?->users ?? [], + getItems: fn (ListUsersPaginationResponse $response) => $response?->data?->users ?? [], /* @phpstan-ignore-next-line */ hasNextPage: null, ); @@ -191,18 +194,19 @@ public function listWithOffsetPagination(ListUsersOffsetPaginationRequest $reque * } $options * @return Pager */ - public function listWithDoubleOffsetPagination(ListUsersDoubleOffsetPaginationRequest $request = new ListUsersDoubleOffsetPaginationRequest(), ?array $options = null): Pager { + public function listWithDoubleOffsetPagination(ListUsersDoubleOffsetPaginationRequest $request = new ListUsersDoubleOffsetPaginationRequest(), ?array $options = null): Pager + { return new OffsetPager( request: $request, - getNextPage: fn(ListUsersDoubleOffsetPaginationRequest $request) => $this->_listWithDoubleOffsetPagination($request, $options), + getNextPage: fn (ListUsersDoubleOffsetPaginationRequest $request) => $this->_listWithDoubleOffsetPagination($request, $options), /* @phpstan-ignore-next-line */ - getOffset: fn(ListUsersDoubleOffsetPaginationRequest $request) => $request?->page ?? 0, - setOffset: function (ListUsersDoubleOffsetPaginationRequest $request, int $offset) { + getOffset: fn (ListUsersDoubleOffsetPaginationRequest $request) => $request?->page ?? 0, + setOffset: function (ListUsersDoubleOffsetPaginationRequest $request, int $offset) { $request->page = $offset; }, getStep: null, /* @phpstan-ignore-next-line */ - getItems: fn(ListUsersPaginationResponse $response) => $response?->data?->users ?? [], + getItems: fn (ListUsersPaginationResponse $response) => $response?->data?->users ?? [], /* @phpstan-ignore-next-line */ hasNextPage: null, ); @@ -220,18 +224,19 @@ public function listWithDoubleOffsetPagination(ListUsersDoubleOffsetPaginationRe * } $options * @return Pager */ - public function listWithBodyOffsetPagination(ListUsersBodyOffsetPaginationRequest $request = new ListUsersBodyOffsetPaginationRequest(), ?array $options = null): Pager { + public function listWithBodyOffsetPagination(ListUsersBodyOffsetPaginationRequest $request = new ListUsersBodyOffsetPaginationRequest(), ?array $options = null): Pager + { return new OffsetPager( request: $request, - getNextPage: fn(ListUsersBodyOffsetPaginationRequest $request) => $this->_listWithBodyOffsetPagination($request, $options), + getNextPage: fn (ListUsersBodyOffsetPaginationRequest $request) => $this->_listWithBodyOffsetPagination($request, $options), /* @phpstan-ignore-next-line */ - getOffset: fn(ListUsersBodyOffsetPaginationRequest $request) => $request?->pagination?->page ?? 0, - setOffset: function (ListUsersBodyOffsetPaginationRequest $request, int $offset) { + getOffset: fn (ListUsersBodyOffsetPaginationRequest $request) => $request?->pagination?->page ?? 0, + setOffset: function (ListUsersBodyOffsetPaginationRequest $request, int $offset) { PaginationHelper::setDeep($request, ["pagination", "page"], $offset); }, getStep: null, /* @phpstan-ignore-next-line */ - getItems: fn(ListUsersPaginationResponse $response) => $response?->data?->users ?? [], + getItems: fn (ListUsersPaginationResponse $response) => $response?->data?->users ?? [], /* @phpstan-ignore-next-line */ hasNextPage: null, ); @@ -249,19 +254,20 @@ public function listWithBodyOffsetPagination(ListUsersBodyOffsetPaginationReques * } $options * @return Pager */ - public function listWithOffsetStepPagination(ListUsersOffsetStepPaginationRequest $request = new ListUsersOffsetStepPaginationRequest(), ?array $options = null): Pager { + public function listWithOffsetStepPagination(ListUsersOffsetStepPaginationRequest $request = new ListUsersOffsetStepPaginationRequest(), ?array $options = null): Pager + { return new OffsetPager( request: $request, - getNextPage: fn(ListUsersOffsetStepPaginationRequest $request) => $this->_listWithOffsetStepPagination($request, $options), + getNextPage: fn (ListUsersOffsetStepPaginationRequest $request) => $this->_listWithOffsetStepPagination($request, $options), /* @phpstan-ignore-next-line */ - getOffset: fn(ListUsersOffsetStepPaginationRequest $request) => $request?->page ?? 0, - setOffset: function (ListUsersOffsetStepPaginationRequest $request, int $offset) { + getOffset: fn (ListUsersOffsetStepPaginationRequest $request) => $request?->page ?? 0, + setOffset: function (ListUsersOffsetStepPaginationRequest $request, int $offset) { $request->page = $offset; }, /* @phpstan-ignore-next-line */ - getStep: fn(ListUsersOffsetStepPaginationRequest $request) => $request?->limit ?? 0, + getStep: fn (ListUsersOffsetStepPaginationRequest $request) => $request?->limit ?? 0, /* @phpstan-ignore-next-line */ - getItems: fn(ListUsersPaginationResponse $response) => $response?->data?->users ?? [], + getItems: fn (ListUsersPaginationResponse $response) => $response?->data?->users ?? [], /* @phpstan-ignore-next-line */ hasNextPage: null, ); @@ -279,21 +285,22 @@ public function listWithOffsetStepPagination(ListUsersOffsetStepPaginationReques * } $options * @return Pager */ - public function listWithOffsetPaginationHasNextPage(ListWithOffsetPaginationHasNextPageRequest $request = new ListWithOffsetPaginationHasNextPageRequest(), ?array $options = null): Pager { + public function listWithOffsetPaginationHasNextPage(ListWithOffsetPaginationHasNextPageRequest $request = new ListWithOffsetPaginationHasNextPageRequest(), ?array $options = null): Pager + { return new OffsetPager( request: $request, - getNextPage: fn(ListWithOffsetPaginationHasNextPageRequest $request) => $this->_listWithOffsetPaginationHasNextPage($request, $options), + getNextPage: fn (ListWithOffsetPaginationHasNextPageRequest $request) => $this->_listWithOffsetPaginationHasNextPage($request, $options), /* @phpstan-ignore-next-line */ - getOffset: fn(ListWithOffsetPaginationHasNextPageRequest $request) => $request?->page ?? 0, - setOffset: function (ListWithOffsetPaginationHasNextPageRequest $request, int $offset) { + getOffset: fn (ListWithOffsetPaginationHasNextPageRequest $request) => $request?->page ?? 0, + setOffset: function (ListWithOffsetPaginationHasNextPageRequest $request, int $offset) { $request->page = $offset; }, /* @phpstan-ignore-next-line */ - getStep: fn(ListWithOffsetPaginationHasNextPageRequest $request) => $request?->limit ?? 0, + getStep: fn (ListWithOffsetPaginationHasNextPageRequest $request) => $request?->limit ?? 0, /* @phpstan-ignore-next-line */ - getItems: fn(ListUsersPaginationResponse $response) => $response?->data?->users ?? [], + getItems: fn (ListUsersPaginationResponse $response) => $response?->data?->users ?? [], /* @phpstan-ignore-next-line */ - hasNextPage: fn(ListUsersPaginationResponse $response) => $response?->hasNextPage, + hasNextPage: fn (ListUsersPaginationResponse $response) => $response?->hasNextPage, ); } @@ -309,11 +316,12 @@ public function listWithOffsetPaginationHasNextPage(ListWithOffsetPaginationHasN * } $options * @return Pager */ - public function listWithExtendedResults(ListUsersExtendedRequest $request = new ListUsersExtendedRequest(), ?array $options = null): Pager { + public function listWithExtendedResults(ListUsersExtendedRequest $request = new ListUsersExtendedRequest(), ?array $options = null): Pager + { return new CursorPager( request: $request, - getNextPage: fn(ListUsersExtendedRequest $request) => $this->_listWithExtendedResults($request, $options), - setCursor: function (ListUsersExtendedRequest $request, ?string $cursor) { + getNextPage: fn (ListUsersExtendedRequest $request) => $this->_listWithExtendedResults($request, $options), + setCursor: function (ListUsersExtendedRequest $request, ?string $cursor) { $request->cursor = $cursor; }, /* @phpstan-ignore-next-line */ @@ -335,11 +343,12 @@ public function listWithExtendedResults(ListUsersExtendedRequest $request = new * } $options * @return Pager */ - public function listWithExtendedResultsAndOptionalData(ListUsersExtendedRequestForOptionalData $request = new ListUsersExtendedRequestForOptionalData(), ?array $options = null): Pager { + public function listWithExtendedResultsAndOptionalData(ListUsersExtendedRequestForOptionalData $request = new ListUsersExtendedRequestForOptionalData(), ?array $options = null): Pager + { return new CursorPager( request: $request, - getNextPage: fn(ListUsersExtendedRequestForOptionalData $request) => $this->_listWithExtendedResultsAndOptionalData($request, $options), - setCursor: function (ListUsersExtendedRequestForOptionalData $request, ?string $cursor) { + getNextPage: fn (ListUsersExtendedRequestForOptionalData $request) => $this->_listWithExtendedResultsAndOptionalData($request, $options), + setCursor: function (ListUsersExtendedRequestForOptionalData $request, ?string $cursor) { $request->cursor = $cursor; }, /* @phpstan-ignore-next-line */ @@ -361,11 +370,12 @@ public function listWithExtendedResultsAndOptionalData(ListUsersExtendedRequestF * } $options * @return Pager */ - public function listUsernames(ListUsernamesRequest $request = new ListUsernamesRequest(), ?array $options = null): Pager { + public function listUsernames(ListUsernamesRequest $request = new ListUsernamesRequest(), ?array $options = null): Pager + { return new CursorPager( request: $request, - getNextPage: fn(ListUsernamesRequest $request) => $this->_listUsernames($request, $options), - setCursor: function (ListUsernamesRequest $request, ?string $cursor) { + getNextPage: fn (ListUsernamesRequest $request) => $this->_listUsernames($request, $options), + setCursor: function (ListUsernamesRequest $request, ?string $cursor) { $request->startingAfter = $cursor; }, /* @phpstan-ignore-next-line */ @@ -387,18 +397,19 @@ public function listUsernames(ListUsernamesRequest $request = new ListUsernamesR * } $options * @return Pager */ - public function listWithGlobalConfig(ListWithGlobalConfigRequest $request = new ListWithGlobalConfigRequest(), ?array $options = null): Pager { + public function listWithGlobalConfig(ListWithGlobalConfigRequest $request = new ListWithGlobalConfigRequest(), ?array $options = null): Pager + { return new OffsetPager( request: $request, - getNextPage: fn(ListWithGlobalConfigRequest $request) => $this->_listWithGlobalConfig($request, $options), + getNextPage: fn (ListWithGlobalConfigRequest $request) => $this->_listWithGlobalConfig($request, $options), /* @phpstan-ignore-next-line */ - getOffset: fn(ListWithGlobalConfigRequest $request) => $request?->offset ?? 0, - setOffset: function (ListWithGlobalConfigRequest $request, int $offset) { + getOffset: fn (ListWithGlobalConfigRequest $request) => $request?->offset ?? 0, + setOffset: function (ListWithGlobalConfigRequest $request, int $offset) { $request->offset = $offset; }, getStep: null, /* @phpstan-ignore-next-line */ - getItems: fn(UsernameContainer $response) => $response?->results ?? [], + getItems: fn (UsernameContainer $response) => $response?->results ?? [], /* @phpstan-ignore-next-line */ hasNextPage: null, ); @@ -418,19 +429,20 @@ public function listWithGlobalConfig(ListWithGlobalConfigRequest $request = new * @throws SeedException * @throws SeedApiException */ - private function _listWithCursorPagination(ListUsersCursorPaginationRequest $request = new ListUsersCursorPaginationRequest(), ?array $options = null): ListUsersPaginationResponse { + private function _listWithCursorPagination(ListUsersCursorPaginationRequest $request = new ListUsersCursorPaginationRequest(), ?array $options = null): ListUsersPaginationResponse + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->page != null){ + if ($request->page != null) { $query['page'] = $request->page; } - if ($request->perPage != null){ + if ($request->perPage != null) { $query['per_page'] = $request->perPage; } - if ($request->order != null){ + if ($request->order != null) { $query['order'] = $request->order; } - if ($request->startingAfter != null){ + if ($request->startingAfter != null) { $query['starting_after'] = $request->startingAfter; } try { @@ -444,15 +456,15 @@ private function _listWithCursorPagination(ListUsersCursorPaginationRequest $req $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ListUsersPaginationResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -484,10 +496,11 @@ private function _listWithCursorPagination(ListUsersCursorPaginationRequest $req * @throws SeedException * @throws SeedApiException */ - private function _listWithMixedTypeCursorPagination(ListUsersMixedTypeCursorPaginationRequest $request = new ListUsersMixedTypeCursorPaginationRequest(), ?array $options = null): ListUsersMixedTypePaginationResponse { + private function _listWithMixedTypeCursorPagination(ListUsersMixedTypeCursorPaginationRequest $request = new ListUsersMixedTypeCursorPaginationRequest(), ?array $options = null): ListUsersMixedTypePaginationResponse + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->cursor != null){ + if ($request->cursor != null) { $query['cursor'] = $request->cursor; } try { @@ -501,15 +514,15 @@ private function _listWithMixedTypeCursorPagination(ListUsersMixedTypeCursorPagi $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ListUsersMixedTypePaginationResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -541,7 +554,8 @@ private function _listWithMixedTypeCursorPagination(ListUsersMixedTypeCursorPagi * @throws SeedException * @throws SeedApiException */ - private function _listWithBodyCursorPagination(ListUsersBodyCursorPaginationRequest $request = new ListUsersBodyCursorPaginationRequest(), ?array $options = null): ListUsersPaginationResponse { + private function _listWithBodyCursorPagination(ListUsersBodyCursorPaginationRequest $request = new ListUsersBodyCursorPaginationRequest(), ?array $options = null): ListUsersPaginationResponse + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -554,15 +568,15 @@ private function _listWithBodyCursorPagination(ListUsersBodyCursorPaginationRequ $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ListUsersPaginationResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -594,19 +608,20 @@ private function _listWithBodyCursorPagination(ListUsersBodyCursorPaginationRequ * @throws SeedException * @throws SeedApiException */ - private function _listWithOffsetPagination(ListUsersOffsetPaginationRequest $request = new ListUsersOffsetPaginationRequest(), ?array $options = null): ListUsersPaginationResponse { + private function _listWithOffsetPagination(ListUsersOffsetPaginationRequest $request = new ListUsersOffsetPaginationRequest(), ?array $options = null): ListUsersPaginationResponse + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->page != null){ + if ($request->page != null) { $query['page'] = $request->page; } - if ($request->perPage != null){ + if ($request->perPage != null) { $query['per_page'] = $request->perPage; } - if ($request->order != null){ + if ($request->order != null) { $query['order'] = $request->order; } - if ($request->startingAfter != null){ + if ($request->startingAfter != null) { $query['starting_after'] = $request->startingAfter; } try { @@ -620,15 +635,15 @@ private function _listWithOffsetPagination(ListUsersOffsetPaginationRequest $req $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ListUsersPaginationResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -660,19 +675,20 @@ private function _listWithOffsetPagination(ListUsersOffsetPaginationRequest $req * @throws SeedException * @throws SeedApiException */ - private function _listWithDoubleOffsetPagination(ListUsersDoubleOffsetPaginationRequest $request = new ListUsersDoubleOffsetPaginationRequest(), ?array $options = null): ListUsersPaginationResponse { + private function _listWithDoubleOffsetPagination(ListUsersDoubleOffsetPaginationRequest $request = new ListUsersDoubleOffsetPaginationRequest(), ?array $options = null): ListUsersPaginationResponse + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->page != null){ + if ($request->page != null) { $query['page'] = $request->page; } - if ($request->perPage != null){ + if ($request->perPage != null) { $query['per_page'] = $request->perPage; } - if ($request->order != null){ + if ($request->order != null) { $query['order'] = $request->order; } - if ($request->startingAfter != null){ + if ($request->startingAfter != null) { $query['starting_after'] = $request->startingAfter; } try { @@ -686,15 +702,15 @@ private function _listWithDoubleOffsetPagination(ListUsersDoubleOffsetPagination $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ListUsersPaginationResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -726,7 +742,8 @@ private function _listWithDoubleOffsetPagination(ListUsersDoubleOffsetPagination * @throws SeedException * @throws SeedApiException */ - private function _listWithBodyOffsetPagination(ListUsersBodyOffsetPaginationRequest $request = new ListUsersBodyOffsetPaginationRequest(), ?array $options = null): ListUsersPaginationResponse { + private function _listWithBodyOffsetPagination(ListUsersBodyOffsetPaginationRequest $request = new ListUsersBodyOffsetPaginationRequest(), ?array $options = null): ListUsersPaginationResponse + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -739,15 +756,15 @@ private function _listWithBodyOffsetPagination(ListUsersBodyOffsetPaginationRequ $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ListUsersPaginationResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -779,16 +796,17 @@ private function _listWithBodyOffsetPagination(ListUsersBodyOffsetPaginationRequ * @throws SeedException * @throws SeedApiException */ - private function _listWithOffsetStepPagination(ListUsersOffsetStepPaginationRequest $request = new ListUsersOffsetStepPaginationRequest(), ?array $options = null): ListUsersPaginationResponse { + private function _listWithOffsetStepPagination(ListUsersOffsetStepPaginationRequest $request = new ListUsersOffsetStepPaginationRequest(), ?array $options = null): ListUsersPaginationResponse + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->page != null){ + if ($request->page != null) { $query['page'] = $request->page; } - if ($request->limit != null){ + if ($request->limit != null) { $query['limit'] = $request->limit; } - if ($request->order != null){ + if ($request->order != null) { $query['order'] = $request->order; } try { @@ -802,15 +820,15 @@ private function _listWithOffsetStepPagination(ListUsersOffsetStepPaginationRequ $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ListUsersPaginationResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -842,16 +860,17 @@ private function _listWithOffsetStepPagination(ListUsersOffsetStepPaginationRequ * @throws SeedException * @throws SeedApiException */ - private function _listWithOffsetPaginationHasNextPage(ListWithOffsetPaginationHasNextPageRequest $request = new ListWithOffsetPaginationHasNextPageRequest(), ?array $options = null): ListUsersPaginationResponse { + private function _listWithOffsetPaginationHasNextPage(ListWithOffsetPaginationHasNextPageRequest $request = new ListWithOffsetPaginationHasNextPageRequest(), ?array $options = null): ListUsersPaginationResponse + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->page != null){ + if ($request->page != null) { $query['page'] = $request->page; } - if ($request->limit != null){ + if ($request->limit != null) { $query['limit'] = $request->limit; } - if ($request->order != null){ + if ($request->order != null) { $query['order'] = $request->order; } try { @@ -865,15 +884,15 @@ private function _listWithOffsetPaginationHasNextPage(ListWithOffsetPaginationHa $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ListUsersPaginationResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -905,10 +924,11 @@ private function _listWithOffsetPaginationHasNextPage(ListWithOffsetPaginationHa * @throws SeedException * @throws SeedApiException */ - private function _listWithExtendedResults(ListUsersExtendedRequest $request = new ListUsersExtendedRequest(), ?array $options = null): ListUsersExtendedResponse { + private function _listWithExtendedResults(ListUsersExtendedRequest $request = new ListUsersExtendedRequest(), ?array $options = null): ListUsersExtendedResponse + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->cursor != null){ + if ($request->cursor != null) { $query['cursor'] = $request->cursor; } try { @@ -922,15 +942,15 @@ private function _listWithExtendedResults(ListUsersExtendedRequest $request = ne $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ListUsersExtendedResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -962,10 +982,11 @@ private function _listWithExtendedResults(ListUsersExtendedRequest $request = ne * @throws SeedException * @throws SeedApiException */ - private function _listWithExtendedResultsAndOptionalData(ListUsersExtendedRequestForOptionalData $request = new ListUsersExtendedRequestForOptionalData(), ?array $options = null): ListUsersExtendedOptionalListResponse { + private function _listWithExtendedResultsAndOptionalData(ListUsersExtendedRequestForOptionalData $request = new ListUsersExtendedRequestForOptionalData(), ?array $options = null): ListUsersExtendedOptionalListResponse + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->cursor != null){ + if ($request->cursor != null) { $query['cursor'] = $request->cursor; } try { @@ -979,15 +1000,15 @@ private function _listWithExtendedResultsAndOptionalData(ListUsersExtendedReques $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ListUsersExtendedOptionalListResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -1019,10 +1040,11 @@ private function _listWithExtendedResultsAndOptionalData(ListUsersExtendedReques * @throws SeedException * @throws SeedApiException */ - private function _listUsernames(ListUsernamesRequest $request = new ListUsernamesRequest(), ?array $options = null): UsernameCursor { + private function _listUsernames(ListUsernamesRequest $request = new ListUsernamesRequest(), ?array $options = null): UsernameCursor + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->startingAfter != null){ + if ($request->startingAfter != null) { $query['starting_after'] = $request->startingAfter; } try { @@ -1036,15 +1058,15 @@ private function _listUsernames(ListUsernamesRequest $request = new ListUsername $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return UsernameCursor::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -1076,10 +1098,11 @@ private function _listUsernames(ListUsernamesRequest $request = new ListUsername * @throws SeedException * @throws SeedApiException */ - private function _listWithGlobalConfig(ListWithGlobalConfigRequest $request = new ListWithGlobalConfigRequest(), ?array $options = null): UsernameContainer { + private function _listWithGlobalConfig(ListWithGlobalConfigRequest $request = new ListWithGlobalConfigRequest(), ?array $options = null): UsernameContainer + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->offset != null){ + if ($request->offset != null) { $query['offset'] = $request->offset; } try { @@ -1093,15 +1116,15 @@ private function _listWithGlobalConfig(ListWithGlobalConfigRequest $request = ne $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return UsernameContainer::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListUsernamesRequest.php b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListUsernamesRequest.php index 82d165e9d720..d8c1111fd313 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListUsernamesRequest.php +++ b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListUsernamesRequest.php @@ -21,8 +21,7 @@ class ListUsernamesRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->startingAfter = $values['startingAfter'] ?? null; } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListUsersBodyCursorPaginationRequest.php b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListUsersBodyCursorPaginationRequest.php index 7331de950afc..ff21725e62e0 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListUsersBodyCursorPaginationRequest.php +++ b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListUsersBodyCursorPaginationRequest.php @@ -24,8 +24,7 @@ class ListUsersBodyCursorPaginationRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->pagination = $values['pagination'] ?? null; } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListUsersBodyOffsetPaginationRequest.php b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListUsersBodyOffsetPaginationRequest.php index 43eae6623ebb..5dd27eb82018 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListUsersBodyOffsetPaginationRequest.php +++ b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListUsersBodyOffsetPaginationRequest.php @@ -24,8 +24,7 @@ class ListUsersBodyOffsetPaginationRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->pagination = $values['pagination'] ?? null; } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListUsersCursorPaginationRequest.php b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListUsersCursorPaginationRequest.php index 4378bd67ac4e..26bbb90d66f6 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListUsersCursorPaginationRequest.php +++ b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListUsersCursorPaginationRequest.php @@ -40,8 +40,10 @@ class ListUsersCursorPaginationRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->page = $values['page'] ?? null;$this->perPage = $values['perPage'] ?? null;$this->order = $values['order'] ?? null;$this->startingAfter = $values['startingAfter'] ?? null; + ) { + $this->page = $values['page'] ?? null; + $this->perPage = $values['perPage'] ?? null; + $this->order = $values['order'] ?? null; + $this->startingAfter = $values['startingAfter'] ?? null; } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListUsersDoubleOffsetPaginationRequest.php b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListUsersDoubleOffsetPaginationRequest.php index f3862db70bc0..f31fa0c90563 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListUsersDoubleOffsetPaginationRequest.php +++ b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListUsersDoubleOffsetPaginationRequest.php @@ -40,8 +40,10 @@ class ListUsersDoubleOffsetPaginationRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->page = $values['page'] ?? null;$this->perPage = $values['perPage'] ?? null;$this->order = $values['order'] ?? null;$this->startingAfter = $values['startingAfter'] ?? null; + ) { + $this->page = $values['page'] ?? null; + $this->perPage = $values['perPage'] ?? null; + $this->order = $values['order'] ?? null; + $this->startingAfter = $values['startingAfter'] ?? null; } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListUsersExtendedRequest.php b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListUsersExtendedRequest.php index 790a18eb7749..3bdd9852ee06 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListUsersExtendedRequest.php +++ b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListUsersExtendedRequest.php @@ -18,8 +18,7 @@ class ListUsersExtendedRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->cursor = $values['cursor'] ?? null; } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListUsersExtendedRequestForOptionalData.php b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListUsersExtendedRequestForOptionalData.php index 94af18ca92b0..23838d951cde 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListUsersExtendedRequestForOptionalData.php +++ b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListUsersExtendedRequestForOptionalData.php @@ -18,8 +18,7 @@ class ListUsersExtendedRequestForOptionalData extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->cursor = $values['cursor'] ?? null; } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListUsersMixedTypeCursorPaginationRequest.php b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListUsersMixedTypeCursorPaginationRequest.php index 5801b6de34eb..a66d8a965c0c 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListUsersMixedTypeCursorPaginationRequest.php +++ b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListUsersMixedTypeCursorPaginationRequest.php @@ -18,8 +18,7 @@ class ListUsersMixedTypeCursorPaginationRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->cursor = $values['cursor'] ?? null; } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListUsersOffsetPaginationRequest.php b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListUsersOffsetPaginationRequest.php index ee3ec7e8f5f9..8e669d5f853c 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListUsersOffsetPaginationRequest.php +++ b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListUsersOffsetPaginationRequest.php @@ -40,8 +40,10 @@ class ListUsersOffsetPaginationRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->page = $values['page'] ?? null;$this->perPage = $values['perPage'] ?? null;$this->order = $values['order'] ?? null;$this->startingAfter = $values['startingAfter'] ?? null; + ) { + $this->page = $values['page'] ?? null; + $this->perPage = $values['perPage'] ?? null; + $this->order = $values['order'] ?? null; + $this->startingAfter = $values['startingAfter'] ?? null; } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListUsersOffsetStepPaginationRequest.php b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListUsersOffsetStepPaginationRequest.php index 4792c4c554e5..d414c73e97bc 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListUsersOffsetStepPaginationRequest.php +++ b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListUsersOffsetStepPaginationRequest.php @@ -35,8 +35,9 @@ class ListUsersOffsetStepPaginationRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->page = $values['page'] ?? null;$this->limit = $values['limit'] ?? null;$this->order = $values['order'] ?? null; + ) { + $this->page = $values['page'] ?? null; + $this->limit = $values['limit'] ?? null; + $this->order = $values['order'] ?? null; } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListWithGlobalConfigRequest.php b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListWithGlobalConfigRequest.php index 44bd23ce0f9d..f12ee9a5cced 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListWithGlobalConfigRequest.php +++ b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListWithGlobalConfigRequest.php @@ -18,8 +18,7 @@ class ListWithGlobalConfigRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->offset = $values['offset'] ?? null; } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListWithOffsetPaginationHasNextPageRequest.php b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListWithOffsetPaginationHasNextPageRequest.php index 0374f3c5b78a..2773e59aa33c 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListWithOffsetPaginationHasNextPageRequest.php +++ b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Requests/ListWithOffsetPaginationHasNextPageRequest.php @@ -35,8 +35,9 @@ class ListWithOffsetPaginationHasNextPageRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->page = $values['page'] ?? null;$this->limit = $values['limit'] ?? null;$this->order = $values['order'] ?? null; + ) { + $this->page = $values['page'] ?? null; + $this->limit = $values['limit'] ?? null; + $this->order = $values['order'] ?? null; } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Traits/UserOptionalListPage.php b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Traits/UserOptionalListPage.php index feab00367ed9..1b985b5c50d3 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Traits/UserOptionalListPage.php +++ b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Traits/UserOptionalListPage.php @@ -9,7 +9,7 @@ * @property UserOptionalListContainer $data * @property ?string $next */ -trait UserOptionalListPage +trait UserOptionalListPage { /** * @var UserOptionalListContainer $data diff --git a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Traits/UserPage.php b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Traits/UserPage.php index f72547d8fb27..713e0ab603ea 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Traits/UserPage.php +++ b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Traits/UserPage.php @@ -9,7 +9,7 @@ * @property UserListContainer $data * @property ?string $next */ -trait UserPage +trait UserPage { /** * @var UserListContainer $data diff --git a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/ListUsersExtendedOptionalListResponse.php b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/ListUsersExtendedOptionalListResponse.php index f096feb117d5..d757af589cfa 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/ListUsersExtendedOptionalListResponse.php +++ b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/ListUsersExtendedOptionalListResponse.php @@ -25,15 +25,17 @@ class ListUsersExtendedOptionalListResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->data = $values['data'];$this->next = $values['next'] ?? null;$this->totalCount = $values['totalCount']; + ) { + $this->data = $values['data']; + $this->next = $values['next'] ?? null; + $this->totalCount = $values['totalCount']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/ListUsersExtendedResponse.php b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/ListUsersExtendedResponse.php index 9c3c90fd451c..91216f8bab0f 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/ListUsersExtendedResponse.php +++ b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/ListUsersExtendedResponse.php @@ -25,15 +25,17 @@ class ListUsersExtendedResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->data = $values['data'];$this->next = $values['next'] ?? null;$this->totalCount = $values['totalCount']; + ) { + $this->data = $values['data']; + $this->next = $values['next'] ?? null; + $this->totalCount = $values['totalCount']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/ListUsersMixedTypePaginationResponse.php b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/ListUsersMixedTypePaginationResponse.php index 83ff3a86d606..d7970ddec963 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/ListUsersMixedTypePaginationResponse.php +++ b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/ListUsersMixedTypePaginationResponse.php @@ -27,15 +27,16 @@ class ListUsersMixedTypePaginationResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->next = $values['next'];$this->data = $values['data']; + ) { + $this->next = $values['next']; + $this->data = $values['data']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/ListUsersPaginationResponse.php b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/ListUsersPaginationResponse.php index 13ec22bcbd89..dfa9bb45716c 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/ListUsersPaginationResponse.php +++ b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/ListUsersPaginationResponse.php @@ -41,15 +41,18 @@ class ListUsersPaginationResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->hasNextPage = $values['hasNextPage'] ?? null;$this->page = $values['page'] ?? null;$this->totalCount = $values['totalCount'];$this->data = $values['data']; + ) { + $this->hasNextPage = $values['hasNextPage'] ?? null; + $this->page = $values['page'] ?? null; + $this->totalCount = $values['totalCount']; + $this->data = $values['data']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/NextPage.php b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/NextPage.php index ea8fe032fd60..a7254f723402 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/NextPage.php +++ b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/NextPage.php @@ -27,15 +27,16 @@ class NextPage extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->page = $values['page'];$this->startingAfter = $values['startingAfter']; + ) { + $this->page = $values['page']; + $this->startingAfter = $values['startingAfter']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/Order.php b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/Order.php index 36338ff1a413..b99b851b7e7b 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/Order.php +++ b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/Order.php @@ -2,8 +2,8 @@ namespace Seed\InlineUsers\InlineUsers\Types; -enum Order - : string { +enum Order: string +{ case Asc = "asc"; case Desc = "desc"; } diff --git a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/Page.php b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/Page.php index ea69365a6533..3cab33f1da09 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/Page.php +++ b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/Page.php @@ -41,15 +41,18 @@ class Page extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->page = $values['page'];$this->next = $values['next'] ?? null;$this->perPage = $values['perPage'];$this->totalPage = $values['totalPage']; + ) { + $this->page = $values['page']; + $this->next = $values['next'] ?? null; + $this->perPage = $values['perPage']; + $this->totalPage = $values['totalPage']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/User.php b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/User.php index bfa7c8d95ed2..21f461f2f02b 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/User.php +++ b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/User.php @@ -27,15 +27,16 @@ class User extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->id = $values['id']; + ) { + $this->name = $values['name']; + $this->id = $values['id']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/UserListContainer.php b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/UserListContainer.php index abc061a70417..faedb33f2c58 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/UserListContainer.php +++ b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/UserListContainer.php @@ -21,15 +21,15 @@ class UserListContainer extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->users = $values['users']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/UserOptionalListContainer.php b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/UserOptionalListContainer.php index 4c6863bcd3d6..62d2d4d21798 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/UserOptionalListContainer.php +++ b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/UserOptionalListContainer.php @@ -21,15 +21,15 @@ class UserOptionalListContainer extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->users = $values['users'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/UserOptionalListPage.php b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/UserOptionalListPage.php index f1b8bb7a06b6..5dc0efc8654f 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/UserOptionalListPage.php +++ b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/UserOptionalListPage.php @@ -27,15 +27,16 @@ class UserOptionalListPage extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->data = $values['data'];$this->next = $values['next'] ?? null; + ) { + $this->data = $values['data']; + $this->next = $values['next'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/UserPage.php b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/UserPage.php index 27cd51762621..fda4454686d1 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/UserPage.php +++ b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/UserPage.php @@ -27,15 +27,16 @@ class UserPage extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->data = $values['data'];$this->next = $values['next'] ?? null; + ) { + $this->data = $values['data']; + $this->next = $values['next'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/UsernameContainer.php b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/UsernameContainer.php index afbe3acbaeb0..afa80fe2b545 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/UsernameContainer.php +++ b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/UsernameContainer.php @@ -21,15 +21,15 @@ class UsernameContainer extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->results = $values['results']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/Users.php b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/Users.php index 336f8759cea0..408324376b29 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/Users.php +++ b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/Users.php @@ -21,15 +21,15 @@ class Users extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->users = $values['users']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/WithCursor.php b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/WithCursor.php index e74913ab734b..d07a1d9d2b24 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/WithCursor.php +++ b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/WithCursor.php @@ -20,15 +20,15 @@ class WithCursor extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->cursor = $values['cursor'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/WithPage.php b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/WithPage.php index 4f42864fbebf..54aaba36aa8f 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/WithPage.php +++ b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsers/Types/WithPage.php @@ -20,15 +20,15 @@ class WithPage extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->page = $values['page'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsersClient.php b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsersClient.php index 5d09a216e694..e624647ad42c 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsersClient.php +++ b/seed/php-sdk/pagination/no-custom-config/src/InlineUsers/InlineUsersClient.php @@ -5,7 +5,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class InlineUsersClient +class InlineUsersClient { /** * @var \Seed\InlineUsers\InlineUsers\InlineUsersClient $inlineUsers @@ -38,11 +38,10 @@ class InlineUsersClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; $this->inlineUsers = new \Seed\InlineUsers\InlineUsers\InlineUsersClient($this->client, $this->options); diff --git a/seed/php-sdk/pagination/no-custom-config/src/SeedClient.php b/seed/php-sdk/pagination/no-custom-config/src/SeedClient.php index 5b69acb295fb..360e481a019e 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/SeedClient.php +++ b/seed/php-sdk/pagination/no-custom-config/src/SeedClient.php @@ -8,7 +8,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var ComplexClient $complex @@ -54,29 +54,28 @@ class SeedClient public function __construct( ?string $token = null, ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - if ($token != null){ + if ($token != null) { $defaultHeaders['Authorization'] = "Bearer $token"; } - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->complex = new ComplexClient($this->client, $this->options); $this->inlineUsers = new InlineUsersClient($this->client, $this->options); $this->users = new UsersClient($this->client, $this->options); diff --git a/seed/php-sdk/pagination/no-custom-config/src/Types/UsernameCursor.php b/seed/php-sdk/pagination/no-custom-config/src/Types/UsernameCursor.php index f6fd712dff06..e64d44fa92c9 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Types/UsernameCursor.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Types/UsernameCursor.php @@ -20,15 +20,15 @@ class UsernameCursor extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->cursor = $values['cursor']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/Types/UsernamePage.php b/seed/php-sdk/pagination/no-custom-config/src/Types/UsernamePage.php index e9520533e182..c21651929c07 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Types/UsernamePage.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Types/UsernamePage.php @@ -28,15 +28,16 @@ class UsernamePage extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->after = $values['after'] ?? null;$this->data = $values['data']; + ) { + $this->after = $values['after'] ?? null; + $this->data = $values['data']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsernamesRequest.php b/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsernamesRequest.php index 11e796d802f7..d8a2ea1bbe04 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsernamesRequest.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsernamesRequest.php @@ -21,8 +21,7 @@ class ListUsernamesRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->startingAfter = $values['startingAfter'] ?? null; } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsernamesWithOptionalResponseRequest.php b/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsernamesWithOptionalResponseRequest.php index bfccfea882e7..218a5fb01817 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsernamesWithOptionalResponseRequest.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsernamesWithOptionalResponseRequest.php @@ -21,8 +21,7 @@ class ListUsernamesWithOptionalResponseRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->startingAfter = $values['startingAfter'] ?? null; } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsersBodyCursorPaginationRequest.php b/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsersBodyCursorPaginationRequest.php index a663b2df746a..f1b508c220b6 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsersBodyCursorPaginationRequest.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsersBodyCursorPaginationRequest.php @@ -24,8 +24,7 @@ class ListUsersBodyCursorPaginationRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->pagination = $values['pagination'] ?? null; } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsersBodyOffsetPaginationRequest.php b/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsersBodyOffsetPaginationRequest.php index 1d8690aaba36..bbe529d63dab 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsersBodyOffsetPaginationRequest.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsersBodyOffsetPaginationRequest.php @@ -24,8 +24,7 @@ class ListUsersBodyOffsetPaginationRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->pagination = $values['pagination'] ?? null; } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsersCursorPaginationRequest.php b/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsersCursorPaginationRequest.php index 7daec9b715e3..02eaa03a200a 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsersCursorPaginationRequest.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsersCursorPaginationRequest.php @@ -40,8 +40,10 @@ class ListUsersCursorPaginationRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->page = $values['page'] ?? null;$this->perPage = $values['perPage'] ?? null;$this->order = $values['order'] ?? null;$this->startingAfter = $values['startingAfter'] ?? null; + ) { + $this->page = $values['page'] ?? null; + $this->perPage = $values['perPage'] ?? null; + $this->order = $values['order'] ?? null; + $this->startingAfter = $values['startingAfter'] ?? null; } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsersDoubleOffsetPaginationRequest.php b/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsersDoubleOffsetPaginationRequest.php index 9f4ed4567b08..8f6a7f424ec2 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsersDoubleOffsetPaginationRequest.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsersDoubleOffsetPaginationRequest.php @@ -40,8 +40,10 @@ class ListUsersDoubleOffsetPaginationRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->page = $values['page'] ?? null;$this->perPage = $values['perPage'] ?? null;$this->order = $values['order'] ?? null;$this->startingAfter = $values['startingAfter'] ?? null; + ) { + $this->page = $values['page'] ?? null; + $this->perPage = $values['perPage'] ?? null; + $this->order = $values['order'] ?? null; + $this->startingAfter = $values['startingAfter'] ?? null; } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsersExtendedRequest.php b/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsersExtendedRequest.php index 9931a6353ca2..c3c6f4fea1f3 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsersExtendedRequest.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsersExtendedRequest.php @@ -18,8 +18,7 @@ class ListUsersExtendedRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->cursor = $values['cursor'] ?? null; } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsersExtendedRequestForOptionalData.php b/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsersExtendedRequestForOptionalData.php index f48ea44a09e8..7285caa39685 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsersExtendedRequestForOptionalData.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsersExtendedRequestForOptionalData.php @@ -18,8 +18,7 @@ class ListUsersExtendedRequestForOptionalData extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->cursor = $values['cursor'] ?? null; } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsersMixedTypeCursorPaginationRequest.php b/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsersMixedTypeCursorPaginationRequest.php index 3af94f5638cd..497d75953869 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsersMixedTypeCursorPaginationRequest.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsersMixedTypeCursorPaginationRequest.php @@ -18,8 +18,7 @@ class ListUsersMixedTypeCursorPaginationRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->cursor = $values['cursor'] ?? null; } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsersOffsetPaginationRequest.php b/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsersOffsetPaginationRequest.php index fe27f333cdf5..6a6fee22c636 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsersOffsetPaginationRequest.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsersOffsetPaginationRequest.php @@ -40,8 +40,10 @@ class ListUsersOffsetPaginationRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->page = $values['page'] ?? null;$this->perPage = $values['perPage'] ?? null;$this->order = $values['order'] ?? null;$this->startingAfter = $values['startingAfter'] ?? null; + ) { + $this->page = $values['page'] ?? null; + $this->perPage = $values['perPage'] ?? null; + $this->order = $values['order'] ?? null; + $this->startingAfter = $values['startingAfter'] ?? null; } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsersOffsetStepPaginationRequest.php b/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsersOffsetStepPaginationRequest.php index 19e101ad9934..560224b59071 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsersOffsetStepPaginationRequest.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsersOffsetStepPaginationRequest.php @@ -35,8 +35,9 @@ class ListUsersOffsetStepPaginationRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->page = $values['page'] ?? null;$this->limit = $values['limit'] ?? null;$this->order = $values['order'] ?? null; + ) { + $this->page = $values['page'] ?? null; + $this->limit = $values['limit'] ?? null; + $this->order = $values['order'] ?? null; } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsersOptionalDataRequest.php b/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsersOptionalDataRequest.php new file mode 100644 index 000000000000..2e70b8b547d4 --- /dev/null +++ b/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListUsersOptionalDataRequest.php @@ -0,0 +1,24 @@ +page = $values['page'] ?? null; + } +} diff --git a/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListWithGlobalConfigRequest.php b/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListWithGlobalConfigRequest.php index 78d3fd6e138f..2478af96fada 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListWithGlobalConfigRequest.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListWithGlobalConfigRequest.php @@ -18,8 +18,7 @@ class ListWithGlobalConfigRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->offset = $values['offset'] ?? null; } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListWithOffsetPaginationHasNextPageRequest.php b/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListWithOffsetPaginationHasNextPageRequest.php index c1d98d5ce7fb..fc61dd4b3a9e 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListWithOffsetPaginationHasNextPageRequest.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Users/Requests/ListWithOffsetPaginationHasNextPageRequest.php @@ -35,8 +35,9 @@ class ListWithOffsetPaginationHasNextPageRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->page = $values['page'] ?? null;$this->limit = $values['limit'] ?? null;$this->order = $values['order'] ?? null; + ) { + $this->page = $values['page'] ?? null; + $this->limit = $values['limit'] ?? null; + $this->order = $values['order'] ?? null; } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/Users/Traits/UserOptionalListPage.php b/seed/php-sdk/pagination/no-custom-config/src/Users/Traits/UserOptionalListPage.php index 9335a71e1f75..d4107da32bb4 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Users/Traits/UserOptionalListPage.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Users/Traits/UserOptionalListPage.php @@ -9,7 +9,7 @@ * @property UserOptionalListContainer $data * @property ?string $next */ -trait UserOptionalListPage +trait UserOptionalListPage { /** * @var UserOptionalListContainer $data diff --git a/seed/php-sdk/pagination/no-custom-config/src/Users/Traits/UserPage.php b/seed/php-sdk/pagination/no-custom-config/src/Users/Traits/UserPage.php index 90f1f30bd7d2..157e7ebf6c11 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Users/Traits/UserPage.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Users/Traits/UserPage.php @@ -9,7 +9,7 @@ * @property UserListContainer $data * @property ?string $next */ -trait UserPage +trait UserPage { /** * @var UserListContainer $data diff --git a/seed/php-sdk/pagination/no-custom-config/src/Users/Types/ListUsersExtendedOptionalListResponse.php b/seed/php-sdk/pagination/no-custom-config/src/Users/Types/ListUsersExtendedOptionalListResponse.php index 6eceeff9e156..8a32c9ddae96 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Users/Types/ListUsersExtendedOptionalListResponse.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Users/Types/ListUsersExtendedOptionalListResponse.php @@ -25,15 +25,17 @@ class ListUsersExtendedOptionalListResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->data = $values['data'];$this->next = $values['next'] ?? null;$this->totalCount = $values['totalCount']; + ) { + $this->data = $values['data']; + $this->next = $values['next'] ?? null; + $this->totalCount = $values['totalCount']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/Users/Types/ListUsersExtendedResponse.php b/seed/php-sdk/pagination/no-custom-config/src/Users/Types/ListUsersExtendedResponse.php index 8748d767d7d6..a5bd5bb8563e 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Users/Types/ListUsersExtendedResponse.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Users/Types/ListUsersExtendedResponse.php @@ -25,15 +25,17 @@ class ListUsersExtendedResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->data = $values['data'];$this->next = $values['next'] ?? null;$this->totalCount = $values['totalCount']; + ) { + $this->data = $values['data']; + $this->next = $values['next'] ?? null; + $this->totalCount = $values['totalCount']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/Users/Types/ListUsersMixedTypePaginationResponse.php b/seed/php-sdk/pagination/no-custom-config/src/Users/Types/ListUsersMixedTypePaginationResponse.php index 977e8604f3da..25984e25055f 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Users/Types/ListUsersMixedTypePaginationResponse.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Users/Types/ListUsersMixedTypePaginationResponse.php @@ -28,15 +28,16 @@ class ListUsersMixedTypePaginationResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->next = $values['next'];$this->data = $values['data']; + ) { + $this->next = $values['next']; + $this->data = $values['data']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/Users/Types/ListUsersOptionalDataPaginationResponse.php b/seed/php-sdk/pagination/no-custom-config/src/Users/Types/ListUsersOptionalDataPaginationResponse.php new file mode 100644 index 000000000000..5c940d0bdad7 --- /dev/null +++ b/seed/php-sdk/pagination/no-custom-config/src/Users/Types/ListUsersOptionalDataPaginationResponse.php @@ -0,0 +1,59 @@ + $data + */ + #[JsonProperty('data'), ArrayType([User::class])] + public ?array $data; + + /** + * @param array{ + * totalCount: int, + * hasNextPage?: ?bool, + * page?: ?Page, + * data?: ?array, + * } $values + */ + public function __construct( + array $values, + ) { + $this->hasNextPage = $values['hasNextPage'] ?? null; + $this->page = $values['page'] ?? null; + $this->totalCount = $values['totalCount']; + $this->data = $values['data'] ?? null; + } + + /** + * @return string + */ + public function __toString(): string + { + return $this->toJson(); + } +} diff --git a/seed/php-sdk/pagination/no-custom-config/src/Users/Types/ListUsersPaginationResponse.php b/seed/php-sdk/pagination/no-custom-config/src/Users/Types/ListUsersPaginationResponse.php index 701eeadb004f..c542f29c382e 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Users/Types/ListUsersPaginationResponse.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Users/Types/ListUsersPaginationResponse.php @@ -42,15 +42,18 @@ class ListUsersPaginationResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->hasNextPage = $values['hasNextPage'] ?? null;$this->page = $values['page'] ?? null;$this->totalCount = $values['totalCount'];$this->data = $values['data']; + ) { + $this->hasNextPage = $values['hasNextPage'] ?? null; + $this->page = $values['page'] ?? null; + $this->totalCount = $values['totalCount']; + $this->data = $values['data']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/Users/Types/NextPage.php b/seed/php-sdk/pagination/no-custom-config/src/Users/Types/NextPage.php index 14a7b1a90bf3..f4f67a700b08 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Users/Types/NextPage.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Users/Types/NextPage.php @@ -27,15 +27,16 @@ class NextPage extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->page = $values['page'];$this->startingAfter = $values['startingAfter']; + ) { + $this->page = $values['page']; + $this->startingAfter = $values['startingAfter']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/Users/Types/Order.php b/seed/php-sdk/pagination/no-custom-config/src/Users/Types/Order.php index 20c3f8b53086..47f5a3ff6243 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Users/Types/Order.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Users/Types/Order.php @@ -2,8 +2,8 @@ namespace Seed\Users\Types; -enum Order - : string { +enum Order: string +{ case Asc = "asc"; case Desc = "desc"; } diff --git a/seed/php-sdk/pagination/no-custom-config/src/Users/Types/Page.php b/seed/php-sdk/pagination/no-custom-config/src/Users/Types/Page.php index eec844a8d10d..87384be83eb7 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Users/Types/Page.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Users/Types/Page.php @@ -41,15 +41,18 @@ class Page extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->page = $values['page'];$this->next = $values['next'] ?? null;$this->perPage = $values['perPage'];$this->totalPage = $values['totalPage']; + ) { + $this->page = $values['page']; + $this->next = $values['next'] ?? null; + $this->perPage = $values['perPage']; + $this->totalPage = $values['totalPage']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/Users/Types/User.php b/seed/php-sdk/pagination/no-custom-config/src/Users/Types/User.php index e619cf1f9c73..7ae63a1f0f0b 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Users/Types/User.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Users/Types/User.php @@ -27,15 +27,16 @@ class User extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->id = $values['id']; + ) { + $this->name = $values['name']; + $this->id = $values['id']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/Users/Types/UserListContainer.php b/seed/php-sdk/pagination/no-custom-config/src/Users/Types/UserListContainer.php index 85d3c1ba3a62..e9e828d48d5d 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Users/Types/UserListContainer.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Users/Types/UserListContainer.php @@ -21,15 +21,15 @@ class UserListContainer extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->users = $values['users']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/Users/Types/UserOptionalListContainer.php b/seed/php-sdk/pagination/no-custom-config/src/Users/Types/UserOptionalListContainer.php index bb8fdc69395e..159c45235bc8 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Users/Types/UserOptionalListContainer.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Users/Types/UserOptionalListContainer.php @@ -21,15 +21,15 @@ class UserOptionalListContainer extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->users = $values['users'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/Users/Types/UserOptionalListPage.php b/seed/php-sdk/pagination/no-custom-config/src/Users/Types/UserOptionalListPage.php index 430a17734c27..5621cd5d324c 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Users/Types/UserOptionalListPage.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Users/Types/UserOptionalListPage.php @@ -27,15 +27,16 @@ class UserOptionalListPage extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->data = $values['data'];$this->next = $values['next'] ?? null; + ) { + $this->data = $values['data']; + $this->next = $values['next'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/Users/Types/UserPage.php b/seed/php-sdk/pagination/no-custom-config/src/Users/Types/UserPage.php index ef42b3b40648..97418ae52e1b 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Users/Types/UserPage.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Users/Types/UserPage.php @@ -27,15 +27,16 @@ class UserPage extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->data = $values['data'];$this->next = $values['next'] ?? null; + ) { + $this->data = $values['data']; + $this->next = $values['next'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/Users/Types/UsernameContainer.php b/seed/php-sdk/pagination/no-custom-config/src/Users/Types/UsernameContainer.php index e19913b73b70..c38cebb1485f 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Users/Types/UsernameContainer.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Users/Types/UsernameContainer.php @@ -21,15 +21,15 @@ class UsernameContainer extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->results = $values['results']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/Users/Types/WithCursor.php b/seed/php-sdk/pagination/no-custom-config/src/Users/Types/WithCursor.php index a2c5c23bf783..e568f140293d 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Users/Types/WithCursor.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Users/Types/WithCursor.php @@ -20,15 +20,15 @@ class WithCursor extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->cursor = $values['cursor'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/Users/Types/WithPage.php b/seed/php-sdk/pagination/no-custom-config/src/Users/Types/WithPage.php index 7c51f871f23e..937286f0ffc3 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Users/Types/WithPage.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Users/Types/WithPage.php @@ -20,15 +20,15 @@ class WithPage extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->page = $values['page'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/no-custom-config/src/Users/UsersClient.php b/seed/php-sdk/pagination/no-custom-config/src/Users/UsersClient.php index f73e76cd84e0..c0e8b927fe3c 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Users/UsersClient.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Users/UsersClient.php @@ -28,6 +28,8 @@ use Seed\Users\Requests\ListUsernamesWithOptionalResponseRequest; use Seed\Users\Requests\ListWithGlobalConfigRequest; use Seed\Users\Types\UsernameContainer; +use Seed\Users\Requests\ListUsersOptionalDataRequest; +use Seed\Users\Types\ListUsersOptionalDataPaginationResponse; use Seed\Exceptions\SeedException; use Seed\Exceptions\SeedApiException; use Seed\Core\Json\JsonApiRequest; @@ -36,7 +38,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class UsersClient +class UsersClient { /** * @var array{ @@ -64,11 +66,10 @@ class UsersClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -85,11 +86,12 @@ function __construct( * } $options * @return Pager */ - public function listWithCursorPagination(ListUsersCursorPaginationRequest $request = new ListUsersCursorPaginationRequest(), ?array $options = null): Pager { + public function listWithCursorPagination(ListUsersCursorPaginationRequest $request = new ListUsersCursorPaginationRequest(), ?array $options = null): Pager + { return new CursorPager( request: $request, - getNextPage: fn(ListUsersCursorPaginationRequest $request) => $this->_listWithCursorPagination($request, $options), - setCursor: function (ListUsersCursorPaginationRequest $request, string $cursor) { + getNextPage: fn (ListUsersCursorPaginationRequest $request) => $this->_listWithCursorPagination($request, $options), + setCursor: function (ListUsersCursorPaginationRequest $request, string $cursor) { $request->startingAfter = $cursor; }, /* @phpstan-ignore-next-line */ @@ -111,11 +113,12 @@ public function listWithCursorPagination(ListUsersCursorPaginationRequest $reque * } $options * @return Pager */ - public function listWithMixedTypeCursorPagination(ListUsersMixedTypeCursorPaginationRequest $request = new ListUsersMixedTypeCursorPaginationRequest(), ?array $options = null): Pager { + public function listWithMixedTypeCursorPagination(ListUsersMixedTypeCursorPaginationRequest $request = new ListUsersMixedTypeCursorPaginationRequest(), ?array $options = null): Pager + { return new CursorPager( request: $request, - getNextPage: fn(ListUsersMixedTypeCursorPaginationRequest $request) => $this->_listWithMixedTypeCursorPagination($request, $options), - setCursor: function (ListUsersMixedTypeCursorPaginationRequest $request, string $cursor) { + getNextPage: fn (ListUsersMixedTypeCursorPaginationRequest $request) => $this->_listWithMixedTypeCursorPagination($request, $options), + setCursor: function (ListUsersMixedTypeCursorPaginationRequest $request, string $cursor) { $request->cursor = $cursor; }, /* @phpstan-ignore-next-line */ @@ -137,11 +140,12 @@ public function listWithMixedTypeCursorPagination(ListUsersMixedTypeCursorPagina * } $options * @return Pager */ - public function listWithBodyCursorPagination(ListUsersBodyCursorPaginationRequest $request = new ListUsersBodyCursorPaginationRequest(), ?array $options = null): Pager { + public function listWithBodyCursorPagination(ListUsersBodyCursorPaginationRequest $request = new ListUsersBodyCursorPaginationRequest(), ?array $options = null): Pager + { return new CursorPager( request: $request, - getNextPage: fn(ListUsersBodyCursorPaginationRequest $request) => $this->_listWithBodyCursorPagination($request, $options), - setCursor: function (ListUsersBodyCursorPaginationRequest $request, string $cursor) { + getNextPage: fn (ListUsersBodyCursorPaginationRequest $request) => $this->_listWithBodyCursorPagination($request, $options), + setCursor: function (ListUsersBodyCursorPaginationRequest $request, string $cursor) { PaginationHelper::setDeep($request, ["pagination", "cursor"], $cursor); }, /* @phpstan-ignore-next-line */ @@ -163,18 +167,19 @@ public function listWithBodyCursorPagination(ListUsersBodyCursorPaginationReques * } $options * @return Pager */ - public function listWithOffsetPagination(ListUsersOffsetPaginationRequest $request = new ListUsersOffsetPaginationRequest(), ?array $options = null): Pager { + public function listWithOffsetPagination(ListUsersOffsetPaginationRequest $request = new ListUsersOffsetPaginationRequest(), ?array $options = null): Pager + { return new OffsetPager( request: $request, - getNextPage: fn(ListUsersOffsetPaginationRequest $request) => $this->_listWithOffsetPagination($request, $options), + getNextPage: fn (ListUsersOffsetPaginationRequest $request) => $this->_listWithOffsetPagination($request, $options), /* @phpstan-ignore-next-line */ - getOffset: fn(ListUsersOffsetPaginationRequest $request) => $request?->page ?? 0, - setOffset: function (ListUsersOffsetPaginationRequest $request, int $offset) { + getOffset: fn (ListUsersOffsetPaginationRequest $request) => $request?->page ?? 0, + setOffset: function (ListUsersOffsetPaginationRequest $request, int $offset) { $request->page = $offset; }, getStep: null, /* @phpstan-ignore-next-line */ - getItems: fn(ListUsersPaginationResponse $response) => $response?->data ?? [], + getItems: fn (ListUsersPaginationResponse $response) => $response?->data ?? [], /* @phpstan-ignore-next-line */ hasNextPage: null, ); @@ -192,18 +197,19 @@ public function listWithOffsetPagination(ListUsersOffsetPaginationRequest $reque * } $options * @return Pager */ - public function listWithDoubleOffsetPagination(ListUsersDoubleOffsetPaginationRequest $request = new ListUsersDoubleOffsetPaginationRequest(), ?array $options = null): Pager { + public function listWithDoubleOffsetPagination(ListUsersDoubleOffsetPaginationRequest $request = new ListUsersDoubleOffsetPaginationRequest(), ?array $options = null): Pager + { return new OffsetPager( request: $request, - getNextPage: fn(ListUsersDoubleOffsetPaginationRequest $request) => $this->_listWithDoubleOffsetPagination($request, $options), + getNextPage: fn (ListUsersDoubleOffsetPaginationRequest $request) => $this->_listWithDoubleOffsetPagination($request, $options), /* @phpstan-ignore-next-line */ - getOffset: fn(ListUsersDoubleOffsetPaginationRequest $request) => $request?->page ?? 0, - setOffset: function (ListUsersDoubleOffsetPaginationRequest $request, int $offset) { + getOffset: fn (ListUsersDoubleOffsetPaginationRequest $request) => $request?->page ?? 0, + setOffset: function (ListUsersDoubleOffsetPaginationRequest $request, int $offset) { $request->page = $offset; }, getStep: null, /* @phpstan-ignore-next-line */ - getItems: fn(ListUsersPaginationResponse $response) => $response?->data ?? [], + getItems: fn (ListUsersPaginationResponse $response) => $response?->data ?? [], /* @phpstan-ignore-next-line */ hasNextPage: null, ); @@ -221,18 +227,19 @@ public function listWithDoubleOffsetPagination(ListUsersDoubleOffsetPaginationRe * } $options * @return Pager */ - public function listWithBodyOffsetPagination(ListUsersBodyOffsetPaginationRequest $request = new ListUsersBodyOffsetPaginationRequest(), ?array $options = null): Pager { + public function listWithBodyOffsetPagination(ListUsersBodyOffsetPaginationRequest $request = new ListUsersBodyOffsetPaginationRequest(), ?array $options = null): Pager + { return new OffsetPager( request: $request, - getNextPage: fn(ListUsersBodyOffsetPaginationRequest $request) => $this->_listWithBodyOffsetPagination($request, $options), + getNextPage: fn (ListUsersBodyOffsetPaginationRequest $request) => $this->_listWithBodyOffsetPagination($request, $options), /* @phpstan-ignore-next-line */ - getOffset: fn(ListUsersBodyOffsetPaginationRequest $request) => $request?->pagination?->page ?? 0, - setOffset: function (ListUsersBodyOffsetPaginationRequest $request, int $offset) { + getOffset: fn (ListUsersBodyOffsetPaginationRequest $request) => $request?->pagination?->page ?? 0, + setOffset: function (ListUsersBodyOffsetPaginationRequest $request, int $offset) { PaginationHelper::setDeep($request, ["pagination", "page"], $offset); }, getStep: null, /* @phpstan-ignore-next-line */ - getItems: fn(ListUsersPaginationResponse $response) => $response?->data ?? [], + getItems: fn (ListUsersPaginationResponse $response) => $response?->data ?? [], /* @phpstan-ignore-next-line */ hasNextPage: null, ); @@ -250,19 +257,20 @@ public function listWithBodyOffsetPagination(ListUsersBodyOffsetPaginationReques * } $options * @return Pager */ - public function listWithOffsetStepPagination(ListUsersOffsetStepPaginationRequest $request = new ListUsersOffsetStepPaginationRequest(), ?array $options = null): Pager { + public function listWithOffsetStepPagination(ListUsersOffsetStepPaginationRequest $request = new ListUsersOffsetStepPaginationRequest(), ?array $options = null): Pager + { return new OffsetPager( request: $request, - getNextPage: fn(ListUsersOffsetStepPaginationRequest $request) => $this->_listWithOffsetStepPagination($request, $options), + getNextPage: fn (ListUsersOffsetStepPaginationRequest $request) => $this->_listWithOffsetStepPagination($request, $options), /* @phpstan-ignore-next-line */ - getOffset: fn(ListUsersOffsetStepPaginationRequest $request) => $request?->page ?? 0, - setOffset: function (ListUsersOffsetStepPaginationRequest $request, int $offset) { + getOffset: fn (ListUsersOffsetStepPaginationRequest $request) => $request?->page ?? 0, + setOffset: function (ListUsersOffsetStepPaginationRequest $request, int $offset) { $request->page = $offset; }, /* @phpstan-ignore-next-line */ - getStep: fn(ListUsersOffsetStepPaginationRequest $request) => $request?->limit ?? 0, + getStep: fn (ListUsersOffsetStepPaginationRequest $request) => $request?->limit ?? 0, /* @phpstan-ignore-next-line */ - getItems: fn(ListUsersPaginationResponse $response) => $response?->data ?? [], + getItems: fn (ListUsersPaginationResponse $response) => $response?->data ?? [], /* @phpstan-ignore-next-line */ hasNextPage: null, ); @@ -280,21 +288,22 @@ public function listWithOffsetStepPagination(ListUsersOffsetStepPaginationReques * } $options * @return Pager */ - public function listWithOffsetPaginationHasNextPage(ListWithOffsetPaginationHasNextPageRequest $request = new ListWithOffsetPaginationHasNextPageRequest(), ?array $options = null): Pager { + public function listWithOffsetPaginationHasNextPage(ListWithOffsetPaginationHasNextPageRequest $request = new ListWithOffsetPaginationHasNextPageRequest(), ?array $options = null): Pager + { return new OffsetPager( request: $request, - getNextPage: fn(ListWithOffsetPaginationHasNextPageRequest $request) => $this->_listWithOffsetPaginationHasNextPage($request, $options), + getNextPage: fn (ListWithOffsetPaginationHasNextPageRequest $request) => $this->_listWithOffsetPaginationHasNextPage($request, $options), /* @phpstan-ignore-next-line */ - getOffset: fn(ListWithOffsetPaginationHasNextPageRequest $request) => $request?->page ?? 0, - setOffset: function (ListWithOffsetPaginationHasNextPageRequest $request, int $offset) { + getOffset: fn (ListWithOffsetPaginationHasNextPageRequest $request) => $request?->page ?? 0, + setOffset: function (ListWithOffsetPaginationHasNextPageRequest $request, int $offset) { $request->page = $offset; }, /* @phpstan-ignore-next-line */ - getStep: fn(ListWithOffsetPaginationHasNextPageRequest $request) => $request?->limit ?? 0, + getStep: fn (ListWithOffsetPaginationHasNextPageRequest $request) => $request?->limit ?? 0, /* @phpstan-ignore-next-line */ - getItems: fn(ListUsersPaginationResponse $response) => $response?->data ?? [], + getItems: fn (ListUsersPaginationResponse $response) => $response?->data ?? [], /* @phpstan-ignore-next-line */ - hasNextPage: fn(ListUsersPaginationResponse $response) => $response?->hasNextPage, + hasNextPage: fn (ListUsersPaginationResponse $response) => $response?->hasNextPage, ); } @@ -310,11 +319,12 @@ public function listWithOffsetPaginationHasNextPage(ListWithOffsetPaginationHasN * } $options * @return Pager */ - public function listWithExtendedResults(ListUsersExtendedRequest $request = new ListUsersExtendedRequest(), ?array $options = null): Pager { + public function listWithExtendedResults(ListUsersExtendedRequest $request = new ListUsersExtendedRequest(), ?array $options = null): Pager + { return new CursorPager( request: $request, - getNextPage: fn(ListUsersExtendedRequest $request) => $this->_listWithExtendedResults($request, $options), - setCursor: function (ListUsersExtendedRequest $request, ?string $cursor) { + getNextPage: fn (ListUsersExtendedRequest $request) => $this->_listWithExtendedResults($request, $options), + setCursor: function (ListUsersExtendedRequest $request, ?string $cursor) { $request->cursor = $cursor; }, /* @phpstan-ignore-next-line */ @@ -336,11 +346,12 @@ public function listWithExtendedResults(ListUsersExtendedRequest $request = new * } $options * @return Pager */ - public function listWithExtendedResultsAndOptionalData(ListUsersExtendedRequestForOptionalData $request = new ListUsersExtendedRequestForOptionalData(), ?array $options = null): Pager { + public function listWithExtendedResultsAndOptionalData(ListUsersExtendedRequestForOptionalData $request = new ListUsersExtendedRequestForOptionalData(), ?array $options = null): Pager + { return new CursorPager( request: $request, - getNextPage: fn(ListUsersExtendedRequestForOptionalData $request) => $this->_listWithExtendedResultsAndOptionalData($request, $options), - setCursor: function (ListUsersExtendedRequestForOptionalData $request, ?string $cursor) { + getNextPage: fn (ListUsersExtendedRequestForOptionalData $request) => $this->_listWithExtendedResultsAndOptionalData($request, $options), + setCursor: function (ListUsersExtendedRequestForOptionalData $request, ?string $cursor) { $request->cursor = $cursor; }, /* @phpstan-ignore-next-line */ @@ -362,11 +373,12 @@ public function listWithExtendedResultsAndOptionalData(ListUsersExtendedRequestF * } $options * @return Pager */ - public function listUsernames(ListUsernamesRequest $request = new ListUsernamesRequest(), ?array $options = null): Pager { + public function listUsernames(ListUsernamesRequest $request = new ListUsernamesRequest(), ?array $options = null): Pager + { return new CursorPager( request: $request, - getNextPage: fn(ListUsernamesRequest $request) => $this->_listUsernames($request, $options), - setCursor: function (ListUsernamesRequest $request, ?string $cursor) { + getNextPage: fn (ListUsernamesRequest $request) => $this->_listUsernames($request, $options), + setCursor: function (ListUsernamesRequest $request, ?string $cursor) { $request->startingAfter = $cursor; }, /* @phpstan-ignore-next-line */ @@ -388,11 +400,12 @@ public function listUsernames(ListUsernamesRequest $request = new ListUsernamesR * } $options * @return Pager */ - public function listUsernamesWithOptionalResponse(ListUsernamesWithOptionalResponseRequest $request = new ListUsernamesWithOptionalResponseRequest(), ?array $options = null): Pager { + public function listUsernamesWithOptionalResponse(ListUsernamesWithOptionalResponseRequest $request = new ListUsernamesWithOptionalResponseRequest(), ?array $options = null): Pager + { return new CursorPager( request: $request, - getNextPage: fn(ListUsernamesWithOptionalResponseRequest $request) => $this->_listUsernamesWithOptionalResponse($request, $options), - setCursor: function (ListUsernamesWithOptionalResponseRequest $request, ?string $cursor) { + getNextPage: fn (ListUsernamesWithOptionalResponseRequest $request) => $this->_listUsernamesWithOptionalResponse($request, $options), + setCursor: function (ListUsernamesWithOptionalResponseRequest $request, ?string $cursor) { $request->startingAfter = $cursor; }, /* @phpstan-ignore-next-line */ @@ -414,18 +427,49 @@ public function listUsernamesWithOptionalResponse(ListUsernamesWithOptionalRespo * } $options * @return Pager */ - public function listWithGlobalConfig(ListWithGlobalConfigRequest $request = new ListWithGlobalConfigRequest(), ?array $options = null): Pager { + public function listWithGlobalConfig(ListWithGlobalConfigRequest $request = new ListWithGlobalConfigRequest(), ?array $options = null): Pager + { return new OffsetPager( request: $request, - getNextPage: fn(ListWithGlobalConfigRequest $request) => $this->_listWithGlobalConfig($request, $options), + getNextPage: fn (ListWithGlobalConfigRequest $request) => $this->_listWithGlobalConfig($request, $options), /* @phpstan-ignore-next-line */ - getOffset: fn(ListWithGlobalConfigRequest $request) => $request?->offset ?? 0, - setOffset: function (ListWithGlobalConfigRequest $request, int $offset) { + getOffset: fn (ListWithGlobalConfigRequest $request) => $request?->offset ?? 0, + setOffset: function (ListWithGlobalConfigRequest $request, int $offset) { $request->offset = $offset; }, getStep: null, /* @phpstan-ignore-next-line */ - getItems: fn(UsernameContainer $response) => $response?->results ?? [], + getItems: fn (UsernameContainer $response) => $response?->results ?? [], + /* @phpstan-ignore-next-line */ + hasNextPage: null, + ); + } + + /** + * @param ListUsersOptionalDataRequest $request + * @param ?array{ + * baseUrl?: string, + * maxRetries?: int, + * timeout?: float, + * headers?: array, + * queryParameters?: array, + * bodyProperties?: array, + * } $options + * @return Pager + */ + public function listWithOptionalData(ListUsersOptionalDataRequest $request = new ListUsersOptionalDataRequest(), ?array $options = null): Pager + { + return new OffsetPager( + request: $request, + getNextPage: fn (ListUsersOptionalDataRequest $request) => $this->_listWithOptionalData($request, $options), + /* @phpstan-ignore-next-line */ + getOffset: fn (ListUsersOptionalDataRequest $request) => $request?->page ?? 0, + setOffset: function (ListUsersOptionalDataRequest $request, int $offset) { + $request->page = $offset; + }, + getStep: null, + /* @phpstan-ignore-next-line */ + getItems: fn (ListUsersOptionalDataPaginationResponse $response) => $response?->data ?? [], /* @phpstan-ignore-next-line */ hasNextPage: null, ); @@ -445,19 +489,20 @@ public function listWithGlobalConfig(ListWithGlobalConfigRequest $request = new * @throws SeedException * @throws SeedApiException */ - private function _listWithCursorPagination(ListUsersCursorPaginationRequest $request = new ListUsersCursorPaginationRequest(), ?array $options = null): ListUsersPaginationResponse { + private function _listWithCursorPagination(ListUsersCursorPaginationRequest $request = new ListUsersCursorPaginationRequest(), ?array $options = null): ListUsersPaginationResponse + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->page != null){ + if ($request->page != null) { $query['page'] = $request->page; } - if ($request->perPage != null){ + if ($request->perPage != null) { $query['per_page'] = $request->perPage; } - if ($request->order != null){ + if ($request->order != null) { $query['order'] = $request->order; } - if ($request->startingAfter != null){ + if ($request->startingAfter != null) { $query['starting_after'] = $request->startingAfter; } try { @@ -471,15 +516,15 @@ private function _listWithCursorPagination(ListUsersCursorPaginationRequest $req $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ListUsersPaginationResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -511,10 +556,11 @@ private function _listWithCursorPagination(ListUsersCursorPaginationRequest $req * @throws SeedException * @throws SeedApiException */ - private function _listWithMixedTypeCursorPagination(ListUsersMixedTypeCursorPaginationRequest $request = new ListUsersMixedTypeCursorPaginationRequest(), ?array $options = null): ListUsersMixedTypePaginationResponse { + private function _listWithMixedTypeCursorPagination(ListUsersMixedTypeCursorPaginationRequest $request = new ListUsersMixedTypeCursorPaginationRequest(), ?array $options = null): ListUsersMixedTypePaginationResponse + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->cursor != null){ + if ($request->cursor != null) { $query['cursor'] = $request->cursor; } try { @@ -528,15 +574,15 @@ private function _listWithMixedTypeCursorPagination(ListUsersMixedTypeCursorPagi $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ListUsersMixedTypePaginationResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -568,7 +614,8 @@ private function _listWithMixedTypeCursorPagination(ListUsersMixedTypeCursorPagi * @throws SeedException * @throws SeedApiException */ - private function _listWithBodyCursorPagination(ListUsersBodyCursorPaginationRequest $request = new ListUsersBodyCursorPaginationRequest(), ?array $options = null): ListUsersPaginationResponse { + private function _listWithBodyCursorPagination(ListUsersBodyCursorPaginationRequest $request = new ListUsersBodyCursorPaginationRequest(), ?array $options = null): ListUsersPaginationResponse + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -581,15 +628,15 @@ private function _listWithBodyCursorPagination(ListUsersBodyCursorPaginationRequ $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ListUsersPaginationResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -621,19 +668,20 @@ private function _listWithBodyCursorPagination(ListUsersBodyCursorPaginationRequ * @throws SeedException * @throws SeedApiException */ - private function _listWithOffsetPagination(ListUsersOffsetPaginationRequest $request = new ListUsersOffsetPaginationRequest(), ?array $options = null): ListUsersPaginationResponse { + private function _listWithOffsetPagination(ListUsersOffsetPaginationRequest $request = new ListUsersOffsetPaginationRequest(), ?array $options = null): ListUsersPaginationResponse + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->page != null){ + if ($request->page != null) { $query['page'] = $request->page; } - if ($request->perPage != null){ + if ($request->perPage != null) { $query['per_page'] = $request->perPage; } - if ($request->order != null){ + if ($request->order != null) { $query['order'] = $request->order; } - if ($request->startingAfter != null){ + if ($request->startingAfter != null) { $query['starting_after'] = $request->startingAfter; } try { @@ -647,15 +695,15 @@ private function _listWithOffsetPagination(ListUsersOffsetPaginationRequest $req $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ListUsersPaginationResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -687,19 +735,20 @@ private function _listWithOffsetPagination(ListUsersOffsetPaginationRequest $req * @throws SeedException * @throws SeedApiException */ - private function _listWithDoubleOffsetPagination(ListUsersDoubleOffsetPaginationRequest $request = new ListUsersDoubleOffsetPaginationRequest(), ?array $options = null): ListUsersPaginationResponse { + private function _listWithDoubleOffsetPagination(ListUsersDoubleOffsetPaginationRequest $request = new ListUsersDoubleOffsetPaginationRequest(), ?array $options = null): ListUsersPaginationResponse + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->page != null){ + if ($request->page != null) { $query['page'] = $request->page; } - if ($request->perPage != null){ + if ($request->perPage != null) { $query['per_page'] = $request->perPage; } - if ($request->order != null){ + if ($request->order != null) { $query['order'] = $request->order; } - if ($request->startingAfter != null){ + if ($request->startingAfter != null) { $query['starting_after'] = $request->startingAfter; } try { @@ -713,15 +762,15 @@ private function _listWithDoubleOffsetPagination(ListUsersDoubleOffsetPagination $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ListUsersPaginationResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -753,7 +802,8 @@ private function _listWithDoubleOffsetPagination(ListUsersDoubleOffsetPagination * @throws SeedException * @throws SeedApiException */ - private function _listWithBodyOffsetPagination(ListUsersBodyOffsetPaginationRequest $request = new ListUsersBodyOffsetPaginationRequest(), ?array $options = null): ListUsersPaginationResponse { + private function _listWithBodyOffsetPagination(ListUsersBodyOffsetPaginationRequest $request = new ListUsersBodyOffsetPaginationRequest(), ?array $options = null): ListUsersPaginationResponse + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -766,15 +816,15 @@ private function _listWithBodyOffsetPagination(ListUsersBodyOffsetPaginationRequ $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ListUsersPaginationResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -806,16 +856,17 @@ private function _listWithBodyOffsetPagination(ListUsersBodyOffsetPaginationRequ * @throws SeedException * @throws SeedApiException */ - private function _listWithOffsetStepPagination(ListUsersOffsetStepPaginationRequest $request = new ListUsersOffsetStepPaginationRequest(), ?array $options = null): ListUsersPaginationResponse { + private function _listWithOffsetStepPagination(ListUsersOffsetStepPaginationRequest $request = new ListUsersOffsetStepPaginationRequest(), ?array $options = null): ListUsersPaginationResponse + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->page != null){ + if ($request->page != null) { $query['page'] = $request->page; } - if ($request->limit != null){ + if ($request->limit != null) { $query['limit'] = $request->limit; } - if ($request->order != null){ + if ($request->order != null) { $query['order'] = $request->order; } try { @@ -829,15 +880,15 @@ private function _listWithOffsetStepPagination(ListUsersOffsetStepPaginationRequ $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ListUsersPaginationResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -869,16 +920,17 @@ private function _listWithOffsetStepPagination(ListUsersOffsetStepPaginationRequ * @throws SeedException * @throws SeedApiException */ - private function _listWithOffsetPaginationHasNextPage(ListWithOffsetPaginationHasNextPageRequest $request = new ListWithOffsetPaginationHasNextPageRequest(), ?array $options = null): ListUsersPaginationResponse { + private function _listWithOffsetPaginationHasNextPage(ListWithOffsetPaginationHasNextPageRequest $request = new ListWithOffsetPaginationHasNextPageRequest(), ?array $options = null): ListUsersPaginationResponse + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->page != null){ + if ($request->page != null) { $query['page'] = $request->page; } - if ($request->limit != null){ + if ($request->limit != null) { $query['limit'] = $request->limit; } - if ($request->order != null){ + if ($request->order != null) { $query['order'] = $request->order; } try { @@ -892,15 +944,15 @@ private function _listWithOffsetPaginationHasNextPage(ListWithOffsetPaginationHa $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ListUsersPaginationResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -932,10 +984,11 @@ private function _listWithOffsetPaginationHasNextPage(ListWithOffsetPaginationHa * @throws SeedException * @throws SeedApiException */ - private function _listWithExtendedResults(ListUsersExtendedRequest $request = new ListUsersExtendedRequest(), ?array $options = null): ListUsersExtendedResponse { + private function _listWithExtendedResults(ListUsersExtendedRequest $request = new ListUsersExtendedRequest(), ?array $options = null): ListUsersExtendedResponse + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->cursor != null){ + if ($request->cursor != null) { $query['cursor'] = $request->cursor; } try { @@ -949,15 +1002,15 @@ private function _listWithExtendedResults(ListUsersExtendedRequest $request = ne $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ListUsersExtendedResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -989,10 +1042,11 @@ private function _listWithExtendedResults(ListUsersExtendedRequest $request = ne * @throws SeedException * @throws SeedApiException */ - private function _listWithExtendedResultsAndOptionalData(ListUsersExtendedRequestForOptionalData $request = new ListUsersExtendedRequestForOptionalData(), ?array $options = null): ListUsersExtendedOptionalListResponse { + private function _listWithExtendedResultsAndOptionalData(ListUsersExtendedRequestForOptionalData $request = new ListUsersExtendedRequestForOptionalData(), ?array $options = null): ListUsersExtendedOptionalListResponse + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->cursor != null){ + if ($request->cursor != null) { $query['cursor'] = $request->cursor; } try { @@ -1006,15 +1060,15 @@ private function _listWithExtendedResultsAndOptionalData(ListUsersExtendedReques $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ListUsersExtendedOptionalListResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -1046,10 +1100,11 @@ private function _listWithExtendedResultsAndOptionalData(ListUsersExtendedReques * @throws SeedException * @throws SeedApiException */ - private function _listUsernames(ListUsernamesRequest $request = new ListUsernamesRequest(), ?array $options = null): UsernameCursor { + private function _listUsernames(ListUsernamesRequest $request = new ListUsernamesRequest(), ?array $options = null): UsernameCursor + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->startingAfter != null){ + if ($request->startingAfter != null) { $query['starting_after'] = $request->startingAfter; } try { @@ -1063,15 +1118,15 @@ private function _listUsernames(ListUsernamesRequest $request = new ListUsername $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return UsernameCursor::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -1103,10 +1158,11 @@ private function _listUsernames(ListUsernamesRequest $request = new ListUsername * @throws SeedException * @throws SeedApiException */ - private function _listUsernamesWithOptionalResponse(ListUsernamesWithOptionalResponseRequest $request = new ListUsernamesWithOptionalResponseRequest(), ?array $options = null): ?UsernameCursor { + private function _listUsernamesWithOptionalResponse(ListUsernamesWithOptionalResponseRequest $request = new ListUsernamesWithOptionalResponseRequest(), ?array $options = null): ?UsernameCursor + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->startingAfter != null){ + if ($request->startingAfter != null) { $query['starting_after'] = $request->startingAfter; } try { @@ -1120,18 +1176,18 @@ private function _listUsernamesWithOptionalResponse(ListUsernamesWithOptionalRes $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); - if (empty($json)){ + if (empty($json)) { return null; } return UsernameCursor::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -1163,10 +1219,11 @@ private function _listUsernamesWithOptionalResponse(ListUsernamesWithOptionalRes * @throws SeedException * @throws SeedApiException */ - private function _listWithGlobalConfig(ListWithGlobalConfigRequest $request = new ListWithGlobalConfigRequest(), ?array $options = null): UsernameContainer { + private function _listWithGlobalConfig(ListWithGlobalConfigRequest $request = new ListWithGlobalConfigRequest(), ?array $options = null): UsernameContainer + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->offset != null){ + if ($request->offset != null) { $query['offset'] = $request->offset; } try { @@ -1180,15 +1237,73 @@ private function _listWithGlobalConfig(ListWithGlobalConfigRequest $request = ne $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return UsernameContainer::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (RequestException $e) { + $response = $e->getResponse(); + if ($response === null) { + throw new SeedException(message: $e->getMessage(), previous: $e); + } + throw new SeedApiException( + message: "API request failed", + statusCode: $response->getStatusCode(), + body: $response->getBody()->getContents(), + ); + } catch (ClientExceptionInterface $e) { + throw new SeedException(message: $e->getMessage(), previous: $e); + } + throw new SeedApiException( + message: 'API request failed', + statusCode: $statusCode, + body: $response->getBody()->getContents(), + ); + } + + /** + * @param ListUsersOptionalDataRequest $request + * @param ?array{ + * baseUrl?: string, + * maxRetries?: int, + * timeout?: float, + * headers?: array, + * queryParameters?: array, + * bodyProperties?: array, + * } $options + * @return ListUsersOptionalDataPaginationResponse + * @throws SeedException + * @throws SeedApiException + */ + private function _listWithOptionalData(ListUsersOptionalDataRequest $request = new ListUsersOptionalDataRequest(), ?array $options = null): ListUsersOptionalDataPaginationResponse + { + $options = array_merge($this->options, $options ?? []); + $query = []; + if ($request->page != null) { + $query['page'] = $request->page; + } + try { + $response = $this->client->sendRequest( + new JsonApiRequest( + baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '', + path: "/users/optional-data", + method: HttpMethod::GET, + query: $query, + ), + $options, + ); + $statusCode = $response->getStatusCode(); + if ($statusCode >= 200 && $statusCode < 400) { + $json = $response->getBody()->getContents(); + return ListUsersOptionalDataPaginationResponse::fromJson($json); + } + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/pagination/no-custom-config/src/Utils/File.php b/seed/php-sdk/pagination/no-custom-config/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/pagination/no-custom-config/src/Utils/File.php +++ b/seed/php-sdk/pagination/no-custom-config/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination/no-custom-config/src/dynamic-snippets/example26/snippet.php b/seed/php-sdk/pagination/no-custom-config/src/dynamic-snippets/example26/snippet.php new file mode 100644 index 000000000000..136421c43075 --- /dev/null +++ b/seed/php-sdk/pagination/no-custom-config/src/dynamic-snippets/example26/snippet.php @@ -0,0 +1,18 @@ +', + options: [ + 'baseUrl' => 'https://api.fern.com', + ], +); +$client->users->listWithOptionalData( + new ListUsersOptionalDataRequest([ + 'page' => 1, + ]), +); diff --git a/seed/php-sdk/pagination/no-custom-config/src/dynamic-snippets/example27/snippet.php b/seed/php-sdk/pagination/no-custom-config/src/dynamic-snippets/example27/snippet.php new file mode 100644 index 000000000000..136421c43075 --- /dev/null +++ b/seed/php-sdk/pagination/no-custom-config/src/dynamic-snippets/example27/snippet.php @@ -0,0 +1,18 @@ +', + options: [ + 'baseUrl' => 'https://api.fern.com', + ], +); +$client->users->listWithOptionalData( + new ListUsersOptionalDataRequest([ + 'page' => 1, + ]), +); diff --git a/seed/php-sdk/pagination/no-custom-config/src/dynamic-snippets/example28/snippet.php b/seed/php-sdk/pagination/no-custom-config/src/dynamic-snippets/example28/snippet.php new file mode 100644 index 000000000000..136421c43075 --- /dev/null +++ b/seed/php-sdk/pagination/no-custom-config/src/dynamic-snippets/example28/snippet.php @@ -0,0 +1,18 @@ +', + options: [ + 'baseUrl' => 'https://api.fern.com', + ], +); +$client->users->listWithOptionalData( + new ListUsersOptionalDataRequest([ + 'page' => 1, + ]), +); diff --git a/seed/php-sdk/pagination/no-custom-config/tests/Core/Client/RawClientTest.php b/seed/php-sdk/pagination/no-custom-config/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/pagination/no-custom-config/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/pagination/no-custom-config/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/pagination/no-custom-config/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/pagination/no-custom-config/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/pagination/no-custom-config/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/pagination/no-custom-config/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination/no-custom-config/tests/Core/Json/EnumTest.php b/seed/php-sdk/pagination/no-custom-config/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/pagination/no-custom-config/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/pagination/no-custom-config/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination/no-custom-config/tests/Core/Json/TraitTest.php b/seed/php-sdk/pagination/no-custom-config/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/pagination/no-custom-config/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/pagination/no-custom-config/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination/no-custom-config/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/pagination/no-custom-config/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/pagination/no-custom-config/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/pagination/no-custom-config/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination/no-custom-config/tests/Core/Pagination/CursorPagerTest/CursorPagerTest.php b/seed/php-sdk/pagination/no-custom-config/tests/Core/Pagination/CursorPagerTest/CursorPagerTest.php index fd9eda9faf6c..8c5ac6b6fd59 100644 --- a/seed/php-sdk/pagination/no-custom-config/tests/Core/Pagination/CursorPagerTest/CursorPagerTest.php +++ b/seed/php-sdk/pagination/no-custom-config/tests/Core/Pagination/CursorPagerTest/CursorPagerTest.php @@ -93,8 +93,8 @@ function (Request $request, ?string $cursor) { $request->cursor = $cursor; $this->cursorCopy = $cursor; }, - fn(Response $response) => $response->next->cursor ?? null, - fn(Response $response) => $response->items ?? [] + fn (Response $response) => $response->next->cursor ?? null, + fn (Response $response) => $response->items ?? [] ); } @@ -129,4 +129,4 @@ private function assertPager(Pager $pager): void $pages->next(); $this->assertNull($pages->current()); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination/no-custom-config/tests/Core/Pagination/DeepSetAccessorsTest.php b/seed/php-sdk/pagination/no-custom-config/tests/Core/Pagination/DeepSetAccessorsTest.php index a21e7201abb9..73ed5401292f 100644 --- a/seed/php-sdk/pagination/no-custom-config/tests/Core/Pagination/DeepSetAccessorsTest.php +++ b/seed/php-sdk/pagination/no-custom-config/tests/Core/Pagination/DeepSetAccessorsTest.php @@ -98,4 +98,4 @@ public function testSetNestedProperty(): void $this->assertEquals('testValue', $object->getLevel1()?->getLevel2()?->getLevel3()); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination/no-custom-config/tests/Core/Pagination/GeneratorPagerTest/GeneratorPagerTest.php b/seed/php-sdk/pagination/no-custom-config/tests/Core/Pagination/GeneratorPagerTest/GeneratorPagerTest.php index 8ada5c5edeb2..47c12833b1a7 100644 --- a/seed/php-sdk/pagination/no-custom-config/tests/Core/Pagination/GeneratorPagerTest/GeneratorPagerTest.php +++ b/seed/php-sdk/pagination/no-custom-config/tests/Core/Pagination/GeneratorPagerTest/GeneratorPagerTest.php @@ -69,7 +69,7 @@ private function createPager(): Pager new Response(new Data([])), ]); - return new class($responses) extends Pager { + return new class ($responses) extends Pager { /** * @var ArrayIterator */ diff --git a/seed/php-sdk/pagination/no-custom-config/tests/Core/Pagination/HasNextPageOffsetPagerTest/HasNextPageOffsetPagerTest.php b/seed/php-sdk/pagination/no-custom-config/tests/Core/Pagination/HasNextPageOffsetPagerTest/HasNextPageOffsetPagerTest.php index 7e96cb22ec9c..f04cc27903c3 100644 --- a/seed/php-sdk/pagination/no-custom-config/tests/Core/Pagination/HasNextPageOffsetPagerTest/HasNextPageOffsetPagerTest.php +++ b/seed/php-sdk/pagination/no-custom-config/tests/Core/Pagination/HasNextPageOffsetPagerTest/HasNextPageOffsetPagerTest.php @@ -81,16 +81,16 @@ function (Request $request) use ($responses) { $responses->next(); return $response; }, - fn(Request $request) => $request->pagination?->page ?? 0, + fn (Request $request) => $request->pagination?->page ?? 0, function (Request $request, int $offset) { - if($request->pagination === null) { + if ($request->pagination === null) { $request->pagination = new Pagination(0); } $request->pagination->page = $offset; }, null, - fn(Response $response) => $response->data->items, - fn(Response $response) => $response->hasNext + fn (Response $response) => $response->data->items, + fn (Response $response) => $response->hasNext ); } @@ -102,9 +102,9 @@ private function assertPager(Pager $pager): void { $pages = iterator_to_array($pager->getPages()); $pageCounter = count($pages); - $itemCounter = array_reduce($pages, fn($carry, $page) => $carry + count($page->getItems()), 0); + $itemCounter = array_reduce($pages, fn ($carry, $page) => $carry + count($page->getItems()), 0); $this->assertEquals(3, $pageCounter); $this->assertEquals(5, $itemCounter); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination/no-custom-config/tests/Core/Pagination/IntOffsetPagerTest/IntOffsetPagerTest.php b/seed/php-sdk/pagination/no-custom-config/tests/Core/Pagination/IntOffsetPagerTest/IntOffsetPagerTest.php index 71d2343f78e4..226ca279d28a 100644 --- a/seed/php-sdk/pagination/no-custom-config/tests/Core/Pagination/IntOffsetPagerTest/IntOffsetPagerTest.php +++ b/seed/php-sdk/pagination/no-custom-config/tests/Core/Pagination/IntOffsetPagerTest/IntOffsetPagerTest.php @@ -79,7 +79,7 @@ function (Request $request) use ($responses) { $responses->next(); return $response; }, - fn(Request $request) => $request->pagination?->page ?? 0, + fn (Request $request) => $request->pagination?->page ?? 0, function (Request $request, int $offset) { if ($request->pagination === null) { $request->pagination = new Pagination(0); @@ -87,7 +87,7 @@ function (Request $request, int $offset) { $request->pagination->page = $offset; }, null, - fn(Response $response) => $response->data->items, + fn (Response $response) => $response->data->items, null ); } @@ -100,9 +100,9 @@ private function assertPager(Pager $pager): void { $pages = iterator_to_array($pager->getPages()); $pageCounter = count($pages); - $itemCounter = array_reduce($pages, fn($carry, $page) => $carry + count($page->getItems()), 0); + $itemCounter = array_reduce($pages, fn ($carry, $page) => $carry + count($page->getItems()), 0); $this->assertEquals(3, $pageCounter); $this->assertEquals(3, $itemCounter); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination/no-custom-config/tests/Core/Pagination/StepOffsetPagerTest/StepOffsetPagerTest.php b/seed/php-sdk/pagination/no-custom-config/tests/Core/Pagination/StepOffsetPagerTest/StepOffsetPagerTest.php index 79b0f27868b0..d44cdc364418 100644 --- a/seed/php-sdk/pagination/no-custom-config/tests/Core/Pagination/StepOffsetPagerTest/StepOffsetPagerTest.php +++ b/seed/php-sdk/pagination/no-custom-config/tests/Core/Pagination/StepOffsetPagerTest/StepOffsetPagerTest.php @@ -85,7 +85,7 @@ function (Request $request) use ($responses) { $responses->next(); return $response; }, - fn(Request $request) => $request->pagination?->itemOffset ?? 0, + fn (Request $request) => $request->pagination?->itemOffset ?? 0, function (Request $request, int $offset) { if ($request->pagination === null) { $request->pagination = new Pagination(0, 2); @@ -93,8 +93,8 @@ function (Request $request, int $offset) { $request->pagination->itemOffset = $offset; $this->paginationCopy = $request->pagination; }, - fn(Request $request) => $request->pagination?->pageSize, - fn(Response $response) => $response->data->items, + fn (Request $request) => $request->pagination?->pageSize, + fn (Response $response) => $response->data->items, null ); } @@ -128,4 +128,4 @@ private function assertPager(Pager $pager): void $pages->next(); $this->assertNull($pages->current()); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination/property-accessors/reference.md b/seed/php-sdk/pagination/property-accessors/reference.md index 4a001318d75f..ef4e8820de25 100644 --- a/seed/php-sdk/pagination/property-accessors/reference.md +++ b/seed/php-sdk/pagination/property-accessors/reference.md @@ -1440,6 +1440,50 @@ $client->users->listWithGlobalConfig( + + + + +
$client->users->listWithOptionalData($request) -> ListUsersOptionalDataPaginationResponse +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```php +$client->users->listWithOptionalData( + new ListUsersOptionalDataRequest([ + 'page' => 1, + ]), +); +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**$page:** `?int` — Defaults to first page + +
+
+
+
+ +
diff --git a/seed/php-sdk/pagination/property-accessors/src/Complex/ComplexClient.php b/seed/php-sdk/pagination/property-accessors/src/Complex/ComplexClient.php index 84fcee5bcd6a..b7154f6ef6b3 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Complex/ComplexClient.php +++ b/seed/php-sdk/pagination/property-accessors/src/Complex/ComplexClient.php @@ -18,7 +18,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class ComplexClient +class ComplexClient { /** * @var array{ @@ -46,11 +46,10 @@ class ComplexClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -68,11 +67,12 @@ function __construct( * } $options * @return Pager */ - public function search(string $index, SearchRequest $request, ?array $options = null): Pager { + public function search(string $index, SearchRequest $request, ?array $options = null): Pager + { return new CursorPager( request: $request, - getNextPage: fn(SearchRequest $request) => $this->_search($index, $request, $options), - setCursor: function (SearchRequest $request, ?string $cursor) { + getNextPage: fn (SearchRequest $request) => $this->_search($index, $request, $options), + setCursor: function (SearchRequest $request, ?string $cursor) { PaginationHelper::setDeep($request, ["pagination", "startingAfter"], $cursor); }, /* @phpstan-ignore-next-line */ @@ -97,7 +97,8 @@ public function search(string $index, SearchRequest $request, ?array $options = * @throws SeedException * @throws SeedApiException */ - private function _search(string $index, SearchRequest $request, ?array $options = null): PaginatedConversationResponse { + private function _search(string $index, SearchRequest $request, ?array $options = null): PaginatedConversationResponse + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -110,15 +111,15 @@ private function _search(string $index, SearchRequest $request, ?array $options $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return PaginatedConversationResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/pagination/property-accessors/src/Complex/Types/Conversation.php b/seed/php-sdk/pagination/property-accessors/src/Complex/Types/Conversation.php index 38e44bf7a158..c1c9a411c129 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Complex/Types/Conversation.php +++ b/seed/php-sdk/pagination/property-accessors/src/Complex/Types/Conversation.php @@ -20,27 +20,32 @@ class Conversation extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->foo = $values['foo']; } /** * @return string */ - public function getFoo(): string { - return $this->foo;} + public function getFoo(): string + { + return $this->foo; + } /** * @param string $value */ - public function setFoo(string $value): self { - $this->foo = $value;return $this;} + public function setFoo(string $value): self + { + $this->foo = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/property-accessors/src/Complex/Types/CursorPages.php b/seed/php-sdk/pagination/property-accessors/src/Complex/Types/CursorPages.php index d6c409abf4b9..14137c9d1473 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Complex/Types/CursorPages.php +++ b/seed/php-sdk/pagination/property-accessors/src/Complex/Types/CursorPages.php @@ -48,75 +48,104 @@ class CursorPages extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->next = $values['next'] ?? null;$this->page = $values['page'] ?? null;$this->perPage = $values['perPage'] ?? null;$this->totalPages = $values['totalPages'] ?? null;$this->type = $values['type']; + ) { + $this->next = $values['next'] ?? null; + $this->page = $values['page'] ?? null; + $this->perPage = $values['perPage'] ?? null; + $this->totalPages = $values['totalPages'] ?? null; + $this->type = $values['type']; } /** * @return ?StartingAfterPaging */ - public function getNext(): ?StartingAfterPaging { - return $this->next;} + public function getNext(): ?StartingAfterPaging + { + return $this->next; + } /** * @param ?StartingAfterPaging $value */ - public function setNext(?StartingAfterPaging $value = null): self { - $this->next = $value;return $this;} + public function setNext(?StartingAfterPaging $value = null): self + { + $this->next = $value; + return $this; + } /** * @return ?int */ - public function getPage(): ?int { - return $this->page;} + public function getPage(): ?int + { + return $this->page; + } /** * @param ?int $value */ - public function setPage(?int $value = null): self { - $this->page = $value;return $this;} + public function setPage(?int $value = null): self + { + $this->page = $value; + return $this; + } /** * @return ?int */ - public function getPerPage(): ?int { - return $this->perPage;} + public function getPerPage(): ?int + { + return $this->perPage; + } /** * @param ?int $value */ - public function setPerPage(?int $value = null): self { - $this->perPage = $value;return $this;} + public function setPerPage(?int $value = null): self + { + $this->perPage = $value; + return $this; + } /** * @return ?int */ - public function getTotalPages(): ?int { - return $this->totalPages;} + public function getTotalPages(): ?int + { + return $this->totalPages; + } /** * @param ?int $value */ - public function setTotalPages(?int $value = null): self { - $this->totalPages = $value;return $this;} + public function setTotalPages(?int $value = null): self + { + $this->totalPages = $value; + return $this; + } /** * @return 'pages' */ - public function getType(): string { - return $this->type;} + public function getType(): string + { + return $this->type; + } /** * @param 'pages' $value */ - public function setType(string $value): self { - $this->type = $value;return $this;} + public function setType(string $value): self + { + $this->type = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/property-accessors/src/Complex/Types/MultipleFilterSearchRequest.php b/seed/php-sdk/pagination/property-accessors/src/Complex/Types/MultipleFilterSearchRequest.php index 1080ffa5739b..35b09bfbf8ea 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Complex/Types/MultipleFilterSearchRequest.php +++ b/seed/php-sdk/pagination/property-accessors/src/Complex/Types/MultipleFilterSearchRequest.php @@ -20,7 +20,7 @@ class MultipleFilterSearchRequest extends JsonSerializableType * |array * )|null $value */ - #[JsonProperty('value'), Union([MultipleFilterSearchRequest::class],[SingleFilterSearchRequest::class],'null')] + #[JsonProperty('value'), Union([MultipleFilterSearchRequest::class], [SingleFilterSearchRequest::class], 'null')] private array|null $value; /** @@ -34,22 +34,27 @@ class MultipleFilterSearchRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->operator = $values['operator'] ?? null;$this->value = $values['value'] ?? null; + ) { + $this->operator = $values['operator'] ?? null; + $this->value = $values['value'] ?? null; } /** * @return ?value-of */ - public function getOperator(): ?string { - return $this->operator;} + public function getOperator(): ?string + { + return $this->operator; + } /** * @param ?value-of $value */ - public function setOperator(?string $value = null): self { - $this->operator = $value;return $this;} + public function setOperator(?string $value = null): self + { + $this->operator = $value; + return $this; + } /** * @return ( @@ -57,8 +62,10 @@ public function setOperator(?string $value = null): self { * |array * )|null */ - public function getValue(): array|null { - return $this->value;} + public function getValue(): array|null + { + return $this->value; + } /** * @param ( @@ -66,13 +73,17 @@ public function getValue(): array|null { * |array * )|null $value */ - public function setValue(array|null $value = null): self { - $this->value = $value;return $this;} + public function setValue(array|null $value = null): self + { + $this->value = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/property-accessors/src/Complex/Types/MultipleFilterSearchRequestOperator.php b/seed/php-sdk/pagination/property-accessors/src/Complex/Types/MultipleFilterSearchRequestOperator.php index 0b84a2b1691d..ff40bc3326f9 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Complex/Types/MultipleFilterSearchRequestOperator.php +++ b/seed/php-sdk/pagination/property-accessors/src/Complex/Types/MultipleFilterSearchRequestOperator.php @@ -2,8 +2,8 @@ namespace Seed\Complex\Types; -enum MultipleFilterSearchRequestOperator - : string { +enum MultipleFilterSearchRequestOperator: string +{ case And_ = "AND"; case Or_ = "OR"; } diff --git a/seed/php-sdk/pagination/property-accessors/src/Complex/Types/PaginatedConversationResponse.php b/seed/php-sdk/pagination/property-accessors/src/Complex/Types/PaginatedConversationResponse.php index 5ea7f5dd2ae1..aff01973c359 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Complex/Types/PaginatedConversationResponse.php +++ b/seed/php-sdk/pagination/property-accessors/src/Complex/Types/PaginatedConversationResponse.php @@ -42,63 +42,86 @@ class PaginatedConversationResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->conversations = $values['conversations'];$this->pages = $values['pages'] ?? null;$this->totalCount = $values['totalCount'];$this->type = $values['type']; + ) { + $this->conversations = $values['conversations']; + $this->pages = $values['pages'] ?? null; + $this->totalCount = $values['totalCount']; + $this->type = $values['type']; } /** * @return array */ - public function getConversations(): array { - return $this->conversations;} + public function getConversations(): array + { + return $this->conversations; + } /** * @param array $value */ - public function setConversations(array $value): self { - $this->conversations = $value;return $this;} + public function setConversations(array $value): self + { + $this->conversations = $value; + return $this; + } /** * @return ?CursorPages */ - public function getPages(): ?CursorPages { - return $this->pages;} + public function getPages(): ?CursorPages + { + return $this->pages; + } /** * @param ?CursorPages $value */ - public function setPages(?CursorPages $value = null): self { - $this->pages = $value;return $this;} + public function setPages(?CursorPages $value = null): self + { + $this->pages = $value; + return $this; + } /** * @return int */ - public function getTotalCount(): int { - return $this->totalCount;} + public function getTotalCount(): int + { + return $this->totalCount; + } /** * @param int $value */ - public function setTotalCount(int $value): self { - $this->totalCount = $value;return $this;} + public function setTotalCount(int $value): self + { + $this->totalCount = $value; + return $this; + } /** * @return 'conversation.list' */ - public function getType(): string { - return $this->type;} + public function getType(): string + { + return $this->type; + } /** * @param 'conversation.list' $value */ - public function setType(string $value): self { - $this->type = $value;return $this;} + public function setType(string $value): self + { + $this->type = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/property-accessors/src/Complex/Types/SearchRequest.php b/seed/php-sdk/pagination/property-accessors/src/Complex/Types/SearchRequest.php index 7b25f1ed4494..765c91dcdc12 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Complex/Types/SearchRequest.php +++ b/seed/php-sdk/pagination/property-accessors/src/Complex/Types/SearchRequest.php @@ -20,7 +20,7 @@ class SearchRequest extends JsonSerializableType * |MultipleFilterSearchRequest * ) $query */ - #[JsonProperty('query'), Union(SingleFilterSearchRequest::class,MultipleFilterSearchRequest::class)] + #[JsonProperty('query'), Union(SingleFilterSearchRequest::class, MultipleFilterSearchRequest::class)] private SingleFilterSearchRequest|MultipleFilterSearchRequest $query; /** @@ -34,22 +34,27 @@ class SearchRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->pagination = $values['pagination'] ?? null;$this->query = $values['query']; + ) { + $this->pagination = $values['pagination'] ?? null; + $this->query = $values['query']; } /** * @return ?StartingAfterPaging */ - public function getPagination(): ?StartingAfterPaging { - return $this->pagination;} + public function getPagination(): ?StartingAfterPaging + { + return $this->pagination; + } /** * @param ?StartingAfterPaging $value */ - public function setPagination(?StartingAfterPaging $value = null): self { - $this->pagination = $value;return $this;} + public function setPagination(?StartingAfterPaging $value = null): self + { + $this->pagination = $value; + return $this; + } /** * @return ( @@ -57,8 +62,10 @@ public function setPagination(?StartingAfterPaging $value = null): self { * |MultipleFilterSearchRequest * ) */ - public function getQuery(): SingleFilterSearchRequest|MultipleFilterSearchRequest { - return $this->query;} + public function getQuery(): SingleFilterSearchRequest|MultipleFilterSearchRequest + { + return $this->query; + } /** * @param ( @@ -66,13 +73,17 @@ public function getQuery(): SingleFilterSearchRequest|MultipleFilterSearchReques * |MultipleFilterSearchRequest * ) $value */ - public function setQuery(SingleFilterSearchRequest|MultipleFilterSearchRequest $value): self { - $this->query = $value;return $this;} + public function setQuery(SingleFilterSearchRequest|MultipleFilterSearchRequest $value): self + { + $this->query = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/property-accessors/src/Complex/Types/SingleFilterSearchRequest.php b/seed/php-sdk/pagination/property-accessors/src/Complex/Types/SingleFilterSearchRequest.php index 8b580d996fb0..9d35fbae980d 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Complex/Types/SingleFilterSearchRequest.php +++ b/seed/php-sdk/pagination/property-accessors/src/Complex/Types/SingleFilterSearchRequest.php @@ -34,51 +34,68 @@ class SingleFilterSearchRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->field = $values['field'] ?? null;$this->operator = $values['operator'] ?? null;$this->value = $values['value'] ?? null; + ) { + $this->field = $values['field'] ?? null; + $this->operator = $values['operator'] ?? null; + $this->value = $values['value'] ?? null; } /** * @return ?string */ - public function getField(): ?string { - return $this->field;} + public function getField(): ?string + { + return $this->field; + } /** * @param ?string $value */ - public function setField(?string $value = null): self { - $this->field = $value;return $this;} + public function setField(?string $value = null): self + { + $this->field = $value; + return $this; + } /** * @return ?value-of */ - public function getOperator(): ?string { - return $this->operator;} + public function getOperator(): ?string + { + return $this->operator; + } /** * @param ?value-of $value */ - public function setOperator(?string $value = null): self { - $this->operator = $value;return $this;} + public function setOperator(?string $value = null): self + { + $this->operator = $value; + return $this; + } /** * @return ?string */ - public function getValue(): ?string { - return $this->value;} + public function getValue(): ?string + { + return $this->value; + } /** * @param ?string $value */ - public function setValue(?string $value = null): self { - $this->value = $value;return $this;} + public function setValue(?string $value = null): self + { + $this->value = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/property-accessors/src/Complex/Types/SingleFilterSearchRequestOperator.php b/seed/php-sdk/pagination/property-accessors/src/Complex/Types/SingleFilterSearchRequestOperator.php index ad18302df3f9..6ba8c6097c55 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Complex/Types/SingleFilterSearchRequestOperator.php +++ b/seed/php-sdk/pagination/property-accessors/src/Complex/Types/SingleFilterSearchRequestOperator.php @@ -2,8 +2,8 @@ namespace Seed\Complex\Types; -enum SingleFilterSearchRequestOperator - : string { +enum SingleFilterSearchRequestOperator: string +{ case Equals = "="; case NotEquals = "!="; case In = "IN"; diff --git a/seed/php-sdk/pagination/property-accessors/src/Complex/Types/StartingAfterPaging.php b/seed/php-sdk/pagination/property-accessors/src/Complex/Types/StartingAfterPaging.php index 6636f1ec8088..a3a84c94f3d5 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Complex/Types/StartingAfterPaging.php +++ b/seed/php-sdk/pagination/property-accessors/src/Complex/Types/StartingAfterPaging.php @@ -27,39 +27,50 @@ class StartingAfterPaging extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->perPage = $values['perPage'];$this->startingAfter = $values['startingAfter'] ?? null; + ) { + $this->perPage = $values['perPage']; + $this->startingAfter = $values['startingAfter'] ?? null; } /** * @return int */ - public function getPerPage(): int { - return $this->perPage;} + public function getPerPage(): int + { + return $this->perPage; + } /** * @param int $value */ - public function setPerPage(int $value): self { - $this->perPage = $value;return $this;} + public function setPerPage(int $value): self + { + $this->perPage = $value; + return $this; + } /** * @return ?string */ - public function getStartingAfter(): ?string { - return $this->startingAfter;} + public function getStartingAfter(): ?string + { + return $this->startingAfter; + } /** * @param ?string $value */ - public function setStartingAfter(?string $value = null): self { - $this->startingAfter = $value;return $this;} + public function setStartingAfter(?string $value = null): self + { + $this->startingAfter = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/property-accessors/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/pagination/property-accessors/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/pagination/property-accessors/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination/property-accessors/src/Core/Client/RawClient.php b/seed/php-sdk/pagination/property-accessors/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Core/Client/RawClient.php +++ b/seed/php-sdk/pagination/property-accessors/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/pagination/property-accessors/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/pagination/property-accessors/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/pagination/property-accessors/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/pagination/property-accessors/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/pagination/property-accessors/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/pagination/property-accessors/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination/property-accessors/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/pagination/property-accessors/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/pagination/property-accessors/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination/property-accessors/src/Core/Pagination/Page.php b/seed/php-sdk/pagination/property-accessors/src/Core/Pagination/Page.php index 9d7fe5e5f243..f38d491e667b 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Core/Pagination/Page.php +++ b/seed/php-sdk/pagination/property-accessors/src/Core/Pagination/Page.php @@ -46,4 +46,4 @@ public function getIterator(): Generator { return yield from $this->getItems(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination/property-accessors/src/Core/Pagination/Pager.php b/seed/php-sdk/pagination/property-accessors/src/Core/Pagination/Pager.php index 351110574462..7897a30b307b 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Core/Pagination/Pager.php +++ b/seed/php-sdk/pagination/property-accessors/src/Core/Pagination/Pager.php @@ -33,4 +33,4 @@ public function getIterator(): Generator yield from $page->getItems(); } } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination/property-accessors/src/Core/Pagination/PaginationHelper.php b/seed/php-sdk/pagination/property-accessors/src/Core/Pagination/PaginationHelper.php index b1be91f12b98..bc0fd78fe428 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Core/Pagination/PaginationHelper.php +++ b/seed/php-sdk/pagination/property-accessors/src/Core/Pagination/PaginationHelper.php @@ -100,8 +100,7 @@ private static function createInstanceWithDefaults(string $className) $type = $property->getType(); $value = self::getDefaultValueForType($type); // if something is nullable, don't explicitly pass the null value as a parameter - if($value === null) - { + if ($value === null) { continue; } diff --git a/seed/php-sdk/pagination/property-accessors/src/Core/Types/ArrayType.php b/seed/php-sdk/pagination/property-accessors/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/pagination/property-accessors/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/pagination/property-accessors/src/Core/Types/Constant.php b/seed/php-sdk/pagination/property-accessors/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Core/Types/Constant.php +++ b/seed/php-sdk/pagination/property-accessors/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination/property-accessors/src/Core/Types/Union.php b/seed/php-sdk/pagination/property-accessors/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Core/Types/Union.php +++ b/seed/php-sdk/pagination/property-accessors/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination/property-accessors/src/Exceptions/SeedApiException.php b/seed/php-sdk/pagination/property-accessors/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/pagination/property-accessors/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/pagination/property-accessors/src/Exceptions/SeedException.php b/seed/php-sdk/pagination/property-accessors/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Exceptions/SeedException.php +++ b/seed/php-sdk/pagination/property-accessors/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/InlineUsersClient.php b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/InlineUsersClient.php index 7b5c1d977cea..ed23f0226a33 100644 --- a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/InlineUsersClient.php +++ b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/InlineUsersClient.php @@ -35,7 +35,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class InlineUsersClient +class InlineUsersClient { /** * @var array{ @@ -63,11 +63,10 @@ class InlineUsersClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -84,11 +83,12 @@ function __construct( * } $options * @return Pager */ - public function listWithCursorPagination(ListUsersCursorPaginationRequest $request = new ListUsersCursorPaginationRequest(), ?array $options = null): Pager { + public function listWithCursorPagination(ListUsersCursorPaginationRequest $request = new ListUsersCursorPaginationRequest(), ?array $options = null): Pager + { return new CursorPager( request: $request, - getNextPage: fn(ListUsersCursorPaginationRequest $request) => $this->_listWithCursorPagination($request, $options), - setCursor: function (ListUsersCursorPaginationRequest $request, string $cursor) { + getNextPage: fn (ListUsersCursorPaginationRequest $request) => $this->_listWithCursorPagination($request, $options), + setCursor: function (ListUsersCursorPaginationRequest $request, string $cursor) { $request->setStartingAfter($cursor); }, /* @phpstan-ignore-next-line */ @@ -110,11 +110,12 @@ public function listWithCursorPagination(ListUsersCursorPaginationRequest $reque * } $options * @return Pager */ - public function listWithMixedTypeCursorPagination(ListUsersMixedTypeCursorPaginationRequest $request = new ListUsersMixedTypeCursorPaginationRequest(), ?array $options = null): Pager { + public function listWithMixedTypeCursorPagination(ListUsersMixedTypeCursorPaginationRequest $request = new ListUsersMixedTypeCursorPaginationRequest(), ?array $options = null): Pager + { return new CursorPager( request: $request, - getNextPage: fn(ListUsersMixedTypeCursorPaginationRequest $request) => $this->_listWithMixedTypeCursorPagination($request, $options), - setCursor: function (ListUsersMixedTypeCursorPaginationRequest $request, string $cursor) { + getNextPage: fn (ListUsersMixedTypeCursorPaginationRequest $request) => $this->_listWithMixedTypeCursorPagination($request, $options), + setCursor: function (ListUsersMixedTypeCursorPaginationRequest $request, string $cursor) { $request->setCursor($cursor); }, /* @phpstan-ignore-next-line */ @@ -136,11 +137,12 @@ public function listWithMixedTypeCursorPagination(ListUsersMixedTypeCursorPagina * } $options * @return Pager */ - public function listWithBodyCursorPagination(ListUsersBodyCursorPaginationRequest $request = new ListUsersBodyCursorPaginationRequest(), ?array $options = null): Pager { + public function listWithBodyCursorPagination(ListUsersBodyCursorPaginationRequest $request = new ListUsersBodyCursorPaginationRequest(), ?array $options = null): Pager + { return new CursorPager( request: $request, - getNextPage: fn(ListUsersBodyCursorPaginationRequest $request) => $this->_listWithBodyCursorPagination($request, $options), - setCursor: function (ListUsersBodyCursorPaginationRequest $request, string $cursor) { + getNextPage: fn (ListUsersBodyCursorPaginationRequest $request) => $this->_listWithBodyCursorPagination($request, $options), + setCursor: function (ListUsersBodyCursorPaginationRequest $request, string $cursor) { PaginationHelper::setDeep($request, ["pagination", "cursor"], $cursor); }, /* @phpstan-ignore-next-line */ @@ -162,18 +164,19 @@ public function listWithBodyCursorPagination(ListUsersBodyCursorPaginationReques * } $options * @return Pager */ - public function listWithOffsetPagination(ListUsersOffsetPaginationRequest $request = new ListUsersOffsetPaginationRequest(), ?array $options = null): Pager { + public function listWithOffsetPagination(ListUsersOffsetPaginationRequest $request = new ListUsersOffsetPaginationRequest(), ?array $options = null): Pager + { return new OffsetPager( request: $request, - getNextPage: fn(ListUsersOffsetPaginationRequest $request) => $this->_listWithOffsetPagination($request, $options), + getNextPage: fn (ListUsersOffsetPaginationRequest $request) => $this->_listWithOffsetPagination($request, $options), /* @phpstan-ignore-next-line */ - getOffset: fn(ListUsersOffsetPaginationRequest $request) => $request?->getPage() ?? 0, - setOffset: function (ListUsersOffsetPaginationRequest $request, int $offset) { + getOffset: fn (ListUsersOffsetPaginationRequest $request) => $request?->getPage() ?? 0, + setOffset: function (ListUsersOffsetPaginationRequest $request, int $offset) { $request->setPage($offset); }, getStep: null, /* @phpstan-ignore-next-line */ - getItems: fn(ListUsersPaginationResponse $response) => $response?->getData()?->getUsers() ?? [], + getItems: fn (ListUsersPaginationResponse $response) => $response?->getData()?->getUsers() ?? [], /* @phpstan-ignore-next-line */ hasNextPage: null, ); @@ -191,18 +194,19 @@ public function listWithOffsetPagination(ListUsersOffsetPaginationRequest $reque * } $options * @return Pager */ - public function listWithDoubleOffsetPagination(ListUsersDoubleOffsetPaginationRequest $request = new ListUsersDoubleOffsetPaginationRequest(), ?array $options = null): Pager { + public function listWithDoubleOffsetPagination(ListUsersDoubleOffsetPaginationRequest $request = new ListUsersDoubleOffsetPaginationRequest(), ?array $options = null): Pager + { return new OffsetPager( request: $request, - getNextPage: fn(ListUsersDoubleOffsetPaginationRequest $request) => $this->_listWithDoubleOffsetPagination($request, $options), + getNextPage: fn (ListUsersDoubleOffsetPaginationRequest $request) => $this->_listWithDoubleOffsetPagination($request, $options), /* @phpstan-ignore-next-line */ - getOffset: fn(ListUsersDoubleOffsetPaginationRequest $request) => $request?->getPage() ?? 0, - setOffset: function (ListUsersDoubleOffsetPaginationRequest $request, int $offset) { + getOffset: fn (ListUsersDoubleOffsetPaginationRequest $request) => $request?->getPage() ?? 0, + setOffset: function (ListUsersDoubleOffsetPaginationRequest $request, int $offset) { $request->setPage($offset); }, getStep: null, /* @phpstan-ignore-next-line */ - getItems: fn(ListUsersPaginationResponse $response) => $response?->getData()?->getUsers() ?? [], + getItems: fn (ListUsersPaginationResponse $response) => $response?->getData()?->getUsers() ?? [], /* @phpstan-ignore-next-line */ hasNextPage: null, ); @@ -220,18 +224,19 @@ public function listWithDoubleOffsetPagination(ListUsersDoubleOffsetPaginationRe * } $options * @return Pager */ - public function listWithBodyOffsetPagination(ListUsersBodyOffsetPaginationRequest $request = new ListUsersBodyOffsetPaginationRequest(), ?array $options = null): Pager { + public function listWithBodyOffsetPagination(ListUsersBodyOffsetPaginationRequest $request = new ListUsersBodyOffsetPaginationRequest(), ?array $options = null): Pager + { return new OffsetPager( request: $request, - getNextPage: fn(ListUsersBodyOffsetPaginationRequest $request) => $this->_listWithBodyOffsetPagination($request, $options), + getNextPage: fn (ListUsersBodyOffsetPaginationRequest $request) => $this->_listWithBodyOffsetPagination($request, $options), /* @phpstan-ignore-next-line */ - getOffset: fn(ListUsersBodyOffsetPaginationRequest $request) => $request?->getPagination()?->getPage() ?? 0, - setOffset: function (ListUsersBodyOffsetPaginationRequest $request, int $offset) { + getOffset: fn (ListUsersBodyOffsetPaginationRequest $request) => $request?->getPagination()?->getPage() ?? 0, + setOffset: function (ListUsersBodyOffsetPaginationRequest $request, int $offset) { PaginationHelper::setDeep($request, ["pagination", "page"], $offset); }, getStep: null, /* @phpstan-ignore-next-line */ - getItems: fn(ListUsersPaginationResponse $response) => $response?->getData()?->getUsers() ?? [], + getItems: fn (ListUsersPaginationResponse $response) => $response?->getData()?->getUsers() ?? [], /* @phpstan-ignore-next-line */ hasNextPage: null, ); @@ -249,19 +254,20 @@ public function listWithBodyOffsetPagination(ListUsersBodyOffsetPaginationReques * } $options * @return Pager */ - public function listWithOffsetStepPagination(ListUsersOffsetStepPaginationRequest $request = new ListUsersOffsetStepPaginationRequest(), ?array $options = null): Pager { + public function listWithOffsetStepPagination(ListUsersOffsetStepPaginationRequest $request = new ListUsersOffsetStepPaginationRequest(), ?array $options = null): Pager + { return new OffsetPager( request: $request, - getNextPage: fn(ListUsersOffsetStepPaginationRequest $request) => $this->_listWithOffsetStepPagination($request, $options), + getNextPage: fn (ListUsersOffsetStepPaginationRequest $request) => $this->_listWithOffsetStepPagination($request, $options), /* @phpstan-ignore-next-line */ - getOffset: fn(ListUsersOffsetStepPaginationRequest $request) => $request?->getPage() ?? 0, - setOffset: function (ListUsersOffsetStepPaginationRequest $request, int $offset) { + getOffset: fn (ListUsersOffsetStepPaginationRequest $request) => $request?->getPage() ?? 0, + setOffset: function (ListUsersOffsetStepPaginationRequest $request, int $offset) { $request->setPage($offset); }, /* @phpstan-ignore-next-line */ - getStep: fn(ListUsersOffsetStepPaginationRequest $request) => $request?->getLimit() ?? 0, + getStep: fn (ListUsersOffsetStepPaginationRequest $request) => $request?->getLimit() ?? 0, /* @phpstan-ignore-next-line */ - getItems: fn(ListUsersPaginationResponse $response) => $response?->getData()?->getUsers() ?? [], + getItems: fn (ListUsersPaginationResponse $response) => $response?->getData()?->getUsers() ?? [], /* @phpstan-ignore-next-line */ hasNextPage: null, ); @@ -279,21 +285,22 @@ public function listWithOffsetStepPagination(ListUsersOffsetStepPaginationReques * } $options * @return Pager */ - public function listWithOffsetPaginationHasNextPage(ListWithOffsetPaginationHasNextPageRequest $request = new ListWithOffsetPaginationHasNextPageRequest(), ?array $options = null): Pager { + public function listWithOffsetPaginationHasNextPage(ListWithOffsetPaginationHasNextPageRequest $request = new ListWithOffsetPaginationHasNextPageRequest(), ?array $options = null): Pager + { return new OffsetPager( request: $request, - getNextPage: fn(ListWithOffsetPaginationHasNextPageRequest $request) => $this->_listWithOffsetPaginationHasNextPage($request, $options), + getNextPage: fn (ListWithOffsetPaginationHasNextPageRequest $request) => $this->_listWithOffsetPaginationHasNextPage($request, $options), /* @phpstan-ignore-next-line */ - getOffset: fn(ListWithOffsetPaginationHasNextPageRequest $request) => $request?->getPage() ?? 0, - setOffset: function (ListWithOffsetPaginationHasNextPageRequest $request, int $offset) { + getOffset: fn (ListWithOffsetPaginationHasNextPageRequest $request) => $request?->getPage() ?? 0, + setOffset: function (ListWithOffsetPaginationHasNextPageRequest $request, int $offset) { $request->setPage($offset); }, /* @phpstan-ignore-next-line */ - getStep: fn(ListWithOffsetPaginationHasNextPageRequest $request) => $request?->getLimit() ?? 0, + getStep: fn (ListWithOffsetPaginationHasNextPageRequest $request) => $request?->getLimit() ?? 0, /* @phpstan-ignore-next-line */ - getItems: fn(ListUsersPaginationResponse $response) => $response?->getData()?->getUsers() ?? [], + getItems: fn (ListUsersPaginationResponse $response) => $response?->getData()?->getUsers() ?? [], /* @phpstan-ignore-next-line */ - hasNextPage: fn(ListUsersPaginationResponse $response) => $response?->getHasNextPage(), + hasNextPage: fn (ListUsersPaginationResponse $response) => $response?->getHasNextPage(), ); } @@ -309,11 +316,12 @@ public function listWithOffsetPaginationHasNextPage(ListWithOffsetPaginationHasN * } $options * @return Pager */ - public function listWithExtendedResults(ListUsersExtendedRequest $request = new ListUsersExtendedRequest(), ?array $options = null): Pager { + public function listWithExtendedResults(ListUsersExtendedRequest $request = new ListUsersExtendedRequest(), ?array $options = null): Pager + { return new CursorPager( request: $request, - getNextPage: fn(ListUsersExtendedRequest $request) => $this->_listWithExtendedResults($request, $options), - setCursor: function (ListUsersExtendedRequest $request, ?string $cursor) { + getNextPage: fn (ListUsersExtendedRequest $request) => $this->_listWithExtendedResults($request, $options), + setCursor: function (ListUsersExtendedRequest $request, ?string $cursor) { $request->setCursor($cursor); }, /* @phpstan-ignore-next-line */ @@ -335,11 +343,12 @@ public function listWithExtendedResults(ListUsersExtendedRequest $request = new * } $options * @return Pager */ - public function listWithExtendedResultsAndOptionalData(ListUsersExtendedRequestForOptionalData $request = new ListUsersExtendedRequestForOptionalData(), ?array $options = null): Pager { + public function listWithExtendedResultsAndOptionalData(ListUsersExtendedRequestForOptionalData $request = new ListUsersExtendedRequestForOptionalData(), ?array $options = null): Pager + { return new CursorPager( request: $request, - getNextPage: fn(ListUsersExtendedRequestForOptionalData $request) => $this->_listWithExtendedResultsAndOptionalData($request, $options), - setCursor: function (ListUsersExtendedRequestForOptionalData $request, ?string $cursor) { + getNextPage: fn (ListUsersExtendedRequestForOptionalData $request) => $this->_listWithExtendedResultsAndOptionalData($request, $options), + setCursor: function (ListUsersExtendedRequestForOptionalData $request, ?string $cursor) { $request->setCursor($cursor); }, /* @phpstan-ignore-next-line */ @@ -361,11 +370,12 @@ public function listWithExtendedResultsAndOptionalData(ListUsersExtendedRequestF * } $options * @return Pager */ - public function listUsernames(ListUsernamesRequest $request = new ListUsernamesRequest(), ?array $options = null): Pager { + public function listUsernames(ListUsernamesRequest $request = new ListUsernamesRequest(), ?array $options = null): Pager + { return new CursorPager( request: $request, - getNextPage: fn(ListUsernamesRequest $request) => $this->_listUsernames($request, $options), - setCursor: function (ListUsernamesRequest $request, ?string $cursor) { + getNextPage: fn (ListUsernamesRequest $request) => $this->_listUsernames($request, $options), + setCursor: function (ListUsernamesRequest $request, ?string $cursor) { $request->setStartingAfter($cursor); }, /* @phpstan-ignore-next-line */ @@ -387,18 +397,19 @@ public function listUsernames(ListUsernamesRequest $request = new ListUsernamesR * } $options * @return Pager */ - public function listWithGlobalConfig(ListWithGlobalConfigRequest $request = new ListWithGlobalConfigRequest(), ?array $options = null): Pager { + public function listWithGlobalConfig(ListWithGlobalConfigRequest $request = new ListWithGlobalConfigRequest(), ?array $options = null): Pager + { return new OffsetPager( request: $request, - getNextPage: fn(ListWithGlobalConfigRequest $request) => $this->_listWithGlobalConfig($request, $options), + getNextPage: fn (ListWithGlobalConfigRequest $request) => $this->_listWithGlobalConfig($request, $options), /* @phpstan-ignore-next-line */ - getOffset: fn(ListWithGlobalConfigRequest $request) => $request?->getOffset() ?? 0, - setOffset: function (ListWithGlobalConfigRequest $request, int $offset) { + getOffset: fn (ListWithGlobalConfigRequest $request) => $request?->getOffset() ?? 0, + setOffset: function (ListWithGlobalConfigRequest $request, int $offset) { $request->setOffset($offset); }, getStep: null, /* @phpstan-ignore-next-line */ - getItems: fn(UsernameContainer $response) => $response?->getResults() ?? [], + getItems: fn (UsernameContainer $response) => $response?->getResults() ?? [], /* @phpstan-ignore-next-line */ hasNextPage: null, ); @@ -418,19 +429,20 @@ public function listWithGlobalConfig(ListWithGlobalConfigRequest $request = new * @throws SeedException * @throws SeedApiException */ - private function _listWithCursorPagination(ListUsersCursorPaginationRequest $request = new ListUsersCursorPaginationRequest(), ?array $options = null): ListUsersPaginationResponse { + private function _listWithCursorPagination(ListUsersCursorPaginationRequest $request = new ListUsersCursorPaginationRequest(), ?array $options = null): ListUsersPaginationResponse + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->getPage() != null){ + if ($request->getPage() != null) { $query['page'] = $request->getPage(); } - if ($request->getPerPage() != null){ + if ($request->getPerPage() != null) { $query['per_page'] = $request->getPerPage(); } - if ($request->getOrder() != null){ + if ($request->getOrder() != null) { $query['order'] = $request->getOrder(); } - if ($request->getStartingAfter() != null){ + if ($request->getStartingAfter() != null) { $query['starting_after'] = $request->getStartingAfter(); } try { @@ -444,15 +456,15 @@ private function _listWithCursorPagination(ListUsersCursorPaginationRequest $req $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ListUsersPaginationResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -484,10 +496,11 @@ private function _listWithCursorPagination(ListUsersCursorPaginationRequest $req * @throws SeedException * @throws SeedApiException */ - private function _listWithMixedTypeCursorPagination(ListUsersMixedTypeCursorPaginationRequest $request = new ListUsersMixedTypeCursorPaginationRequest(), ?array $options = null): ListUsersMixedTypePaginationResponse { + private function _listWithMixedTypeCursorPagination(ListUsersMixedTypeCursorPaginationRequest $request = new ListUsersMixedTypeCursorPaginationRequest(), ?array $options = null): ListUsersMixedTypePaginationResponse + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->getCursor() != null){ + if ($request->getCursor() != null) { $query['cursor'] = $request->getCursor(); } try { @@ -501,15 +514,15 @@ private function _listWithMixedTypeCursorPagination(ListUsersMixedTypeCursorPagi $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ListUsersMixedTypePaginationResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -541,7 +554,8 @@ private function _listWithMixedTypeCursorPagination(ListUsersMixedTypeCursorPagi * @throws SeedException * @throws SeedApiException */ - private function _listWithBodyCursorPagination(ListUsersBodyCursorPaginationRequest $request = new ListUsersBodyCursorPaginationRequest(), ?array $options = null): ListUsersPaginationResponse { + private function _listWithBodyCursorPagination(ListUsersBodyCursorPaginationRequest $request = new ListUsersBodyCursorPaginationRequest(), ?array $options = null): ListUsersPaginationResponse + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -554,15 +568,15 @@ private function _listWithBodyCursorPagination(ListUsersBodyCursorPaginationRequ $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ListUsersPaginationResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -594,19 +608,20 @@ private function _listWithBodyCursorPagination(ListUsersBodyCursorPaginationRequ * @throws SeedException * @throws SeedApiException */ - private function _listWithOffsetPagination(ListUsersOffsetPaginationRequest $request = new ListUsersOffsetPaginationRequest(), ?array $options = null): ListUsersPaginationResponse { + private function _listWithOffsetPagination(ListUsersOffsetPaginationRequest $request = new ListUsersOffsetPaginationRequest(), ?array $options = null): ListUsersPaginationResponse + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->getPage() != null){ + if ($request->getPage() != null) { $query['page'] = $request->getPage(); } - if ($request->getPerPage() != null){ + if ($request->getPerPage() != null) { $query['per_page'] = $request->getPerPage(); } - if ($request->getOrder() != null){ + if ($request->getOrder() != null) { $query['order'] = $request->getOrder(); } - if ($request->getStartingAfter() != null){ + if ($request->getStartingAfter() != null) { $query['starting_after'] = $request->getStartingAfter(); } try { @@ -620,15 +635,15 @@ private function _listWithOffsetPagination(ListUsersOffsetPaginationRequest $req $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ListUsersPaginationResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -660,19 +675,20 @@ private function _listWithOffsetPagination(ListUsersOffsetPaginationRequest $req * @throws SeedException * @throws SeedApiException */ - private function _listWithDoubleOffsetPagination(ListUsersDoubleOffsetPaginationRequest $request = new ListUsersDoubleOffsetPaginationRequest(), ?array $options = null): ListUsersPaginationResponse { + private function _listWithDoubleOffsetPagination(ListUsersDoubleOffsetPaginationRequest $request = new ListUsersDoubleOffsetPaginationRequest(), ?array $options = null): ListUsersPaginationResponse + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->getPage() != null){ + if ($request->getPage() != null) { $query['page'] = $request->getPage(); } - if ($request->getPerPage() != null){ + if ($request->getPerPage() != null) { $query['per_page'] = $request->getPerPage(); } - if ($request->getOrder() != null){ + if ($request->getOrder() != null) { $query['order'] = $request->getOrder(); } - if ($request->getStartingAfter() != null){ + if ($request->getStartingAfter() != null) { $query['starting_after'] = $request->getStartingAfter(); } try { @@ -686,15 +702,15 @@ private function _listWithDoubleOffsetPagination(ListUsersDoubleOffsetPagination $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ListUsersPaginationResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -726,7 +742,8 @@ private function _listWithDoubleOffsetPagination(ListUsersDoubleOffsetPagination * @throws SeedException * @throws SeedApiException */ - private function _listWithBodyOffsetPagination(ListUsersBodyOffsetPaginationRequest $request = new ListUsersBodyOffsetPaginationRequest(), ?array $options = null): ListUsersPaginationResponse { + private function _listWithBodyOffsetPagination(ListUsersBodyOffsetPaginationRequest $request = new ListUsersBodyOffsetPaginationRequest(), ?array $options = null): ListUsersPaginationResponse + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -739,15 +756,15 @@ private function _listWithBodyOffsetPagination(ListUsersBodyOffsetPaginationRequ $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ListUsersPaginationResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -779,16 +796,17 @@ private function _listWithBodyOffsetPagination(ListUsersBodyOffsetPaginationRequ * @throws SeedException * @throws SeedApiException */ - private function _listWithOffsetStepPagination(ListUsersOffsetStepPaginationRequest $request = new ListUsersOffsetStepPaginationRequest(), ?array $options = null): ListUsersPaginationResponse { + private function _listWithOffsetStepPagination(ListUsersOffsetStepPaginationRequest $request = new ListUsersOffsetStepPaginationRequest(), ?array $options = null): ListUsersPaginationResponse + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->getPage() != null){ + if ($request->getPage() != null) { $query['page'] = $request->getPage(); } - if ($request->getLimit() != null){ + if ($request->getLimit() != null) { $query['limit'] = $request->getLimit(); } - if ($request->getOrder() != null){ + if ($request->getOrder() != null) { $query['order'] = $request->getOrder(); } try { @@ -802,15 +820,15 @@ private function _listWithOffsetStepPagination(ListUsersOffsetStepPaginationRequ $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ListUsersPaginationResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -842,16 +860,17 @@ private function _listWithOffsetStepPagination(ListUsersOffsetStepPaginationRequ * @throws SeedException * @throws SeedApiException */ - private function _listWithOffsetPaginationHasNextPage(ListWithOffsetPaginationHasNextPageRequest $request = new ListWithOffsetPaginationHasNextPageRequest(), ?array $options = null): ListUsersPaginationResponse { + private function _listWithOffsetPaginationHasNextPage(ListWithOffsetPaginationHasNextPageRequest $request = new ListWithOffsetPaginationHasNextPageRequest(), ?array $options = null): ListUsersPaginationResponse + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->getPage() != null){ + if ($request->getPage() != null) { $query['page'] = $request->getPage(); } - if ($request->getLimit() != null){ + if ($request->getLimit() != null) { $query['limit'] = $request->getLimit(); } - if ($request->getOrder() != null){ + if ($request->getOrder() != null) { $query['order'] = $request->getOrder(); } try { @@ -865,15 +884,15 @@ private function _listWithOffsetPaginationHasNextPage(ListWithOffsetPaginationHa $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ListUsersPaginationResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -905,10 +924,11 @@ private function _listWithOffsetPaginationHasNextPage(ListWithOffsetPaginationHa * @throws SeedException * @throws SeedApiException */ - private function _listWithExtendedResults(ListUsersExtendedRequest $request = new ListUsersExtendedRequest(), ?array $options = null): ListUsersExtendedResponse { + private function _listWithExtendedResults(ListUsersExtendedRequest $request = new ListUsersExtendedRequest(), ?array $options = null): ListUsersExtendedResponse + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->getCursor() != null){ + if ($request->getCursor() != null) { $query['cursor'] = $request->getCursor(); } try { @@ -922,15 +942,15 @@ private function _listWithExtendedResults(ListUsersExtendedRequest $request = ne $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ListUsersExtendedResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -962,10 +982,11 @@ private function _listWithExtendedResults(ListUsersExtendedRequest $request = ne * @throws SeedException * @throws SeedApiException */ - private function _listWithExtendedResultsAndOptionalData(ListUsersExtendedRequestForOptionalData $request = new ListUsersExtendedRequestForOptionalData(), ?array $options = null): ListUsersExtendedOptionalListResponse { + private function _listWithExtendedResultsAndOptionalData(ListUsersExtendedRequestForOptionalData $request = new ListUsersExtendedRequestForOptionalData(), ?array $options = null): ListUsersExtendedOptionalListResponse + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->getCursor() != null){ + if ($request->getCursor() != null) { $query['cursor'] = $request->getCursor(); } try { @@ -979,15 +1000,15 @@ private function _listWithExtendedResultsAndOptionalData(ListUsersExtendedReques $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ListUsersExtendedOptionalListResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -1019,10 +1040,11 @@ private function _listWithExtendedResultsAndOptionalData(ListUsersExtendedReques * @throws SeedException * @throws SeedApiException */ - private function _listUsernames(ListUsernamesRequest $request = new ListUsernamesRequest(), ?array $options = null): UsernameCursor { + private function _listUsernames(ListUsernamesRequest $request = new ListUsernamesRequest(), ?array $options = null): UsernameCursor + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->getStartingAfter() != null){ + if ($request->getStartingAfter() != null) { $query['starting_after'] = $request->getStartingAfter(); } try { @@ -1036,15 +1058,15 @@ private function _listUsernames(ListUsernamesRequest $request = new ListUsername $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return UsernameCursor::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -1076,10 +1098,11 @@ private function _listUsernames(ListUsernamesRequest $request = new ListUsername * @throws SeedException * @throws SeedApiException */ - private function _listWithGlobalConfig(ListWithGlobalConfigRequest $request = new ListWithGlobalConfigRequest(), ?array $options = null): UsernameContainer { + private function _listWithGlobalConfig(ListWithGlobalConfigRequest $request = new ListWithGlobalConfigRequest(), ?array $options = null): UsernameContainer + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->getOffset() != null){ + if ($request->getOffset() != null) { $query['offset'] = $request->getOffset(); } try { @@ -1093,15 +1116,15 @@ private function _listWithGlobalConfig(ListWithGlobalConfigRequest $request = ne $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return UsernameContainer::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListUsernamesRequest.php b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListUsernamesRequest.php index 008e1dbd8bf2..a78adf591408 100644 --- a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListUsernamesRequest.php +++ b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListUsernamesRequest.php @@ -21,20 +21,24 @@ class ListUsernamesRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->startingAfter = $values['startingAfter'] ?? null; } /** * @return ?string */ - public function getStartingAfter(): ?string { - return $this->startingAfter;} + public function getStartingAfter(): ?string + { + return $this->startingAfter; + } /** * @param ?string $value */ - public function setStartingAfter(?string $value = null): self { - $this->startingAfter = $value;return $this;} + public function setStartingAfter(?string $value = null): self + { + $this->startingAfter = $value; + return $this; + } } diff --git a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListUsersBodyCursorPaginationRequest.php b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListUsersBodyCursorPaginationRequest.php index 3e0e682fbf0f..ac3e4d3ef2eb 100644 --- a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListUsersBodyCursorPaginationRequest.php +++ b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListUsersBodyCursorPaginationRequest.php @@ -24,20 +24,24 @@ class ListUsersBodyCursorPaginationRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->pagination = $values['pagination'] ?? null; } /** * @return ?WithCursor */ - public function getPagination(): ?WithCursor { - return $this->pagination;} + public function getPagination(): ?WithCursor + { + return $this->pagination; + } /** * @param ?WithCursor $value */ - public function setPagination(?WithCursor $value = null): self { - $this->pagination = $value;return $this;} + public function setPagination(?WithCursor $value = null): self + { + $this->pagination = $value; + return $this; + } } diff --git a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListUsersBodyOffsetPaginationRequest.php b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListUsersBodyOffsetPaginationRequest.php index 47702eaa79a7..6718c732796e 100644 --- a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListUsersBodyOffsetPaginationRequest.php +++ b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListUsersBodyOffsetPaginationRequest.php @@ -24,20 +24,24 @@ class ListUsersBodyOffsetPaginationRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->pagination = $values['pagination'] ?? null; } /** * @return ?WithPage */ - public function getPagination(): ?WithPage { - return $this->pagination;} + public function getPagination(): ?WithPage + { + return $this->pagination; + } /** * @param ?WithPage $value */ - public function setPagination(?WithPage $value = null): self { - $this->pagination = $value;return $this;} + public function setPagination(?WithPage $value = null): self + { + $this->pagination = $value; + return $this; + } } diff --git a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListUsersCursorPaginationRequest.php b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListUsersCursorPaginationRequest.php index e5faed8d9c96..2d453cb44372 100644 --- a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListUsersCursorPaginationRequest.php +++ b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListUsersCursorPaginationRequest.php @@ -40,56 +40,78 @@ class ListUsersCursorPaginationRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->page = $values['page'] ?? null;$this->perPage = $values['perPage'] ?? null;$this->order = $values['order'] ?? null;$this->startingAfter = $values['startingAfter'] ?? null; + ) { + $this->page = $values['page'] ?? null; + $this->perPage = $values['perPage'] ?? null; + $this->order = $values['order'] ?? null; + $this->startingAfter = $values['startingAfter'] ?? null; } /** * @return ?int */ - public function getPage(): ?int { - return $this->page;} + public function getPage(): ?int + { + return $this->page; + } /** * @param ?int $value */ - public function setPage(?int $value = null): self { - $this->page = $value;return $this;} + public function setPage(?int $value = null): self + { + $this->page = $value; + return $this; + } /** * @return ?int */ - public function getPerPage(): ?int { - return $this->perPage;} + public function getPerPage(): ?int + { + return $this->perPage; + } /** * @param ?int $value */ - public function setPerPage(?int $value = null): self { - $this->perPage = $value;return $this;} + public function setPerPage(?int $value = null): self + { + $this->perPage = $value; + return $this; + } /** * @return ?value-of */ - public function getOrder(): ?string { - return $this->order;} + public function getOrder(): ?string + { + return $this->order; + } /** * @param ?value-of $value */ - public function setOrder(?string $value = null): self { - $this->order = $value;return $this;} + public function setOrder(?string $value = null): self + { + $this->order = $value; + return $this; + } /** * @return ?string */ - public function getStartingAfter(): ?string { - return $this->startingAfter;} + public function getStartingAfter(): ?string + { + return $this->startingAfter; + } /** * @param ?string $value */ - public function setStartingAfter(?string $value = null): self { - $this->startingAfter = $value;return $this;} + public function setStartingAfter(?string $value = null): self + { + $this->startingAfter = $value; + return $this; + } } diff --git a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListUsersDoubleOffsetPaginationRequest.php b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListUsersDoubleOffsetPaginationRequest.php index f302bd729a02..cab26a801e3e 100644 --- a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListUsersDoubleOffsetPaginationRequest.php +++ b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListUsersDoubleOffsetPaginationRequest.php @@ -40,56 +40,78 @@ class ListUsersDoubleOffsetPaginationRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->page = $values['page'] ?? null;$this->perPage = $values['perPage'] ?? null;$this->order = $values['order'] ?? null;$this->startingAfter = $values['startingAfter'] ?? null; + ) { + $this->page = $values['page'] ?? null; + $this->perPage = $values['perPage'] ?? null; + $this->order = $values['order'] ?? null; + $this->startingAfter = $values['startingAfter'] ?? null; } /** * @return ?float */ - public function getPage(): ?float { - return $this->page;} + public function getPage(): ?float + { + return $this->page; + } /** * @param ?float $value */ - public function setPage(?float $value = null): self { - $this->page = $value;return $this;} + public function setPage(?float $value = null): self + { + $this->page = $value; + return $this; + } /** * @return ?float */ - public function getPerPage(): ?float { - return $this->perPage;} + public function getPerPage(): ?float + { + return $this->perPage; + } /** * @param ?float $value */ - public function setPerPage(?float $value = null): self { - $this->perPage = $value;return $this;} + public function setPerPage(?float $value = null): self + { + $this->perPage = $value; + return $this; + } /** * @return ?value-of */ - public function getOrder(): ?string { - return $this->order;} + public function getOrder(): ?string + { + return $this->order; + } /** * @param ?value-of $value */ - public function setOrder(?string $value = null): self { - $this->order = $value;return $this;} + public function setOrder(?string $value = null): self + { + $this->order = $value; + return $this; + } /** * @return ?string */ - public function getStartingAfter(): ?string { - return $this->startingAfter;} + public function getStartingAfter(): ?string + { + return $this->startingAfter; + } /** * @param ?string $value */ - public function setStartingAfter(?string $value = null): self { - $this->startingAfter = $value;return $this;} + public function setStartingAfter(?string $value = null): self + { + $this->startingAfter = $value; + return $this; + } } diff --git a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListUsersExtendedRequest.php b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListUsersExtendedRequest.php index 3e84468c1b24..6063e7107d96 100644 --- a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListUsersExtendedRequest.php +++ b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListUsersExtendedRequest.php @@ -18,20 +18,24 @@ class ListUsersExtendedRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->cursor = $values['cursor'] ?? null; } /** * @return ?string */ - public function getCursor(): ?string { - return $this->cursor;} + public function getCursor(): ?string + { + return $this->cursor; + } /** * @param ?string $value */ - public function setCursor(?string $value = null): self { - $this->cursor = $value;return $this;} + public function setCursor(?string $value = null): self + { + $this->cursor = $value; + return $this; + } } diff --git a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListUsersExtendedRequestForOptionalData.php b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListUsersExtendedRequestForOptionalData.php index f61969b836d5..2405d3d98e90 100644 --- a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListUsersExtendedRequestForOptionalData.php +++ b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListUsersExtendedRequestForOptionalData.php @@ -18,20 +18,24 @@ class ListUsersExtendedRequestForOptionalData extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->cursor = $values['cursor'] ?? null; } /** * @return ?string */ - public function getCursor(): ?string { - return $this->cursor;} + public function getCursor(): ?string + { + return $this->cursor; + } /** * @param ?string $value */ - public function setCursor(?string $value = null): self { - $this->cursor = $value;return $this;} + public function setCursor(?string $value = null): self + { + $this->cursor = $value; + return $this; + } } diff --git a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListUsersMixedTypeCursorPaginationRequest.php b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListUsersMixedTypeCursorPaginationRequest.php index 4def1af84bc5..9b0e552e9ef6 100644 --- a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListUsersMixedTypeCursorPaginationRequest.php +++ b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListUsersMixedTypeCursorPaginationRequest.php @@ -18,20 +18,24 @@ class ListUsersMixedTypeCursorPaginationRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->cursor = $values['cursor'] ?? null; } /** * @return ?string */ - public function getCursor(): ?string { - return $this->cursor;} + public function getCursor(): ?string + { + return $this->cursor; + } /** * @param ?string $value */ - public function setCursor(?string $value = null): self { - $this->cursor = $value;return $this;} + public function setCursor(?string $value = null): self + { + $this->cursor = $value; + return $this; + } } diff --git a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListUsersOffsetPaginationRequest.php b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListUsersOffsetPaginationRequest.php index c7a9404d619e..85c0811f626a 100644 --- a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListUsersOffsetPaginationRequest.php +++ b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListUsersOffsetPaginationRequest.php @@ -40,56 +40,78 @@ class ListUsersOffsetPaginationRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->page = $values['page'] ?? null;$this->perPage = $values['perPage'] ?? null;$this->order = $values['order'] ?? null;$this->startingAfter = $values['startingAfter'] ?? null; + ) { + $this->page = $values['page'] ?? null; + $this->perPage = $values['perPage'] ?? null; + $this->order = $values['order'] ?? null; + $this->startingAfter = $values['startingAfter'] ?? null; } /** * @return ?int */ - public function getPage(): ?int { - return $this->page;} + public function getPage(): ?int + { + return $this->page; + } /** * @param ?int $value */ - public function setPage(?int $value = null): self { - $this->page = $value;return $this;} + public function setPage(?int $value = null): self + { + $this->page = $value; + return $this; + } /** * @return ?int */ - public function getPerPage(): ?int { - return $this->perPage;} + public function getPerPage(): ?int + { + return $this->perPage; + } /** * @param ?int $value */ - public function setPerPage(?int $value = null): self { - $this->perPage = $value;return $this;} + public function setPerPage(?int $value = null): self + { + $this->perPage = $value; + return $this; + } /** * @return ?value-of */ - public function getOrder(): ?string { - return $this->order;} + public function getOrder(): ?string + { + return $this->order; + } /** * @param ?value-of $value */ - public function setOrder(?string $value = null): self { - $this->order = $value;return $this;} + public function setOrder(?string $value = null): self + { + $this->order = $value; + return $this; + } /** * @return ?string */ - public function getStartingAfter(): ?string { - return $this->startingAfter;} + public function getStartingAfter(): ?string + { + return $this->startingAfter; + } /** * @param ?string $value */ - public function setStartingAfter(?string $value = null): self { - $this->startingAfter = $value;return $this;} + public function setStartingAfter(?string $value = null): self + { + $this->startingAfter = $value; + return $this; + } } diff --git a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListUsersOffsetStepPaginationRequest.php b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListUsersOffsetStepPaginationRequest.php index 20f5eeac1c2d..29c9e1c1dfe6 100644 --- a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListUsersOffsetStepPaginationRequest.php +++ b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListUsersOffsetStepPaginationRequest.php @@ -35,44 +35,60 @@ class ListUsersOffsetStepPaginationRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->page = $values['page'] ?? null;$this->limit = $values['limit'] ?? null;$this->order = $values['order'] ?? null; + ) { + $this->page = $values['page'] ?? null; + $this->limit = $values['limit'] ?? null; + $this->order = $values['order'] ?? null; } /** * @return ?int */ - public function getPage(): ?int { - return $this->page;} + public function getPage(): ?int + { + return $this->page; + } /** * @param ?int $value */ - public function setPage(?int $value = null): self { - $this->page = $value;return $this;} + public function setPage(?int $value = null): self + { + $this->page = $value; + return $this; + } /** * @return ?int */ - public function getLimit(): ?int { - return $this->limit;} + public function getLimit(): ?int + { + return $this->limit; + } /** * @param ?int $value */ - public function setLimit(?int $value = null): self { - $this->limit = $value;return $this;} + public function setLimit(?int $value = null): self + { + $this->limit = $value; + return $this; + } /** * @return ?value-of */ - public function getOrder(): ?string { - return $this->order;} + public function getOrder(): ?string + { + return $this->order; + } /** * @param ?value-of $value */ - public function setOrder(?string $value = null): self { - $this->order = $value;return $this;} + public function setOrder(?string $value = null): self + { + $this->order = $value; + return $this; + } } diff --git a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListWithGlobalConfigRequest.php b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListWithGlobalConfigRequest.php index e6419f84a058..f517bef532ab 100644 --- a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListWithGlobalConfigRequest.php +++ b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListWithGlobalConfigRequest.php @@ -18,20 +18,24 @@ class ListWithGlobalConfigRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->offset = $values['offset'] ?? null; } /** * @return ?int */ - public function getOffset(): ?int { - return $this->offset;} + public function getOffset(): ?int + { + return $this->offset; + } /** * @param ?int $value */ - public function setOffset(?int $value = null): self { - $this->offset = $value;return $this;} + public function setOffset(?int $value = null): self + { + $this->offset = $value; + return $this; + } } diff --git a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListWithOffsetPaginationHasNextPageRequest.php b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListWithOffsetPaginationHasNextPageRequest.php index c9f5885cc0e3..51b05f85a771 100644 --- a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListWithOffsetPaginationHasNextPageRequest.php +++ b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Requests/ListWithOffsetPaginationHasNextPageRequest.php @@ -35,44 +35,60 @@ class ListWithOffsetPaginationHasNextPageRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->page = $values['page'] ?? null;$this->limit = $values['limit'] ?? null;$this->order = $values['order'] ?? null; + ) { + $this->page = $values['page'] ?? null; + $this->limit = $values['limit'] ?? null; + $this->order = $values['order'] ?? null; } /** * @return ?int */ - public function getPage(): ?int { - return $this->page;} + public function getPage(): ?int + { + return $this->page; + } /** * @param ?int $value */ - public function setPage(?int $value = null): self { - $this->page = $value;return $this;} + public function setPage(?int $value = null): self + { + $this->page = $value; + return $this; + } /** * @return ?int */ - public function getLimit(): ?int { - return $this->limit;} + public function getLimit(): ?int + { + return $this->limit; + } /** * @param ?int $value */ - public function setLimit(?int $value = null): self { - $this->limit = $value;return $this;} + public function setLimit(?int $value = null): self + { + $this->limit = $value; + return $this; + } /** * @return ?value-of */ - public function getOrder(): ?string { - return $this->order;} + public function getOrder(): ?string + { + return $this->order; + } /** * @param ?value-of $value */ - public function setOrder(?string $value = null): self { - $this->order = $value;return $this;} + public function setOrder(?string $value = null): self + { + $this->order = $value; + return $this; + } } diff --git a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Traits/UserOptionalListPage.php b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Traits/UserOptionalListPage.php index 5b0dac3a2b6a..59dc6573dff8 100644 --- a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Traits/UserOptionalListPage.php +++ b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Traits/UserOptionalListPage.php @@ -9,7 +9,7 @@ * @property UserOptionalListContainer $data * @property ?string $next */ -trait UserOptionalListPage +trait UserOptionalListPage { /** * @var UserOptionalListContainer $data @@ -26,24 +26,34 @@ trait UserOptionalListPage /** * @return UserOptionalListContainer */ - public function getData(): UserOptionalListContainer { - return $this->data;} + public function getData(): UserOptionalListContainer + { + return $this->data; + } /** * @param UserOptionalListContainer $value */ - public function setData(UserOptionalListContainer $value): self { - $this->data = $value;return $this;} + public function setData(UserOptionalListContainer $value): self + { + $this->data = $value; + return $this; + } /** * @return ?string */ - public function getNext(): ?string { - return $this->next;} + public function getNext(): ?string + { + return $this->next; + } /** * @param ?string $value */ - public function setNext(?string $value = null): self { - $this->next = $value;return $this;} + public function setNext(?string $value = null): self + { + $this->next = $value; + return $this; + } } diff --git a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Traits/UserPage.php b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Traits/UserPage.php index 4e52151ce5ec..ab20bc40abe0 100644 --- a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Traits/UserPage.php +++ b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Traits/UserPage.php @@ -9,7 +9,7 @@ * @property UserListContainer $data * @property ?string $next */ -trait UserPage +trait UserPage { /** * @var UserListContainer $data @@ -26,24 +26,34 @@ trait UserPage /** * @return UserListContainer */ - public function getData(): UserListContainer { - return $this->data;} + public function getData(): UserListContainer + { + return $this->data; + } /** * @param UserListContainer $value */ - public function setData(UserListContainer $value): self { - $this->data = $value;return $this;} + public function setData(UserListContainer $value): self + { + $this->data = $value; + return $this; + } /** * @return ?string */ - public function getNext(): ?string { - return $this->next;} + public function getNext(): ?string + { + return $this->next; + } /** * @param ?string $value */ - public function setNext(?string $value = null): self { - $this->next = $value;return $this;} + public function setNext(?string $value = null): self + { + $this->next = $value; + return $this; + } } diff --git a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/ListUsersExtendedOptionalListResponse.php b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/ListUsersExtendedOptionalListResponse.php index 38e99c83bf7b..dd2b0cb1566e 100644 --- a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/ListUsersExtendedOptionalListResponse.php +++ b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/ListUsersExtendedOptionalListResponse.php @@ -25,27 +25,34 @@ class ListUsersExtendedOptionalListResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->data = $values['data'];$this->next = $values['next'] ?? null;$this->totalCount = $values['totalCount']; + ) { + $this->data = $values['data']; + $this->next = $values['next'] ?? null; + $this->totalCount = $values['totalCount']; } /** * @return int */ - public function getTotalCount(): int { - return $this->totalCount;} + public function getTotalCount(): int + { + return $this->totalCount; + } /** * @param int $value */ - public function setTotalCount(int $value): self { - $this->totalCount = $value;return $this;} + public function setTotalCount(int $value): self + { + $this->totalCount = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/ListUsersExtendedResponse.php b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/ListUsersExtendedResponse.php index fa47648d1fbd..7f02fc6a35c0 100644 --- a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/ListUsersExtendedResponse.php +++ b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/ListUsersExtendedResponse.php @@ -25,27 +25,34 @@ class ListUsersExtendedResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->data = $values['data'];$this->next = $values['next'] ?? null;$this->totalCount = $values['totalCount']; + ) { + $this->data = $values['data']; + $this->next = $values['next'] ?? null; + $this->totalCount = $values['totalCount']; } /** * @return int */ - public function getTotalCount(): int { - return $this->totalCount;} + public function getTotalCount(): int + { + return $this->totalCount; + } /** * @param int $value */ - public function setTotalCount(int $value): self { - $this->totalCount = $value;return $this;} + public function setTotalCount(int $value): self + { + $this->totalCount = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/ListUsersMixedTypePaginationResponse.php b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/ListUsersMixedTypePaginationResponse.php index eb697984f94b..579b6c182392 100644 --- a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/ListUsersMixedTypePaginationResponse.php +++ b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/ListUsersMixedTypePaginationResponse.php @@ -27,39 +27,50 @@ class ListUsersMixedTypePaginationResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->next = $values['next'];$this->data = $values['data']; + ) { + $this->next = $values['next']; + $this->data = $values['data']; } /** * @return string */ - public function getNext(): string { - return $this->next;} + public function getNext(): string + { + return $this->next; + } /** * @param string $value */ - public function setNext(string $value): self { - $this->next = $value;return $this;} + public function setNext(string $value): self + { + $this->next = $value; + return $this; + } /** * @return Users */ - public function getData(): Users { - return $this->data;} + public function getData(): Users + { + return $this->data; + } /** * @param Users $value */ - public function setData(Users $value): self { - $this->data = $value;return $this;} + public function setData(Users $value): self + { + $this->data = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/ListUsersPaginationResponse.php b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/ListUsersPaginationResponse.php index 94b824b1fc82..953718398810 100644 --- a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/ListUsersPaginationResponse.php +++ b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/ListUsersPaginationResponse.php @@ -41,63 +41,86 @@ class ListUsersPaginationResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->hasNextPage = $values['hasNextPage'] ?? null;$this->page = $values['page'] ?? null;$this->totalCount = $values['totalCount'];$this->data = $values['data']; + ) { + $this->hasNextPage = $values['hasNextPage'] ?? null; + $this->page = $values['page'] ?? null; + $this->totalCount = $values['totalCount']; + $this->data = $values['data']; } /** * @return ?bool */ - public function getHasNextPage(): ?bool { - return $this->hasNextPage;} + public function getHasNextPage(): ?bool + { + return $this->hasNextPage; + } /** * @param ?bool $value */ - public function setHasNextPage(?bool $value = null): self { - $this->hasNextPage = $value;return $this;} + public function setHasNextPage(?bool $value = null): self + { + $this->hasNextPage = $value; + return $this; + } /** * @return ?Page */ - public function getPage(): ?Page { - return $this->page;} + public function getPage(): ?Page + { + return $this->page; + } /** * @param ?Page $value */ - public function setPage(?Page $value = null): self { - $this->page = $value;return $this;} + public function setPage(?Page $value = null): self + { + $this->page = $value; + return $this; + } /** * @return int */ - public function getTotalCount(): int { - return $this->totalCount;} + public function getTotalCount(): int + { + return $this->totalCount; + } /** * @param int $value */ - public function setTotalCount(int $value): self { - $this->totalCount = $value;return $this;} + public function setTotalCount(int $value): self + { + $this->totalCount = $value; + return $this; + } /** * @return Users */ - public function getData(): Users { - return $this->data;} + public function getData(): Users + { + return $this->data; + } /** * @param Users $value */ - public function setData(Users $value): self { - $this->data = $value;return $this;} + public function setData(Users $value): self + { + $this->data = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/NextPage.php b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/NextPage.php index 883b302f0b16..549a7fbb3f9a 100644 --- a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/NextPage.php +++ b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/NextPage.php @@ -27,39 +27,50 @@ class NextPage extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->page = $values['page'];$this->startingAfter = $values['startingAfter']; + ) { + $this->page = $values['page']; + $this->startingAfter = $values['startingAfter']; } /** * @return int */ - public function getPage(): int { - return $this->page;} + public function getPage(): int + { + return $this->page; + } /** * @param int $value */ - public function setPage(int $value): self { - $this->page = $value;return $this;} + public function setPage(int $value): self + { + $this->page = $value; + return $this; + } /** * @return string */ - public function getStartingAfter(): string { - return $this->startingAfter;} + public function getStartingAfter(): string + { + return $this->startingAfter; + } /** * @param string $value */ - public function setStartingAfter(string $value): self { - $this->startingAfter = $value;return $this;} + public function setStartingAfter(string $value): self + { + $this->startingAfter = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/Order.php b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/Order.php index 36338ff1a413..b99b851b7e7b 100644 --- a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/Order.php +++ b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/Order.php @@ -2,8 +2,8 @@ namespace Seed\InlineUsers\InlineUsers\Types; -enum Order - : string { +enum Order: string +{ case Asc = "asc"; case Desc = "desc"; } diff --git a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/Page.php b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/Page.php index edeead54fbe0..912ac5aac983 100644 --- a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/Page.php +++ b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/Page.php @@ -41,63 +41,86 @@ class Page extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->page = $values['page'];$this->next = $values['next'] ?? null;$this->perPage = $values['perPage'];$this->totalPage = $values['totalPage']; + ) { + $this->page = $values['page']; + $this->next = $values['next'] ?? null; + $this->perPage = $values['perPage']; + $this->totalPage = $values['totalPage']; } /** * @return int */ - public function getPage(): int { - return $this->page;} + public function getPage(): int + { + return $this->page; + } /** * @param int $value */ - public function setPage(int $value): self { - $this->page = $value;return $this;} + public function setPage(int $value): self + { + $this->page = $value; + return $this; + } /** * @return ?NextPage */ - public function getNext(): ?NextPage { - return $this->next;} + public function getNext(): ?NextPage + { + return $this->next; + } /** * @param ?NextPage $value */ - public function setNext(?NextPage $value = null): self { - $this->next = $value;return $this;} + public function setNext(?NextPage $value = null): self + { + $this->next = $value; + return $this; + } /** * @return int */ - public function getPerPage(): int { - return $this->perPage;} + public function getPerPage(): int + { + return $this->perPage; + } /** * @param int $value */ - public function setPerPage(int $value): self { - $this->perPage = $value;return $this;} + public function setPerPage(int $value): self + { + $this->perPage = $value; + return $this; + } /** * @return int */ - public function getTotalPage(): int { - return $this->totalPage;} + public function getTotalPage(): int + { + return $this->totalPage; + } /** * @param int $value */ - public function setTotalPage(int $value): self { - $this->totalPage = $value;return $this;} + public function setTotalPage(int $value): self + { + $this->totalPage = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/User.php b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/User.php index 64f9ea0f2341..a8613f7aa9dd 100644 --- a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/User.php +++ b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/User.php @@ -27,39 +27,50 @@ class User extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->id = $values['id']; + ) { + $this->name = $values['name']; + $this->id = $values['id']; } /** * @return string */ - public function getName(): string { - return $this->name;} + public function getName(): string + { + return $this->name; + } /** * @param string $value */ - public function setName(string $value): self { - $this->name = $value;return $this;} + public function setName(string $value): self + { + $this->name = $value; + return $this; + } /** * @return int */ - public function getId(): int { - return $this->id;} + public function getId(): int + { + return $this->id; + } /** * @param int $value */ - public function setId(int $value): self { - $this->id = $value;return $this;} + public function setId(int $value): self + { + $this->id = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/UserListContainer.php b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/UserListContainer.php index 24e0a9374bfd..1790c9dedf2b 100644 --- a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/UserListContainer.php +++ b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/UserListContainer.php @@ -21,27 +21,32 @@ class UserListContainer extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->users = $values['users']; } /** * @return array */ - public function getUsers(): array { - return $this->users;} + public function getUsers(): array + { + return $this->users; + } /** * @param array $value */ - public function setUsers(array $value): self { - $this->users = $value;return $this;} + public function setUsers(array $value): self + { + $this->users = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/UserOptionalListContainer.php b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/UserOptionalListContainer.php index 80d941c0bf84..2c180d9eca1c 100644 --- a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/UserOptionalListContainer.php +++ b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/UserOptionalListContainer.php @@ -21,27 +21,32 @@ class UserOptionalListContainer extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->users = $values['users'] ?? null; } /** * @return ?array */ - public function getUsers(): ?array { - return $this->users;} + public function getUsers(): ?array + { + return $this->users; + } /** * @param ?array $value */ - public function setUsers(?array $value = null): self { - $this->users = $value;return $this;} + public function setUsers(?array $value = null): self + { + $this->users = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/UserOptionalListPage.php b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/UserOptionalListPage.php index 23ec212e4eaf..5a1860705e89 100644 --- a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/UserOptionalListPage.php +++ b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/UserOptionalListPage.php @@ -27,39 +27,50 @@ class UserOptionalListPage extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->data = $values['data'];$this->next = $values['next'] ?? null; + ) { + $this->data = $values['data']; + $this->next = $values['next'] ?? null; } /** * @return UserOptionalListContainer */ - public function getData(): UserOptionalListContainer { - return $this->data;} + public function getData(): UserOptionalListContainer + { + return $this->data; + } /** * @param UserOptionalListContainer $value */ - public function setData(UserOptionalListContainer $value): self { - $this->data = $value;return $this;} + public function setData(UserOptionalListContainer $value): self + { + $this->data = $value; + return $this; + } /** * @return ?string */ - public function getNext(): ?string { - return $this->next;} + public function getNext(): ?string + { + return $this->next; + } /** * @param ?string $value */ - public function setNext(?string $value = null): self { - $this->next = $value;return $this;} + public function setNext(?string $value = null): self + { + $this->next = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/UserPage.php b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/UserPage.php index e1f4d4971880..7104b79dfffe 100644 --- a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/UserPage.php +++ b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/UserPage.php @@ -27,39 +27,50 @@ class UserPage extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->data = $values['data'];$this->next = $values['next'] ?? null; + ) { + $this->data = $values['data']; + $this->next = $values['next'] ?? null; } /** * @return UserListContainer */ - public function getData(): UserListContainer { - return $this->data;} + public function getData(): UserListContainer + { + return $this->data; + } /** * @param UserListContainer $value */ - public function setData(UserListContainer $value): self { - $this->data = $value;return $this;} + public function setData(UserListContainer $value): self + { + $this->data = $value; + return $this; + } /** * @return ?string */ - public function getNext(): ?string { - return $this->next;} + public function getNext(): ?string + { + return $this->next; + } /** * @param ?string $value */ - public function setNext(?string $value = null): self { - $this->next = $value;return $this;} + public function setNext(?string $value = null): self + { + $this->next = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/UsernameContainer.php b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/UsernameContainer.php index 28ee2786d580..ca5f83b77715 100644 --- a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/UsernameContainer.php +++ b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/UsernameContainer.php @@ -21,27 +21,32 @@ class UsernameContainer extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->results = $values['results']; } /** * @return array */ - public function getResults(): array { - return $this->results;} + public function getResults(): array + { + return $this->results; + } /** * @param array $value */ - public function setResults(array $value): self { - $this->results = $value;return $this;} + public function setResults(array $value): self + { + $this->results = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/Users.php b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/Users.php index cd6aefc70f7b..549de3e53331 100644 --- a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/Users.php +++ b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/Users.php @@ -21,27 +21,32 @@ class Users extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->users = $values['users']; } /** * @return array */ - public function getUsers(): array { - return $this->users;} + public function getUsers(): array + { + return $this->users; + } /** * @param array $value */ - public function setUsers(array $value): self { - $this->users = $value;return $this;} + public function setUsers(array $value): self + { + $this->users = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/WithCursor.php b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/WithCursor.php index 0165161137dd..e466c70863bd 100644 --- a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/WithCursor.php +++ b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/WithCursor.php @@ -20,27 +20,32 @@ class WithCursor extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->cursor = $values['cursor'] ?? null; } /** * @return ?string */ - public function getCursor(): ?string { - return $this->cursor;} + public function getCursor(): ?string + { + return $this->cursor; + } /** * @param ?string $value */ - public function setCursor(?string $value = null): self { - $this->cursor = $value;return $this;} + public function setCursor(?string $value = null): self + { + $this->cursor = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/WithPage.php b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/WithPage.php index a804a45287b5..b2908166e324 100644 --- a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/WithPage.php +++ b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsers/Types/WithPage.php @@ -20,27 +20,32 @@ class WithPage extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->page = $values['page'] ?? null; } /** * @return ?int */ - public function getPage(): ?int { - return $this->page;} + public function getPage(): ?int + { + return $this->page; + } /** * @param ?int $value */ - public function setPage(?int $value = null): self { - $this->page = $value;return $this;} + public function setPage(?int $value = null): self + { + $this->page = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsersClient.php b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsersClient.php index 5d09a216e694..e624647ad42c 100644 --- a/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsersClient.php +++ b/seed/php-sdk/pagination/property-accessors/src/InlineUsers/InlineUsersClient.php @@ -5,7 +5,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class InlineUsersClient +class InlineUsersClient { /** * @var \Seed\InlineUsers\InlineUsers\InlineUsersClient $inlineUsers @@ -38,11 +38,10 @@ class InlineUsersClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; $this->inlineUsers = new \Seed\InlineUsers\InlineUsers\InlineUsersClient($this->client, $this->options); diff --git a/seed/php-sdk/pagination/property-accessors/src/SeedClient.php b/seed/php-sdk/pagination/property-accessors/src/SeedClient.php index 5b69acb295fb..360e481a019e 100644 --- a/seed/php-sdk/pagination/property-accessors/src/SeedClient.php +++ b/seed/php-sdk/pagination/property-accessors/src/SeedClient.php @@ -8,7 +8,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var ComplexClient $complex @@ -54,29 +54,28 @@ class SeedClient public function __construct( ?string $token = null, ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - if ($token != null){ + if ($token != null) { $defaultHeaders['Authorization'] = "Bearer $token"; } - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->complex = new ComplexClient($this->client, $this->options); $this->inlineUsers = new InlineUsersClient($this->client, $this->options); $this->users = new UsersClient($this->client, $this->options); diff --git a/seed/php-sdk/pagination/property-accessors/src/Types/UsernameCursor.php b/seed/php-sdk/pagination/property-accessors/src/Types/UsernameCursor.php index 61fa15522b1e..5c832a00650b 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Types/UsernameCursor.php +++ b/seed/php-sdk/pagination/property-accessors/src/Types/UsernameCursor.php @@ -20,27 +20,32 @@ class UsernameCursor extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->cursor = $values['cursor']; } /** * @return UsernamePage */ - public function getCursor(): UsernamePage { - return $this->cursor;} + public function getCursor(): UsernamePage + { + return $this->cursor; + } /** * @param UsernamePage $value */ - public function setCursor(UsernamePage $value): self { - $this->cursor = $value;return $this;} + public function setCursor(UsernamePage $value): self + { + $this->cursor = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/property-accessors/src/Types/UsernamePage.php b/seed/php-sdk/pagination/property-accessors/src/Types/UsernamePage.php index f5bfbd5338e3..2a6c8cb49b25 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Types/UsernamePage.php +++ b/seed/php-sdk/pagination/property-accessors/src/Types/UsernamePage.php @@ -28,39 +28,50 @@ class UsernamePage extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->after = $values['after'] ?? null;$this->data = $values['data']; + ) { + $this->after = $values['after'] ?? null; + $this->data = $values['data']; } /** * @return ?string */ - public function getAfter(): ?string { - return $this->after;} + public function getAfter(): ?string + { + return $this->after; + } /** * @param ?string $value */ - public function setAfter(?string $value = null): self { - $this->after = $value;return $this;} + public function setAfter(?string $value = null): self + { + $this->after = $value; + return $this; + } /** * @return array */ - public function getData(): array { - return $this->data;} + public function getData(): array + { + return $this->data; + } /** * @param array $value */ - public function setData(array $value): self { - $this->data = $value;return $this;} + public function setData(array $value): self + { + $this->data = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsernamesRequest.php b/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsernamesRequest.php index e855cdc5242f..42c6248fb67d 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsernamesRequest.php +++ b/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsernamesRequest.php @@ -21,20 +21,24 @@ class ListUsernamesRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->startingAfter = $values['startingAfter'] ?? null; } /** * @return ?string */ - public function getStartingAfter(): ?string { - return $this->startingAfter;} + public function getStartingAfter(): ?string + { + return $this->startingAfter; + } /** * @param ?string $value */ - public function setStartingAfter(?string $value = null): self { - $this->startingAfter = $value;return $this;} + public function setStartingAfter(?string $value = null): self + { + $this->startingAfter = $value; + return $this; + } } diff --git a/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsernamesWithOptionalResponseRequest.php b/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsernamesWithOptionalResponseRequest.php index af307f60e94a..73305ea9392c 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsernamesWithOptionalResponseRequest.php +++ b/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsernamesWithOptionalResponseRequest.php @@ -21,20 +21,24 @@ class ListUsernamesWithOptionalResponseRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->startingAfter = $values['startingAfter'] ?? null; } /** * @return ?string */ - public function getStartingAfter(): ?string { - return $this->startingAfter;} + public function getStartingAfter(): ?string + { + return $this->startingAfter; + } /** * @param ?string $value */ - public function setStartingAfter(?string $value = null): self { - $this->startingAfter = $value;return $this;} + public function setStartingAfter(?string $value = null): self + { + $this->startingAfter = $value; + return $this; + } } diff --git a/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsersBodyCursorPaginationRequest.php b/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsersBodyCursorPaginationRequest.php index 751cf46669b2..d063069b672d 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsersBodyCursorPaginationRequest.php +++ b/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsersBodyCursorPaginationRequest.php @@ -24,20 +24,24 @@ class ListUsersBodyCursorPaginationRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->pagination = $values['pagination'] ?? null; } /** * @return ?WithCursor */ - public function getPagination(): ?WithCursor { - return $this->pagination;} + public function getPagination(): ?WithCursor + { + return $this->pagination; + } /** * @param ?WithCursor $value */ - public function setPagination(?WithCursor $value = null): self { - $this->pagination = $value;return $this;} + public function setPagination(?WithCursor $value = null): self + { + $this->pagination = $value; + return $this; + } } diff --git a/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsersBodyOffsetPaginationRequest.php b/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsersBodyOffsetPaginationRequest.php index b4a3f5d3d5b6..95f9cfcfd919 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsersBodyOffsetPaginationRequest.php +++ b/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsersBodyOffsetPaginationRequest.php @@ -24,20 +24,24 @@ class ListUsersBodyOffsetPaginationRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->pagination = $values['pagination'] ?? null; } /** * @return ?WithPage */ - public function getPagination(): ?WithPage { - return $this->pagination;} + public function getPagination(): ?WithPage + { + return $this->pagination; + } /** * @param ?WithPage $value */ - public function setPagination(?WithPage $value = null): self { - $this->pagination = $value;return $this;} + public function setPagination(?WithPage $value = null): self + { + $this->pagination = $value; + return $this; + } } diff --git a/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsersCursorPaginationRequest.php b/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsersCursorPaginationRequest.php index 2867e78ffd19..5321472ee542 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsersCursorPaginationRequest.php +++ b/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsersCursorPaginationRequest.php @@ -40,56 +40,78 @@ class ListUsersCursorPaginationRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->page = $values['page'] ?? null;$this->perPage = $values['perPage'] ?? null;$this->order = $values['order'] ?? null;$this->startingAfter = $values['startingAfter'] ?? null; + ) { + $this->page = $values['page'] ?? null; + $this->perPage = $values['perPage'] ?? null; + $this->order = $values['order'] ?? null; + $this->startingAfter = $values['startingAfter'] ?? null; } /** * @return ?int */ - public function getPage(): ?int { - return $this->page;} + public function getPage(): ?int + { + return $this->page; + } /** * @param ?int $value */ - public function setPage(?int $value = null): self { - $this->page = $value;return $this;} + public function setPage(?int $value = null): self + { + $this->page = $value; + return $this; + } /** * @return ?int */ - public function getPerPage(): ?int { - return $this->perPage;} + public function getPerPage(): ?int + { + return $this->perPage; + } /** * @param ?int $value */ - public function setPerPage(?int $value = null): self { - $this->perPage = $value;return $this;} + public function setPerPage(?int $value = null): self + { + $this->perPage = $value; + return $this; + } /** * @return ?value-of */ - public function getOrder(): ?string { - return $this->order;} + public function getOrder(): ?string + { + return $this->order; + } /** * @param ?value-of $value */ - public function setOrder(?string $value = null): self { - $this->order = $value;return $this;} + public function setOrder(?string $value = null): self + { + $this->order = $value; + return $this; + } /** * @return ?string */ - public function getStartingAfter(): ?string { - return $this->startingAfter;} + public function getStartingAfter(): ?string + { + return $this->startingAfter; + } /** * @param ?string $value */ - public function setStartingAfter(?string $value = null): self { - $this->startingAfter = $value;return $this;} + public function setStartingAfter(?string $value = null): self + { + $this->startingAfter = $value; + return $this; + } } diff --git a/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsersDoubleOffsetPaginationRequest.php b/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsersDoubleOffsetPaginationRequest.php index 5d0d77565737..936ec2b57fee 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsersDoubleOffsetPaginationRequest.php +++ b/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsersDoubleOffsetPaginationRequest.php @@ -40,56 +40,78 @@ class ListUsersDoubleOffsetPaginationRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->page = $values['page'] ?? null;$this->perPage = $values['perPage'] ?? null;$this->order = $values['order'] ?? null;$this->startingAfter = $values['startingAfter'] ?? null; + ) { + $this->page = $values['page'] ?? null; + $this->perPage = $values['perPage'] ?? null; + $this->order = $values['order'] ?? null; + $this->startingAfter = $values['startingAfter'] ?? null; } /** * @return ?float */ - public function getPage(): ?float { - return $this->page;} + public function getPage(): ?float + { + return $this->page; + } /** * @param ?float $value */ - public function setPage(?float $value = null): self { - $this->page = $value;return $this;} + public function setPage(?float $value = null): self + { + $this->page = $value; + return $this; + } /** * @return ?float */ - public function getPerPage(): ?float { - return $this->perPage;} + public function getPerPage(): ?float + { + return $this->perPage; + } /** * @param ?float $value */ - public function setPerPage(?float $value = null): self { - $this->perPage = $value;return $this;} + public function setPerPage(?float $value = null): self + { + $this->perPage = $value; + return $this; + } /** * @return ?value-of */ - public function getOrder(): ?string { - return $this->order;} + public function getOrder(): ?string + { + return $this->order; + } /** * @param ?value-of $value */ - public function setOrder(?string $value = null): self { - $this->order = $value;return $this;} + public function setOrder(?string $value = null): self + { + $this->order = $value; + return $this; + } /** * @return ?string */ - public function getStartingAfter(): ?string { - return $this->startingAfter;} + public function getStartingAfter(): ?string + { + return $this->startingAfter; + } /** * @param ?string $value */ - public function setStartingAfter(?string $value = null): self { - $this->startingAfter = $value;return $this;} + public function setStartingAfter(?string $value = null): self + { + $this->startingAfter = $value; + return $this; + } } diff --git a/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsersExtendedRequest.php b/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsersExtendedRequest.php index 6358745ecdbd..40d0683bb7e6 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsersExtendedRequest.php +++ b/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsersExtendedRequest.php @@ -18,20 +18,24 @@ class ListUsersExtendedRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->cursor = $values['cursor'] ?? null; } /** * @return ?string */ - public function getCursor(): ?string { - return $this->cursor;} + public function getCursor(): ?string + { + return $this->cursor; + } /** * @param ?string $value */ - public function setCursor(?string $value = null): self { - $this->cursor = $value;return $this;} + public function setCursor(?string $value = null): self + { + $this->cursor = $value; + return $this; + } } diff --git a/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsersExtendedRequestForOptionalData.php b/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsersExtendedRequestForOptionalData.php index 6ee522a44962..aa366266ae19 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsersExtendedRequestForOptionalData.php +++ b/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsersExtendedRequestForOptionalData.php @@ -18,20 +18,24 @@ class ListUsersExtendedRequestForOptionalData extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->cursor = $values['cursor'] ?? null; } /** * @return ?string */ - public function getCursor(): ?string { - return $this->cursor;} + public function getCursor(): ?string + { + return $this->cursor; + } /** * @param ?string $value */ - public function setCursor(?string $value = null): self { - $this->cursor = $value;return $this;} + public function setCursor(?string $value = null): self + { + $this->cursor = $value; + return $this; + } } diff --git a/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsersMixedTypeCursorPaginationRequest.php b/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsersMixedTypeCursorPaginationRequest.php index 2da8787e22ea..bcda4b14c866 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsersMixedTypeCursorPaginationRequest.php +++ b/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsersMixedTypeCursorPaginationRequest.php @@ -18,20 +18,24 @@ class ListUsersMixedTypeCursorPaginationRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->cursor = $values['cursor'] ?? null; } /** * @return ?string */ - public function getCursor(): ?string { - return $this->cursor;} + public function getCursor(): ?string + { + return $this->cursor; + } /** * @param ?string $value */ - public function setCursor(?string $value = null): self { - $this->cursor = $value;return $this;} + public function setCursor(?string $value = null): self + { + $this->cursor = $value; + return $this; + } } diff --git a/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsersOffsetPaginationRequest.php b/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsersOffsetPaginationRequest.php index 0211cb5ad4ed..e4a245461ea9 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsersOffsetPaginationRequest.php +++ b/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsersOffsetPaginationRequest.php @@ -40,56 +40,78 @@ class ListUsersOffsetPaginationRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->page = $values['page'] ?? null;$this->perPage = $values['perPage'] ?? null;$this->order = $values['order'] ?? null;$this->startingAfter = $values['startingAfter'] ?? null; + ) { + $this->page = $values['page'] ?? null; + $this->perPage = $values['perPage'] ?? null; + $this->order = $values['order'] ?? null; + $this->startingAfter = $values['startingAfter'] ?? null; } /** * @return ?int */ - public function getPage(): ?int { - return $this->page;} + public function getPage(): ?int + { + return $this->page; + } /** * @param ?int $value */ - public function setPage(?int $value = null): self { - $this->page = $value;return $this;} + public function setPage(?int $value = null): self + { + $this->page = $value; + return $this; + } /** * @return ?int */ - public function getPerPage(): ?int { - return $this->perPage;} + public function getPerPage(): ?int + { + return $this->perPage; + } /** * @param ?int $value */ - public function setPerPage(?int $value = null): self { - $this->perPage = $value;return $this;} + public function setPerPage(?int $value = null): self + { + $this->perPage = $value; + return $this; + } /** * @return ?value-of */ - public function getOrder(): ?string { - return $this->order;} + public function getOrder(): ?string + { + return $this->order; + } /** * @param ?value-of $value */ - public function setOrder(?string $value = null): self { - $this->order = $value;return $this;} + public function setOrder(?string $value = null): self + { + $this->order = $value; + return $this; + } /** * @return ?string */ - public function getStartingAfter(): ?string { - return $this->startingAfter;} + public function getStartingAfter(): ?string + { + return $this->startingAfter; + } /** * @param ?string $value */ - public function setStartingAfter(?string $value = null): self { - $this->startingAfter = $value;return $this;} + public function setStartingAfter(?string $value = null): self + { + $this->startingAfter = $value; + return $this; + } } diff --git a/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsersOffsetStepPaginationRequest.php b/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsersOffsetStepPaginationRequest.php index e19e20964ed9..cad356f04fc4 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsersOffsetStepPaginationRequest.php +++ b/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsersOffsetStepPaginationRequest.php @@ -35,44 +35,60 @@ class ListUsersOffsetStepPaginationRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->page = $values['page'] ?? null;$this->limit = $values['limit'] ?? null;$this->order = $values['order'] ?? null; + ) { + $this->page = $values['page'] ?? null; + $this->limit = $values['limit'] ?? null; + $this->order = $values['order'] ?? null; } /** * @return ?int */ - public function getPage(): ?int { - return $this->page;} + public function getPage(): ?int + { + return $this->page; + } /** * @param ?int $value */ - public function setPage(?int $value = null): self { - $this->page = $value;return $this;} + public function setPage(?int $value = null): self + { + $this->page = $value; + return $this; + } /** * @return ?int */ - public function getLimit(): ?int { - return $this->limit;} + public function getLimit(): ?int + { + return $this->limit; + } /** * @param ?int $value */ - public function setLimit(?int $value = null): self { - $this->limit = $value;return $this;} + public function setLimit(?int $value = null): self + { + $this->limit = $value; + return $this; + } /** * @return ?value-of */ - public function getOrder(): ?string { - return $this->order;} + public function getOrder(): ?string + { + return $this->order; + } /** * @param ?value-of $value */ - public function setOrder(?string $value = null): self { - $this->order = $value;return $this;} + public function setOrder(?string $value = null): self + { + $this->order = $value; + return $this; + } } diff --git a/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsersOptionalDataRequest.php b/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsersOptionalDataRequest.php new file mode 100644 index 000000000000..2fa99197c52f --- /dev/null +++ b/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListUsersOptionalDataRequest.php @@ -0,0 +1,41 @@ +page = $values['page'] ?? null; + } + + /** + * @return ?int + */ + public function getPage(): ?int + { + return $this->page; + } + + /** + * @param ?int $value + */ + public function setPage(?int $value = null): self + { + $this->page = $value; + return $this; + } +} diff --git a/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListWithGlobalConfigRequest.php b/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListWithGlobalConfigRequest.php index cfbf7716bc93..11cd3dfba852 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListWithGlobalConfigRequest.php +++ b/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListWithGlobalConfigRequest.php @@ -18,20 +18,24 @@ class ListWithGlobalConfigRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->offset = $values['offset'] ?? null; } /** * @return ?int */ - public function getOffset(): ?int { - return $this->offset;} + public function getOffset(): ?int + { + return $this->offset; + } /** * @param ?int $value */ - public function setOffset(?int $value = null): self { - $this->offset = $value;return $this;} + public function setOffset(?int $value = null): self + { + $this->offset = $value; + return $this; + } } diff --git a/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListWithOffsetPaginationHasNextPageRequest.php b/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListWithOffsetPaginationHasNextPageRequest.php index 9d34c240d80d..fa2871157703 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListWithOffsetPaginationHasNextPageRequest.php +++ b/seed/php-sdk/pagination/property-accessors/src/Users/Requests/ListWithOffsetPaginationHasNextPageRequest.php @@ -35,44 +35,60 @@ class ListWithOffsetPaginationHasNextPageRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->page = $values['page'] ?? null;$this->limit = $values['limit'] ?? null;$this->order = $values['order'] ?? null; + ) { + $this->page = $values['page'] ?? null; + $this->limit = $values['limit'] ?? null; + $this->order = $values['order'] ?? null; } /** * @return ?int */ - public function getPage(): ?int { - return $this->page;} + public function getPage(): ?int + { + return $this->page; + } /** * @param ?int $value */ - public function setPage(?int $value = null): self { - $this->page = $value;return $this;} + public function setPage(?int $value = null): self + { + $this->page = $value; + return $this; + } /** * @return ?int */ - public function getLimit(): ?int { - return $this->limit;} + public function getLimit(): ?int + { + return $this->limit; + } /** * @param ?int $value */ - public function setLimit(?int $value = null): self { - $this->limit = $value;return $this;} + public function setLimit(?int $value = null): self + { + $this->limit = $value; + return $this; + } /** * @return ?value-of */ - public function getOrder(): ?string { - return $this->order;} + public function getOrder(): ?string + { + return $this->order; + } /** * @param ?value-of $value */ - public function setOrder(?string $value = null): self { - $this->order = $value;return $this;} + public function setOrder(?string $value = null): self + { + $this->order = $value; + return $this; + } } diff --git a/seed/php-sdk/pagination/property-accessors/src/Users/Traits/UserOptionalListPage.php b/seed/php-sdk/pagination/property-accessors/src/Users/Traits/UserOptionalListPage.php index d0d44755da3b..fc9a2c37c8c5 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Users/Traits/UserOptionalListPage.php +++ b/seed/php-sdk/pagination/property-accessors/src/Users/Traits/UserOptionalListPage.php @@ -9,7 +9,7 @@ * @property UserOptionalListContainer $data * @property ?string $next */ -trait UserOptionalListPage +trait UserOptionalListPage { /** * @var UserOptionalListContainer $data @@ -26,24 +26,34 @@ trait UserOptionalListPage /** * @return UserOptionalListContainer */ - public function getData(): UserOptionalListContainer { - return $this->data;} + public function getData(): UserOptionalListContainer + { + return $this->data; + } /** * @param UserOptionalListContainer $value */ - public function setData(UserOptionalListContainer $value): self { - $this->data = $value;return $this;} + public function setData(UserOptionalListContainer $value): self + { + $this->data = $value; + return $this; + } /** * @return ?string */ - public function getNext(): ?string { - return $this->next;} + public function getNext(): ?string + { + return $this->next; + } /** * @param ?string $value */ - public function setNext(?string $value = null): self { - $this->next = $value;return $this;} + public function setNext(?string $value = null): self + { + $this->next = $value; + return $this; + } } diff --git a/seed/php-sdk/pagination/property-accessors/src/Users/Traits/UserPage.php b/seed/php-sdk/pagination/property-accessors/src/Users/Traits/UserPage.php index 49c28980708a..613cd8e6c83c 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Users/Traits/UserPage.php +++ b/seed/php-sdk/pagination/property-accessors/src/Users/Traits/UserPage.php @@ -9,7 +9,7 @@ * @property UserListContainer $data * @property ?string $next */ -trait UserPage +trait UserPage { /** * @var UserListContainer $data @@ -26,24 +26,34 @@ trait UserPage /** * @return UserListContainer */ - public function getData(): UserListContainer { - return $this->data;} + public function getData(): UserListContainer + { + return $this->data; + } /** * @param UserListContainer $value */ - public function setData(UserListContainer $value): self { - $this->data = $value;return $this;} + public function setData(UserListContainer $value): self + { + $this->data = $value; + return $this; + } /** * @return ?string */ - public function getNext(): ?string { - return $this->next;} + public function getNext(): ?string + { + return $this->next; + } /** * @param ?string $value */ - public function setNext(?string $value = null): self { - $this->next = $value;return $this;} + public function setNext(?string $value = null): self + { + $this->next = $value; + return $this; + } } diff --git a/seed/php-sdk/pagination/property-accessors/src/Users/Types/ListUsersExtendedOptionalListResponse.php b/seed/php-sdk/pagination/property-accessors/src/Users/Types/ListUsersExtendedOptionalListResponse.php index 8a0cc779ff03..822e5f16ed53 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Users/Types/ListUsersExtendedOptionalListResponse.php +++ b/seed/php-sdk/pagination/property-accessors/src/Users/Types/ListUsersExtendedOptionalListResponse.php @@ -25,27 +25,34 @@ class ListUsersExtendedOptionalListResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->data = $values['data'];$this->next = $values['next'] ?? null;$this->totalCount = $values['totalCount']; + ) { + $this->data = $values['data']; + $this->next = $values['next'] ?? null; + $this->totalCount = $values['totalCount']; } /** * @return int */ - public function getTotalCount(): int { - return $this->totalCount;} + public function getTotalCount(): int + { + return $this->totalCount; + } /** * @param int $value */ - public function setTotalCount(int $value): self { - $this->totalCount = $value;return $this;} + public function setTotalCount(int $value): self + { + $this->totalCount = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/property-accessors/src/Users/Types/ListUsersExtendedResponse.php b/seed/php-sdk/pagination/property-accessors/src/Users/Types/ListUsersExtendedResponse.php index 7d30dc1e51cc..a8bc40eecd23 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Users/Types/ListUsersExtendedResponse.php +++ b/seed/php-sdk/pagination/property-accessors/src/Users/Types/ListUsersExtendedResponse.php @@ -25,27 +25,34 @@ class ListUsersExtendedResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->data = $values['data'];$this->next = $values['next'] ?? null;$this->totalCount = $values['totalCount']; + ) { + $this->data = $values['data']; + $this->next = $values['next'] ?? null; + $this->totalCount = $values['totalCount']; } /** * @return int */ - public function getTotalCount(): int { - return $this->totalCount;} + public function getTotalCount(): int + { + return $this->totalCount; + } /** * @param int $value */ - public function setTotalCount(int $value): self { - $this->totalCount = $value;return $this;} + public function setTotalCount(int $value): self + { + $this->totalCount = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/property-accessors/src/Users/Types/ListUsersMixedTypePaginationResponse.php b/seed/php-sdk/pagination/property-accessors/src/Users/Types/ListUsersMixedTypePaginationResponse.php index 5e5af1601f09..508155b28695 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Users/Types/ListUsersMixedTypePaginationResponse.php +++ b/seed/php-sdk/pagination/property-accessors/src/Users/Types/ListUsersMixedTypePaginationResponse.php @@ -28,39 +28,50 @@ class ListUsersMixedTypePaginationResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->next = $values['next'];$this->data = $values['data']; + ) { + $this->next = $values['next']; + $this->data = $values['data']; } /** * @return string */ - public function getNext(): string { - return $this->next;} + public function getNext(): string + { + return $this->next; + } /** * @param string $value */ - public function setNext(string $value): self { - $this->next = $value;return $this;} + public function setNext(string $value): self + { + $this->next = $value; + return $this; + } /** * @return array */ - public function getData(): array { - return $this->data;} + public function getData(): array + { + return $this->data; + } /** * @param array $value */ - public function setData(array $value): self { - $this->data = $value;return $this;} + public function setData(array $value): self + { + $this->data = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/property-accessors/src/Users/Types/ListUsersOptionalDataPaginationResponse.php b/seed/php-sdk/pagination/property-accessors/src/Users/Types/ListUsersOptionalDataPaginationResponse.php new file mode 100644 index 000000000000..1d9316bdc611 --- /dev/null +++ b/seed/php-sdk/pagination/property-accessors/src/Users/Types/ListUsersOptionalDataPaginationResponse.php @@ -0,0 +1,127 @@ + $data + */ + #[JsonProperty('data'), ArrayType([User::class])] + private ?array $data; + + /** + * @param array{ + * totalCount: int, + * hasNextPage?: ?bool, + * page?: ?Page, + * data?: ?array, + * } $values + */ + public function __construct( + array $values, + ) { + $this->hasNextPage = $values['hasNextPage'] ?? null; + $this->page = $values['page'] ?? null; + $this->totalCount = $values['totalCount']; + $this->data = $values['data'] ?? null; + } + + /** + * @return ?bool + */ + public function getHasNextPage(): ?bool + { + return $this->hasNextPage; + } + + /** + * @param ?bool $value + */ + public function setHasNextPage(?bool $value = null): self + { + $this->hasNextPage = $value; + return $this; + } + + /** + * @return ?Page + */ + public function getPage(): ?Page + { + return $this->page; + } + + /** + * @param ?Page $value + */ + public function setPage(?Page $value = null): self + { + $this->page = $value; + return $this; + } + + /** + * @return int + */ + public function getTotalCount(): int + { + return $this->totalCount; + } + + /** + * @param int $value + */ + public function setTotalCount(int $value): self + { + $this->totalCount = $value; + return $this; + } + + /** + * @return ?array + */ + public function getData(): ?array + { + return $this->data; + } + + /** + * @param ?array $value + */ + public function setData(?array $value = null): self + { + $this->data = $value; + return $this; + } + + /** + * @return string + */ + public function __toString(): string + { + return $this->toJson(); + } +} diff --git a/seed/php-sdk/pagination/property-accessors/src/Users/Types/ListUsersPaginationResponse.php b/seed/php-sdk/pagination/property-accessors/src/Users/Types/ListUsersPaginationResponse.php index bc461a2b0de6..1574cd0f0cda 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Users/Types/ListUsersPaginationResponse.php +++ b/seed/php-sdk/pagination/property-accessors/src/Users/Types/ListUsersPaginationResponse.php @@ -42,63 +42,86 @@ class ListUsersPaginationResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->hasNextPage = $values['hasNextPage'] ?? null;$this->page = $values['page'] ?? null;$this->totalCount = $values['totalCount'];$this->data = $values['data']; + ) { + $this->hasNextPage = $values['hasNextPage'] ?? null; + $this->page = $values['page'] ?? null; + $this->totalCount = $values['totalCount']; + $this->data = $values['data']; } /** * @return ?bool */ - public function getHasNextPage(): ?bool { - return $this->hasNextPage;} + public function getHasNextPage(): ?bool + { + return $this->hasNextPage; + } /** * @param ?bool $value */ - public function setHasNextPage(?bool $value = null): self { - $this->hasNextPage = $value;return $this;} + public function setHasNextPage(?bool $value = null): self + { + $this->hasNextPage = $value; + return $this; + } /** * @return ?Page */ - public function getPage(): ?Page { - return $this->page;} + public function getPage(): ?Page + { + return $this->page; + } /** * @param ?Page $value */ - public function setPage(?Page $value = null): self { - $this->page = $value;return $this;} + public function setPage(?Page $value = null): self + { + $this->page = $value; + return $this; + } /** * @return int */ - public function getTotalCount(): int { - return $this->totalCount;} + public function getTotalCount(): int + { + return $this->totalCount; + } /** * @param int $value */ - public function setTotalCount(int $value): self { - $this->totalCount = $value;return $this;} + public function setTotalCount(int $value): self + { + $this->totalCount = $value; + return $this; + } /** * @return array */ - public function getData(): array { - return $this->data;} + public function getData(): array + { + return $this->data; + } /** * @param array $value */ - public function setData(array $value): self { - $this->data = $value;return $this;} + public function setData(array $value): self + { + $this->data = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/property-accessors/src/Users/Types/NextPage.php b/seed/php-sdk/pagination/property-accessors/src/Users/Types/NextPage.php index 2799c5a9da4c..a85a1aefa466 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Users/Types/NextPage.php +++ b/seed/php-sdk/pagination/property-accessors/src/Users/Types/NextPage.php @@ -27,39 +27,50 @@ class NextPage extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->page = $values['page'];$this->startingAfter = $values['startingAfter']; + ) { + $this->page = $values['page']; + $this->startingAfter = $values['startingAfter']; } /** * @return int */ - public function getPage(): int { - return $this->page;} + public function getPage(): int + { + return $this->page; + } /** * @param int $value */ - public function setPage(int $value): self { - $this->page = $value;return $this;} + public function setPage(int $value): self + { + $this->page = $value; + return $this; + } /** * @return string */ - public function getStartingAfter(): string { - return $this->startingAfter;} + public function getStartingAfter(): string + { + return $this->startingAfter; + } /** * @param string $value */ - public function setStartingAfter(string $value): self { - $this->startingAfter = $value;return $this;} + public function setStartingAfter(string $value): self + { + $this->startingAfter = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/property-accessors/src/Users/Types/Order.php b/seed/php-sdk/pagination/property-accessors/src/Users/Types/Order.php index 20c3f8b53086..47f5a3ff6243 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Users/Types/Order.php +++ b/seed/php-sdk/pagination/property-accessors/src/Users/Types/Order.php @@ -2,8 +2,8 @@ namespace Seed\Users\Types; -enum Order - : string { +enum Order: string +{ case Asc = "asc"; case Desc = "desc"; } diff --git a/seed/php-sdk/pagination/property-accessors/src/Users/Types/Page.php b/seed/php-sdk/pagination/property-accessors/src/Users/Types/Page.php index bbcb235d42f9..1edd6abbb24f 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Users/Types/Page.php +++ b/seed/php-sdk/pagination/property-accessors/src/Users/Types/Page.php @@ -41,63 +41,86 @@ class Page extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->page = $values['page'];$this->next = $values['next'] ?? null;$this->perPage = $values['perPage'];$this->totalPage = $values['totalPage']; + ) { + $this->page = $values['page']; + $this->next = $values['next'] ?? null; + $this->perPage = $values['perPage']; + $this->totalPage = $values['totalPage']; } /** * @return int */ - public function getPage(): int { - return $this->page;} + public function getPage(): int + { + return $this->page; + } /** * @param int $value */ - public function setPage(int $value): self { - $this->page = $value;return $this;} + public function setPage(int $value): self + { + $this->page = $value; + return $this; + } /** * @return ?NextPage */ - public function getNext(): ?NextPage { - return $this->next;} + public function getNext(): ?NextPage + { + return $this->next; + } /** * @param ?NextPage $value */ - public function setNext(?NextPage $value = null): self { - $this->next = $value;return $this;} + public function setNext(?NextPage $value = null): self + { + $this->next = $value; + return $this; + } /** * @return int */ - public function getPerPage(): int { - return $this->perPage;} + public function getPerPage(): int + { + return $this->perPage; + } /** * @param int $value */ - public function setPerPage(int $value): self { - $this->perPage = $value;return $this;} + public function setPerPage(int $value): self + { + $this->perPage = $value; + return $this; + } /** * @return int */ - public function getTotalPage(): int { - return $this->totalPage;} + public function getTotalPage(): int + { + return $this->totalPage; + } /** * @param int $value */ - public function setTotalPage(int $value): self { - $this->totalPage = $value;return $this;} + public function setTotalPage(int $value): self + { + $this->totalPage = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/property-accessors/src/Users/Types/User.php b/seed/php-sdk/pagination/property-accessors/src/Users/Types/User.php index dc16d229880c..82e575f84d93 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Users/Types/User.php +++ b/seed/php-sdk/pagination/property-accessors/src/Users/Types/User.php @@ -27,39 +27,50 @@ class User extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->id = $values['id']; + ) { + $this->name = $values['name']; + $this->id = $values['id']; } /** * @return string */ - public function getName(): string { - return $this->name;} + public function getName(): string + { + return $this->name; + } /** * @param string $value */ - public function setName(string $value): self { - $this->name = $value;return $this;} + public function setName(string $value): self + { + $this->name = $value; + return $this; + } /** * @return int */ - public function getId(): int { - return $this->id;} + public function getId(): int + { + return $this->id; + } /** * @param int $value */ - public function setId(int $value): self { - $this->id = $value;return $this;} + public function setId(int $value): self + { + $this->id = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/property-accessors/src/Users/Types/UserListContainer.php b/seed/php-sdk/pagination/property-accessors/src/Users/Types/UserListContainer.php index 3aeaacab66a9..7f1a4fc16dc9 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Users/Types/UserListContainer.php +++ b/seed/php-sdk/pagination/property-accessors/src/Users/Types/UserListContainer.php @@ -21,27 +21,32 @@ class UserListContainer extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->users = $values['users']; } /** * @return array */ - public function getUsers(): array { - return $this->users;} + public function getUsers(): array + { + return $this->users; + } /** * @param array $value */ - public function setUsers(array $value): self { - $this->users = $value;return $this;} + public function setUsers(array $value): self + { + $this->users = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/property-accessors/src/Users/Types/UserOptionalListContainer.php b/seed/php-sdk/pagination/property-accessors/src/Users/Types/UserOptionalListContainer.php index 1ca5c648b704..2420f372a47a 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Users/Types/UserOptionalListContainer.php +++ b/seed/php-sdk/pagination/property-accessors/src/Users/Types/UserOptionalListContainer.php @@ -21,27 +21,32 @@ class UserOptionalListContainer extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->users = $values['users'] ?? null; } /** * @return ?array */ - public function getUsers(): ?array { - return $this->users;} + public function getUsers(): ?array + { + return $this->users; + } /** * @param ?array $value */ - public function setUsers(?array $value = null): self { - $this->users = $value;return $this;} + public function setUsers(?array $value = null): self + { + $this->users = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/property-accessors/src/Users/Types/UserOptionalListPage.php b/seed/php-sdk/pagination/property-accessors/src/Users/Types/UserOptionalListPage.php index 4b9e716c872f..03381de2a8f0 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Users/Types/UserOptionalListPage.php +++ b/seed/php-sdk/pagination/property-accessors/src/Users/Types/UserOptionalListPage.php @@ -27,39 +27,50 @@ class UserOptionalListPage extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->data = $values['data'];$this->next = $values['next'] ?? null; + ) { + $this->data = $values['data']; + $this->next = $values['next'] ?? null; } /** * @return UserOptionalListContainer */ - public function getData(): UserOptionalListContainer { - return $this->data;} + public function getData(): UserOptionalListContainer + { + return $this->data; + } /** * @param UserOptionalListContainer $value */ - public function setData(UserOptionalListContainer $value): self { - $this->data = $value;return $this;} + public function setData(UserOptionalListContainer $value): self + { + $this->data = $value; + return $this; + } /** * @return ?string */ - public function getNext(): ?string { - return $this->next;} + public function getNext(): ?string + { + return $this->next; + } /** * @param ?string $value */ - public function setNext(?string $value = null): self { - $this->next = $value;return $this;} + public function setNext(?string $value = null): self + { + $this->next = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/property-accessors/src/Users/Types/UserPage.php b/seed/php-sdk/pagination/property-accessors/src/Users/Types/UserPage.php index d7854aae1598..04bdec10bf45 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Users/Types/UserPage.php +++ b/seed/php-sdk/pagination/property-accessors/src/Users/Types/UserPage.php @@ -27,39 +27,50 @@ class UserPage extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->data = $values['data'];$this->next = $values['next'] ?? null; + ) { + $this->data = $values['data']; + $this->next = $values['next'] ?? null; } /** * @return UserListContainer */ - public function getData(): UserListContainer { - return $this->data;} + public function getData(): UserListContainer + { + return $this->data; + } /** * @param UserListContainer $value */ - public function setData(UserListContainer $value): self { - $this->data = $value;return $this;} + public function setData(UserListContainer $value): self + { + $this->data = $value; + return $this; + } /** * @return ?string */ - public function getNext(): ?string { - return $this->next;} + public function getNext(): ?string + { + return $this->next; + } /** * @param ?string $value */ - public function setNext(?string $value = null): self { - $this->next = $value;return $this;} + public function setNext(?string $value = null): self + { + $this->next = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/property-accessors/src/Users/Types/UsernameContainer.php b/seed/php-sdk/pagination/property-accessors/src/Users/Types/UsernameContainer.php index 25d652007f64..51a6f602fb05 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Users/Types/UsernameContainer.php +++ b/seed/php-sdk/pagination/property-accessors/src/Users/Types/UsernameContainer.php @@ -21,27 +21,32 @@ class UsernameContainer extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->results = $values['results']; } /** * @return array */ - public function getResults(): array { - return $this->results;} + public function getResults(): array + { + return $this->results; + } /** * @param array $value */ - public function setResults(array $value): self { - $this->results = $value;return $this;} + public function setResults(array $value): self + { + $this->results = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/property-accessors/src/Users/Types/WithCursor.php b/seed/php-sdk/pagination/property-accessors/src/Users/Types/WithCursor.php index b61ac10b9057..6f9917edc944 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Users/Types/WithCursor.php +++ b/seed/php-sdk/pagination/property-accessors/src/Users/Types/WithCursor.php @@ -20,27 +20,32 @@ class WithCursor extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->cursor = $values['cursor'] ?? null; } /** * @return ?string */ - public function getCursor(): ?string { - return $this->cursor;} + public function getCursor(): ?string + { + return $this->cursor; + } /** * @param ?string $value */ - public function setCursor(?string $value = null): self { - $this->cursor = $value;return $this;} + public function setCursor(?string $value = null): self + { + $this->cursor = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/property-accessors/src/Users/Types/WithPage.php b/seed/php-sdk/pagination/property-accessors/src/Users/Types/WithPage.php index 9def2a93ea11..279a3f4bbf8a 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Users/Types/WithPage.php +++ b/seed/php-sdk/pagination/property-accessors/src/Users/Types/WithPage.php @@ -20,27 +20,32 @@ class WithPage extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->page = $values['page'] ?? null; } /** * @return ?int */ - public function getPage(): ?int { - return $this->page;} + public function getPage(): ?int + { + return $this->page; + } /** * @param ?int $value */ - public function setPage(?int $value = null): self { - $this->page = $value;return $this;} + public function setPage(?int $value = null): self + { + $this->page = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/pagination/property-accessors/src/Users/UsersClient.php b/seed/php-sdk/pagination/property-accessors/src/Users/UsersClient.php index c1ec45614c00..630af343dda7 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Users/UsersClient.php +++ b/seed/php-sdk/pagination/property-accessors/src/Users/UsersClient.php @@ -28,6 +28,8 @@ use Seed\Users\Requests\ListUsernamesWithOptionalResponseRequest; use Seed\Users\Requests\ListWithGlobalConfigRequest; use Seed\Users\Types\UsernameContainer; +use Seed\Users\Requests\ListUsersOptionalDataRequest; +use Seed\Users\Types\ListUsersOptionalDataPaginationResponse; use Seed\Exceptions\SeedException; use Seed\Exceptions\SeedApiException; use Seed\Core\Json\JsonApiRequest; @@ -36,7 +38,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class UsersClient +class UsersClient { /** * @var array{ @@ -64,11 +66,10 @@ class UsersClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -85,11 +86,12 @@ function __construct( * } $options * @return Pager */ - public function listWithCursorPagination(ListUsersCursorPaginationRequest $request = new ListUsersCursorPaginationRequest(), ?array $options = null): Pager { + public function listWithCursorPagination(ListUsersCursorPaginationRequest $request = new ListUsersCursorPaginationRequest(), ?array $options = null): Pager + { return new CursorPager( request: $request, - getNextPage: fn(ListUsersCursorPaginationRequest $request) => $this->_listWithCursorPagination($request, $options), - setCursor: function (ListUsersCursorPaginationRequest $request, string $cursor) { + getNextPage: fn (ListUsersCursorPaginationRequest $request) => $this->_listWithCursorPagination($request, $options), + setCursor: function (ListUsersCursorPaginationRequest $request, string $cursor) { $request->setStartingAfter($cursor); }, /* @phpstan-ignore-next-line */ @@ -111,11 +113,12 @@ public function listWithCursorPagination(ListUsersCursorPaginationRequest $reque * } $options * @return Pager */ - public function listWithMixedTypeCursorPagination(ListUsersMixedTypeCursorPaginationRequest $request = new ListUsersMixedTypeCursorPaginationRequest(), ?array $options = null): Pager { + public function listWithMixedTypeCursorPagination(ListUsersMixedTypeCursorPaginationRequest $request = new ListUsersMixedTypeCursorPaginationRequest(), ?array $options = null): Pager + { return new CursorPager( request: $request, - getNextPage: fn(ListUsersMixedTypeCursorPaginationRequest $request) => $this->_listWithMixedTypeCursorPagination($request, $options), - setCursor: function (ListUsersMixedTypeCursorPaginationRequest $request, string $cursor) { + getNextPage: fn (ListUsersMixedTypeCursorPaginationRequest $request) => $this->_listWithMixedTypeCursorPagination($request, $options), + setCursor: function (ListUsersMixedTypeCursorPaginationRequest $request, string $cursor) { $request->setCursor($cursor); }, /* @phpstan-ignore-next-line */ @@ -137,11 +140,12 @@ public function listWithMixedTypeCursorPagination(ListUsersMixedTypeCursorPagina * } $options * @return Pager */ - public function listWithBodyCursorPagination(ListUsersBodyCursorPaginationRequest $request = new ListUsersBodyCursorPaginationRequest(), ?array $options = null): Pager { + public function listWithBodyCursorPagination(ListUsersBodyCursorPaginationRequest $request = new ListUsersBodyCursorPaginationRequest(), ?array $options = null): Pager + { return new CursorPager( request: $request, - getNextPage: fn(ListUsersBodyCursorPaginationRequest $request) => $this->_listWithBodyCursorPagination($request, $options), - setCursor: function (ListUsersBodyCursorPaginationRequest $request, string $cursor) { + getNextPage: fn (ListUsersBodyCursorPaginationRequest $request) => $this->_listWithBodyCursorPagination($request, $options), + setCursor: function (ListUsersBodyCursorPaginationRequest $request, string $cursor) { PaginationHelper::setDeep($request, ["pagination", "cursor"], $cursor); }, /* @phpstan-ignore-next-line */ @@ -163,18 +167,19 @@ public function listWithBodyCursorPagination(ListUsersBodyCursorPaginationReques * } $options * @return Pager */ - public function listWithOffsetPagination(ListUsersOffsetPaginationRequest $request = new ListUsersOffsetPaginationRequest(), ?array $options = null): Pager { + public function listWithOffsetPagination(ListUsersOffsetPaginationRequest $request = new ListUsersOffsetPaginationRequest(), ?array $options = null): Pager + { return new OffsetPager( request: $request, - getNextPage: fn(ListUsersOffsetPaginationRequest $request) => $this->_listWithOffsetPagination($request, $options), + getNextPage: fn (ListUsersOffsetPaginationRequest $request) => $this->_listWithOffsetPagination($request, $options), /* @phpstan-ignore-next-line */ - getOffset: fn(ListUsersOffsetPaginationRequest $request) => $request?->getPage() ?? 0, - setOffset: function (ListUsersOffsetPaginationRequest $request, int $offset) { + getOffset: fn (ListUsersOffsetPaginationRequest $request) => $request?->getPage() ?? 0, + setOffset: function (ListUsersOffsetPaginationRequest $request, int $offset) { $request->setPage($offset); }, getStep: null, /* @phpstan-ignore-next-line */ - getItems: fn(ListUsersPaginationResponse $response) => $response?->getData() ?? [], + getItems: fn (ListUsersPaginationResponse $response) => $response?->getData() ?? [], /* @phpstan-ignore-next-line */ hasNextPage: null, ); @@ -192,18 +197,19 @@ public function listWithOffsetPagination(ListUsersOffsetPaginationRequest $reque * } $options * @return Pager */ - public function listWithDoubleOffsetPagination(ListUsersDoubleOffsetPaginationRequest $request = new ListUsersDoubleOffsetPaginationRequest(), ?array $options = null): Pager { + public function listWithDoubleOffsetPagination(ListUsersDoubleOffsetPaginationRequest $request = new ListUsersDoubleOffsetPaginationRequest(), ?array $options = null): Pager + { return new OffsetPager( request: $request, - getNextPage: fn(ListUsersDoubleOffsetPaginationRequest $request) => $this->_listWithDoubleOffsetPagination($request, $options), + getNextPage: fn (ListUsersDoubleOffsetPaginationRequest $request) => $this->_listWithDoubleOffsetPagination($request, $options), /* @phpstan-ignore-next-line */ - getOffset: fn(ListUsersDoubleOffsetPaginationRequest $request) => $request?->getPage() ?? 0, - setOffset: function (ListUsersDoubleOffsetPaginationRequest $request, int $offset) { + getOffset: fn (ListUsersDoubleOffsetPaginationRequest $request) => $request?->getPage() ?? 0, + setOffset: function (ListUsersDoubleOffsetPaginationRequest $request, int $offset) { $request->setPage($offset); }, getStep: null, /* @phpstan-ignore-next-line */ - getItems: fn(ListUsersPaginationResponse $response) => $response?->getData() ?? [], + getItems: fn (ListUsersPaginationResponse $response) => $response?->getData() ?? [], /* @phpstan-ignore-next-line */ hasNextPage: null, ); @@ -221,18 +227,19 @@ public function listWithDoubleOffsetPagination(ListUsersDoubleOffsetPaginationRe * } $options * @return Pager */ - public function listWithBodyOffsetPagination(ListUsersBodyOffsetPaginationRequest $request = new ListUsersBodyOffsetPaginationRequest(), ?array $options = null): Pager { + public function listWithBodyOffsetPagination(ListUsersBodyOffsetPaginationRequest $request = new ListUsersBodyOffsetPaginationRequest(), ?array $options = null): Pager + { return new OffsetPager( request: $request, - getNextPage: fn(ListUsersBodyOffsetPaginationRequest $request) => $this->_listWithBodyOffsetPagination($request, $options), + getNextPage: fn (ListUsersBodyOffsetPaginationRequest $request) => $this->_listWithBodyOffsetPagination($request, $options), /* @phpstan-ignore-next-line */ - getOffset: fn(ListUsersBodyOffsetPaginationRequest $request) => $request?->getPagination()?->getPage() ?? 0, - setOffset: function (ListUsersBodyOffsetPaginationRequest $request, int $offset) { + getOffset: fn (ListUsersBodyOffsetPaginationRequest $request) => $request?->getPagination()?->getPage() ?? 0, + setOffset: function (ListUsersBodyOffsetPaginationRequest $request, int $offset) { PaginationHelper::setDeep($request, ["pagination", "page"], $offset); }, getStep: null, /* @phpstan-ignore-next-line */ - getItems: fn(ListUsersPaginationResponse $response) => $response?->getData() ?? [], + getItems: fn (ListUsersPaginationResponse $response) => $response?->getData() ?? [], /* @phpstan-ignore-next-line */ hasNextPage: null, ); @@ -250,19 +257,20 @@ public function listWithBodyOffsetPagination(ListUsersBodyOffsetPaginationReques * } $options * @return Pager */ - public function listWithOffsetStepPagination(ListUsersOffsetStepPaginationRequest $request = new ListUsersOffsetStepPaginationRequest(), ?array $options = null): Pager { + public function listWithOffsetStepPagination(ListUsersOffsetStepPaginationRequest $request = new ListUsersOffsetStepPaginationRequest(), ?array $options = null): Pager + { return new OffsetPager( request: $request, - getNextPage: fn(ListUsersOffsetStepPaginationRequest $request) => $this->_listWithOffsetStepPagination($request, $options), + getNextPage: fn (ListUsersOffsetStepPaginationRequest $request) => $this->_listWithOffsetStepPagination($request, $options), /* @phpstan-ignore-next-line */ - getOffset: fn(ListUsersOffsetStepPaginationRequest $request) => $request?->getPage() ?? 0, - setOffset: function (ListUsersOffsetStepPaginationRequest $request, int $offset) { + getOffset: fn (ListUsersOffsetStepPaginationRequest $request) => $request?->getPage() ?? 0, + setOffset: function (ListUsersOffsetStepPaginationRequest $request, int $offset) { $request->setPage($offset); }, /* @phpstan-ignore-next-line */ - getStep: fn(ListUsersOffsetStepPaginationRequest $request) => $request?->getLimit() ?? 0, + getStep: fn (ListUsersOffsetStepPaginationRequest $request) => $request?->getLimit() ?? 0, /* @phpstan-ignore-next-line */ - getItems: fn(ListUsersPaginationResponse $response) => $response?->getData() ?? [], + getItems: fn (ListUsersPaginationResponse $response) => $response?->getData() ?? [], /* @phpstan-ignore-next-line */ hasNextPage: null, ); @@ -280,21 +288,22 @@ public function listWithOffsetStepPagination(ListUsersOffsetStepPaginationReques * } $options * @return Pager */ - public function listWithOffsetPaginationHasNextPage(ListWithOffsetPaginationHasNextPageRequest $request = new ListWithOffsetPaginationHasNextPageRequest(), ?array $options = null): Pager { + public function listWithOffsetPaginationHasNextPage(ListWithOffsetPaginationHasNextPageRequest $request = new ListWithOffsetPaginationHasNextPageRequest(), ?array $options = null): Pager + { return new OffsetPager( request: $request, - getNextPage: fn(ListWithOffsetPaginationHasNextPageRequest $request) => $this->_listWithOffsetPaginationHasNextPage($request, $options), + getNextPage: fn (ListWithOffsetPaginationHasNextPageRequest $request) => $this->_listWithOffsetPaginationHasNextPage($request, $options), /* @phpstan-ignore-next-line */ - getOffset: fn(ListWithOffsetPaginationHasNextPageRequest $request) => $request?->getPage() ?? 0, - setOffset: function (ListWithOffsetPaginationHasNextPageRequest $request, int $offset) { + getOffset: fn (ListWithOffsetPaginationHasNextPageRequest $request) => $request?->getPage() ?? 0, + setOffset: function (ListWithOffsetPaginationHasNextPageRequest $request, int $offset) { $request->setPage($offset); }, /* @phpstan-ignore-next-line */ - getStep: fn(ListWithOffsetPaginationHasNextPageRequest $request) => $request?->getLimit() ?? 0, + getStep: fn (ListWithOffsetPaginationHasNextPageRequest $request) => $request?->getLimit() ?? 0, /* @phpstan-ignore-next-line */ - getItems: fn(ListUsersPaginationResponse $response) => $response?->getData() ?? [], + getItems: fn (ListUsersPaginationResponse $response) => $response?->getData() ?? [], /* @phpstan-ignore-next-line */ - hasNextPage: fn(ListUsersPaginationResponse $response) => $response?->getHasNextPage(), + hasNextPage: fn (ListUsersPaginationResponse $response) => $response?->getHasNextPage(), ); } @@ -310,11 +319,12 @@ public function listWithOffsetPaginationHasNextPage(ListWithOffsetPaginationHasN * } $options * @return Pager */ - public function listWithExtendedResults(ListUsersExtendedRequest $request = new ListUsersExtendedRequest(), ?array $options = null): Pager { + public function listWithExtendedResults(ListUsersExtendedRequest $request = new ListUsersExtendedRequest(), ?array $options = null): Pager + { return new CursorPager( request: $request, - getNextPage: fn(ListUsersExtendedRequest $request) => $this->_listWithExtendedResults($request, $options), - setCursor: function (ListUsersExtendedRequest $request, ?string $cursor) { + getNextPage: fn (ListUsersExtendedRequest $request) => $this->_listWithExtendedResults($request, $options), + setCursor: function (ListUsersExtendedRequest $request, ?string $cursor) { $request->setCursor($cursor); }, /* @phpstan-ignore-next-line */ @@ -336,11 +346,12 @@ public function listWithExtendedResults(ListUsersExtendedRequest $request = new * } $options * @return Pager */ - public function listWithExtendedResultsAndOptionalData(ListUsersExtendedRequestForOptionalData $request = new ListUsersExtendedRequestForOptionalData(), ?array $options = null): Pager { + public function listWithExtendedResultsAndOptionalData(ListUsersExtendedRequestForOptionalData $request = new ListUsersExtendedRequestForOptionalData(), ?array $options = null): Pager + { return new CursorPager( request: $request, - getNextPage: fn(ListUsersExtendedRequestForOptionalData $request) => $this->_listWithExtendedResultsAndOptionalData($request, $options), - setCursor: function (ListUsersExtendedRequestForOptionalData $request, ?string $cursor) { + getNextPage: fn (ListUsersExtendedRequestForOptionalData $request) => $this->_listWithExtendedResultsAndOptionalData($request, $options), + setCursor: function (ListUsersExtendedRequestForOptionalData $request, ?string $cursor) { $request->setCursor($cursor); }, /* @phpstan-ignore-next-line */ @@ -362,11 +373,12 @@ public function listWithExtendedResultsAndOptionalData(ListUsersExtendedRequestF * } $options * @return Pager */ - public function listUsernames(ListUsernamesRequest $request = new ListUsernamesRequest(), ?array $options = null): Pager { + public function listUsernames(ListUsernamesRequest $request = new ListUsernamesRequest(), ?array $options = null): Pager + { return new CursorPager( request: $request, - getNextPage: fn(ListUsernamesRequest $request) => $this->_listUsernames($request, $options), - setCursor: function (ListUsernamesRequest $request, ?string $cursor) { + getNextPage: fn (ListUsernamesRequest $request) => $this->_listUsernames($request, $options), + setCursor: function (ListUsernamesRequest $request, ?string $cursor) { $request->setStartingAfter($cursor); }, /* @phpstan-ignore-next-line */ @@ -388,11 +400,12 @@ public function listUsernames(ListUsernamesRequest $request = new ListUsernamesR * } $options * @return Pager */ - public function listUsernamesWithOptionalResponse(ListUsernamesWithOptionalResponseRequest $request = new ListUsernamesWithOptionalResponseRequest(), ?array $options = null): Pager { + public function listUsernamesWithOptionalResponse(ListUsernamesWithOptionalResponseRequest $request = new ListUsernamesWithOptionalResponseRequest(), ?array $options = null): Pager + { return new CursorPager( request: $request, - getNextPage: fn(ListUsernamesWithOptionalResponseRequest $request) => $this->_listUsernamesWithOptionalResponse($request, $options), - setCursor: function (ListUsernamesWithOptionalResponseRequest $request, ?string $cursor) { + getNextPage: fn (ListUsernamesWithOptionalResponseRequest $request) => $this->_listUsernamesWithOptionalResponse($request, $options), + setCursor: function (ListUsernamesWithOptionalResponseRequest $request, ?string $cursor) { $request->setStartingAfter($cursor); }, /* @phpstan-ignore-next-line */ @@ -414,18 +427,49 @@ public function listUsernamesWithOptionalResponse(ListUsernamesWithOptionalRespo * } $options * @return Pager */ - public function listWithGlobalConfig(ListWithGlobalConfigRequest $request = new ListWithGlobalConfigRequest(), ?array $options = null): Pager { + public function listWithGlobalConfig(ListWithGlobalConfigRequest $request = new ListWithGlobalConfigRequest(), ?array $options = null): Pager + { return new OffsetPager( request: $request, - getNextPage: fn(ListWithGlobalConfigRequest $request) => $this->_listWithGlobalConfig($request, $options), + getNextPage: fn (ListWithGlobalConfigRequest $request) => $this->_listWithGlobalConfig($request, $options), /* @phpstan-ignore-next-line */ - getOffset: fn(ListWithGlobalConfigRequest $request) => $request?->getOffset() ?? 0, - setOffset: function (ListWithGlobalConfigRequest $request, int $offset) { + getOffset: fn (ListWithGlobalConfigRequest $request) => $request?->getOffset() ?? 0, + setOffset: function (ListWithGlobalConfigRequest $request, int $offset) { $request->setOffset($offset); }, getStep: null, /* @phpstan-ignore-next-line */ - getItems: fn(UsernameContainer $response) => $response?->getResults() ?? [], + getItems: fn (UsernameContainer $response) => $response?->getResults() ?? [], + /* @phpstan-ignore-next-line */ + hasNextPage: null, + ); + } + + /** + * @param ListUsersOptionalDataRequest $request + * @param ?array{ + * baseUrl?: string, + * maxRetries?: int, + * timeout?: float, + * headers?: array, + * queryParameters?: array, + * bodyProperties?: array, + * } $options + * @return Pager + */ + public function listWithOptionalData(ListUsersOptionalDataRequest $request = new ListUsersOptionalDataRequest(), ?array $options = null): Pager + { + return new OffsetPager( + request: $request, + getNextPage: fn (ListUsersOptionalDataRequest $request) => $this->_listWithOptionalData($request, $options), + /* @phpstan-ignore-next-line */ + getOffset: fn (ListUsersOptionalDataRequest $request) => $request?->getPage() ?? 0, + setOffset: function (ListUsersOptionalDataRequest $request, int $offset) { + $request->setPage($offset); + }, + getStep: null, + /* @phpstan-ignore-next-line */ + getItems: fn (ListUsersOptionalDataPaginationResponse $response) => $response?->getData() ?? [], /* @phpstan-ignore-next-line */ hasNextPage: null, ); @@ -445,19 +489,20 @@ public function listWithGlobalConfig(ListWithGlobalConfigRequest $request = new * @throws SeedException * @throws SeedApiException */ - private function _listWithCursorPagination(ListUsersCursorPaginationRequest $request = new ListUsersCursorPaginationRequest(), ?array $options = null): ListUsersPaginationResponse { + private function _listWithCursorPagination(ListUsersCursorPaginationRequest $request = new ListUsersCursorPaginationRequest(), ?array $options = null): ListUsersPaginationResponse + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->getPage() != null){ + if ($request->getPage() != null) { $query['page'] = $request->getPage(); } - if ($request->getPerPage() != null){ + if ($request->getPerPage() != null) { $query['per_page'] = $request->getPerPage(); } - if ($request->getOrder() != null){ + if ($request->getOrder() != null) { $query['order'] = $request->getOrder(); } - if ($request->getStartingAfter() != null){ + if ($request->getStartingAfter() != null) { $query['starting_after'] = $request->getStartingAfter(); } try { @@ -471,15 +516,15 @@ private function _listWithCursorPagination(ListUsersCursorPaginationRequest $req $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ListUsersPaginationResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -511,10 +556,11 @@ private function _listWithCursorPagination(ListUsersCursorPaginationRequest $req * @throws SeedException * @throws SeedApiException */ - private function _listWithMixedTypeCursorPagination(ListUsersMixedTypeCursorPaginationRequest $request = new ListUsersMixedTypeCursorPaginationRequest(), ?array $options = null): ListUsersMixedTypePaginationResponse { + private function _listWithMixedTypeCursorPagination(ListUsersMixedTypeCursorPaginationRequest $request = new ListUsersMixedTypeCursorPaginationRequest(), ?array $options = null): ListUsersMixedTypePaginationResponse + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->getCursor() != null){ + if ($request->getCursor() != null) { $query['cursor'] = $request->getCursor(); } try { @@ -528,15 +574,15 @@ private function _listWithMixedTypeCursorPagination(ListUsersMixedTypeCursorPagi $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ListUsersMixedTypePaginationResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -568,7 +614,8 @@ private function _listWithMixedTypeCursorPagination(ListUsersMixedTypeCursorPagi * @throws SeedException * @throws SeedApiException */ - private function _listWithBodyCursorPagination(ListUsersBodyCursorPaginationRequest $request = new ListUsersBodyCursorPaginationRequest(), ?array $options = null): ListUsersPaginationResponse { + private function _listWithBodyCursorPagination(ListUsersBodyCursorPaginationRequest $request = new ListUsersBodyCursorPaginationRequest(), ?array $options = null): ListUsersPaginationResponse + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -581,15 +628,15 @@ private function _listWithBodyCursorPagination(ListUsersBodyCursorPaginationRequ $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ListUsersPaginationResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -621,19 +668,20 @@ private function _listWithBodyCursorPagination(ListUsersBodyCursorPaginationRequ * @throws SeedException * @throws SeedApiException */ - private function _listWithOffsetPagination(ListUsersOffsetPaginationRequest $request = new ListUsersOffsetPaginationRequest(), ?array $options = null): ListUsersPaginationResponse { + private function _listWithOffsetPagination(ListUsersOffsetPaginationRequest $request = new ListUsersOffsetPaginationRequest(), ?array $options = null): ListUsersPaginationResponse + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->getPage() != null){ + if ($request->getPage() != null) { $query['page'] = $request->getPage(); } - if ($request->getPerPage() != null){ + if ($request->getPerPage() != null) { $query['per_page'] = $request->getPerPage(); } - if ($request->getOrder() != null){ + if ($request->getOrder() != null) { $query['order'] = $request->getOrder(); } - if ($request->getStartingAfter() != null){ + if ($request->getStartingAfter() != null) { $query['starting_after'] = $request->getStartingAfter(); } try { @@ -647,15 +695,15 @@ private function _listWithOffsetPagination(ListUsersOffsetPaginationRequest $req $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ListUsersPaginationResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -687,19 +735,20 @@ private function _listWithOffsetPagination(ListUsersOffsetPaginationRequest $req * @throws SeedException * @throws SeedApiException */ - private function _listWithDoubleOffsetPagination(ListUsersDoubleOffsetPaginationRequest $request = new ListUsersDoubleOffsetPaginationRequest(), ?array $options = null): ListUsersPaginationResponse { + private function _listWithDoubleOffsetPagination(ListUsersDoubleOffsetPaginationRequest $request = new ListUsersDoubleOffsetPaginationRequest(), ?array $options = null): ListUsersPaginationResponse + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->getPage() != null){ + if ($request->getPage() != null) { $query['page'] = $request->getPage(); } - if ($request->getPerPage() != null){ + if ($request->getPerPage() != null) { $query['per_page'] = $request->getPerPage(); } - if ($request->getOrder() != null){ + if ($request->getOrder() != null) { $query['order'] = $request->getOrder(); } - if ($request->getStartingAfter() != null){ + if ($request->getStartingAfter() != null) { $query['starting_after'] = $request->getStartingAfter(); } try { @@ -713,15 +762,15 @@ private function _listWithDoubleOffsetPagination(ListUsersDoubleOffsetPagination $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ListUsersPaginationResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -753,7 +802,8 @@ private function _listWithDoubleOffsetPagination(ListUsersDoubleOffsetPagination * @throws SeedException * @throws SeedApiException */ - private function _listWithBodyOffsetPagination(ListUsersBodyOffsetPaginationRequest $request = new ListUsersBodyOffsetPaginationRequest(), ?array $options = null): ListUsersPaginationResponse { + private function _listWithBodyOffsetPagination(ListUsersBodyOffsetPaginationRequest $request = new ListUsersBodyOffsetPaginationRequest(), ?array $options = null): ListUsersPaginationResponse + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -766,15 +816,15 @@ private function _listWithBodyOffsetPagination(ListUsersBodyOffsetPaginationRequ $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ListUsersPaginationResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -806,16 +856,17 @@ private function _listWithBodyOffsetPagination(ListUsersBodyOffsetPaginationRequ * @throws SeedException * @throws SeedApiException */ - private function _listWithOffsetStepPagination(ListUsersOffsetStepPaginationRequest $request = new ListUsersOffsetStepPaginationRequest(), ?array $options = null): ListUsersPaginationResponse { + private function _listWithOffsetStepPagination(ListUsersOffsetStepPaginationRequest $request = new ListUsersOffsetStepPaginationRequest(), ?array $options = null): ListUsersPaginationResponse + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->getPage() != null){ + if ($request->getPage() != null) { $query['page'] = $request->getPage(); } - if ($request->getLimit() != null){ + if ($request->getLimit() != null) { $query['limit'] = $request->getLimit(); } - if ($request->getOrder() != null){ + if ($request->getOrder() != null) { $query['order'] = $request->getOrder(); } try { @@ -829,15 +880,15 @@ private function _listWithOffsetStepPagination(ListUsersOffsetStepPaginationRequ $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ListUsersPaginationResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -869,16 +920,17 @@ private function _listWithOffsetStepPagination(ListUsersOffsetStepPaginationRequ * @throws SeedException * @throws SeedApiException */ - private function _listWithOffsetPaginationHasNextPage(ListWithOffsetPaginationHasNextPageRequest $request = new ListWithOffsetPaginationHasNextPageRequest(), ?array $options = null): ListUsersPaginationResponse { + private function _listWithOffsetPaginationHasNextPage(ListWithOffsetPaginationHasNextPageRequest $request = new ListWithOffsetPaginationHasNextPageRequest(), ?array $options = null): ListUsersPaginationResponse + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->getPage() != null){ + if ($request->getPage() != null) { $query['page'] = $request->getPage(); } - if ($request->getLimit() != null){ + if ($request->getLimit() != null) { $query['limit'] = $request->getLimit(); } - if ($request->getOrder() != null){ + if ($request->getOrder() != null) { $query['order'] = $request->getOrder(); } try { @@ -892,15 +944,15 @@ private function _listWithOffsetPaginationHasNextPage(ListWithOffsetPaginationHa $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ListUsersPaginationResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -932,10 +984,11 @@ private function _listWithOffsetPaginationHasNextPage(ListWithOffsetPaginationHa * @throws SeedException * @throws SeedApiException */ - private function _listWithExtendedResults(ListUsersExtendedRequest $request = new ListUsersExtendedRequest(), ?array $options = null): ListUsersExtendedResponse { + private function _listWithExtendedResults(ListUsersExtendedRequest $request = new ListUsersExtendedRequest(), ?array $options = null): ListUsersExtendedResponse + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->getCursor() != null){ + if ($request->getCursor() != null) { $query['cursor'] = $request->getCursor(); } try { @@ -949,15 +1002,15 @@ private function _listWithExtendedResults(ListUsersExtendedRequest $request = ne $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ListUsersExtendedResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -989,10 +1042,11 @@ private function _listWithExtendedResults(ListUsersExtendedRequest $request = ne * @throws SeedException * @throws SeedApiException */ - private function _listWithExtendedResultsAndOptionalData(ListUsersExtendedRequestForOptionalData $request = new ListUsersExtendedRequestForOptionalData(), ?array $options = null): ListUsersExtendedOptionalListResponse { + private function _listWithExtendedResultsAndOptionalData(ListUsersExtendedRequestForOptionalData $request = new ListUsersExtendedRequestForOptionalData(), ?array $options = null): ListUsersExtendedOptionalListResponse + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->getCursor() != null){ + if ($request->getCursor() != null) { $query['cursor'] = $request->getCursor(); } try { @@ -1006,15 +1060,15 @@ private function _listWithExtendedResultsAndOptionalData(ListUsersExtendedReques $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ListUsersExtendedOptionalListResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -1046,10 +1100,11 @@ private function _listWithExtendedResultsAndOptionalData(ListUsersExtendedReques * @throws SeedException * @throws SeedApiException */ - private function _listUsernames(ListUsernamesRequest $request = new ListUsernamesRequest(), ?array $options = null): UsernameCursor { + private function _listUsernames(ListUsernamesRequest $request = new ListUsernamesRequest(), ?array $options = null): UsernameCursor + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->getStartingAfter() != null){ + if ($request->getStartingAfter() != null) { $query['starting_after'] = $request->getStartingAfter(); } try { @@ -1063,15 +1118,15 @@ private function _listUsernames(ListUsernamesRequest $request = new ListUsername $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return UsernameCursor::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -1103,10 +1158,11 @@ private function _listUsernames(ListUsernamesRequest $request = new ListUsername * @throws SeedException * @throws SeedApiException */ - private function _listUsernamesWithOptionalResponse(ListUsernamesWithOptionalResponseRequest $request = new ListUsernamesWithOptionalResponseRequest(), ?array $options = null): ?UsernameCursor { + private function _listUsernamesWithOptionalResponse(ListUsernamesWithOptionalResponseRequest $request = new ListUsernamesWithOptionalResponseRequest(), ?array $options = null): ?UsernameCursor + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->getStartingAfter() != null){ + if ($request->getStartingAfter() != null) { $query['starting_after'] = $request->getStartingAfter(); } try { @@ -1120,18 +1176,18 @@ private function _listUsernamesWithOptionalResponse(ListUsernamesWithOptionalRes $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); - if (empty($json)){ + if (empty($json)) { return null; } return UsernameCursor::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -1163,10 +1219,11 @@ private function _listUsernamesWithOptionalResponse(ListUsernamesWithOptionalRes * @throws SeedException * @throws SeedApiException */ - private function _listWithGlobalConfig(ListWithGlobalConfigRequest $request = new ListWithGlobalConfigRequest(), ?array $options = null): UsernameContainer { + private function _listWithGlobalConfig(ListWithGlobalConfigRequest $request = new ListWithGlobalConfigRequest(), ?array $options = null): UsernameContainer + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->getOffset() != null){ + if ($request->getOffset() != null) { $query['offset'] = $request->getOffset(); } try { @@ -1180,15 +1237,73 @@ private function _listWithGlobalConfig(ListWithGlobalConfigRequest $request = ne $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return UsernameContainer::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (RequestException $e) { + $response = $e->getResponse(); + if ($response === null) { + throw new SeedException(message: $e->getMessage(), previous: $e); + } + throw new SeedApiException( + message: "API request failed", + statusCode: $response->getStatusCode(), + body: $response->getBody()->getContents(), + ); + } catch (ClientExceptionInterface $e) { + throw new SeedException(message: $e->getMessage(), previous: $e); + } + throw new SeedApiException( + message: 'API request failed', + statusCode: $statusCode, + body: $response->getBody()->getContents(), + ); + } + + /** + * @param ListUsersOptionalDataRequest $request + * @param ?array{ + * baseUrl?: string, + * maxRetries?: int, + * timeout?: float, + * headers?: array, + * queryParameters?: array, + * bodyProperties?: array, + * } $options + * @return ListUsersOptionalDataPaginationResponse + * @throws SeedException + * @throws SeedApiException + */ + private function _listWithOptionalData(ListUsersOptionalDataRequest $request = new ListUsersOptionalDataRequest(), ?array $options = null): ListUsersOptionalDataPaginationResponse + { + $options = array_merge($this->options, $options ?? []); + $query = []; + if ($request->getPage() != null) { + $query['page'] = $request->getPage(); + } + try { + $response = $this->client->sendRequest( + new JsonApiRequest( + baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '', + path: "/users/optional-data", + method: HttpMethod::GET, + query: $query, + ), + $options, + ); + $statusCode = $response->getStatusCode(); + if ($statusCode >= 200 && $statusCode < 400) { + $json = $response->getBody()->getContents(); + return ListUsersOptionalDataPaginationResponse::fromJson($json); + } + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/pagination/property-accessors/src/Utils/File.php b/seed/php-sdk/pagination/property-accessors/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/pagination/property-accessors/src/Utils/File.php +++ b/seed/php-sdk/pagination/property-accessors/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination/property-accessors/src/dynamic-snippets/example26/snippet.php b/seed/php-sdk/pagination/property-accessors/src/dynamic-snippets/example26/snippet.php new file mode 100644 index 000000000000..136421c43075 --- /dev/null +++ b/seed/php-sdk/pagination/property-accessors/src/dynamic-snippets/example26/snippet.php @@ -0,0 +1,18 @@ +', + options: [ + 'baseUrl' => 'https://api.fern.com', + ], +); +$client->users->listWithOptionalData( + new ListUsersOptionalDataRequest([ + 'page' => 1, + ]), +); diff --git a/seed/php-sdk/pagination/property-accessors/src/dynamic-snippets/example27/snippet.php b/seed/php-sdk/pagination/property-accessors/src/dynamic-snippets/example27/snippet.php new file mode 100644 index 000000000000..136421c43075 --- /dev/null +++ b/seed/php-sdk/pagination/property-accessors/src/dynamic-snippets/example27/snippet.php @@ -0,0 +1,18 @@ +', + options: [ + 'baseUrl' => 'https://api.fern.com', + ], +); +$client->users->listWithOptionalData( + new ListUsersOptionalDataRequest([ + 'page' => 1, + ]), +); diff --git a/seed/php-sdk/pagination/property-accessors/src/dynamic-snippets/example28/snippet.php b/seed/php-sdk/pagination/property-accessors/src/dynamic-snippets/example28/snippet.php new file mode 100644 index 000000000000..136421c43075 --- /dev/null +++ b/seed/php-sdk/pagination/property-accessors/src/dynamic-snippets/example28/snippet.php @@ -0,0 +1,18 @@ +', + options: [ + 'baseUrl' => 'https://api.fern.com', + ], +); +$client->users->listWithOptionalData( + new ListUsersOptionalDataRequest([ + 'page' => 1, + ]), +); diff --git a/seed/php-sdk/pagination/property-accessors/tests/Core/Client/RawClientTest.php b/seed/php-sdk/pagination/property-accessors/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/pagination/property-accessors/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/pagination/property-accessors/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/pagination/property-accessors/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/pagination/property-accessors/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/pagination/property-accessors/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/pagination/property-accessors/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination/property-accessors/tests/Core/Json/EnumTest.php b/seed/php-sdk/pagination/property-accessors/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/pagination/property-accessors/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/pagination/property-accessors/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination/property-accessors/tests/Core/Json/TraitTest.php b/seed/php-sdk/pagination/property-accessors/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/pagination/property-accessors/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/pagination/property-accessors/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination/property-accessors/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/pagination/property-accessors/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/pagination/property-accessors/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/pagination/property-accessors/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination/property-accessors/tests/Core/Pagination/CursorPagerTest/CursorPagerTest.php b/seed/php-sdk/pagination/property-accessors/tests/Core/Pagination/CursorPagerTest/CursorPagerTest.php index fd9eda9faf6c..8c5ac6b6fd59 100644 --- a/seed/php-sdk/pagination/property-accessors/tests/Core/Pagination/CursorPagerTest/CursorPagerTest.php +++ b/seed/php-sdk/pagination/property-accessors/tests/Core/Pagination/CursorPagerTest/CursorPagerTest.php @@ -93,8 +93,8 @@ function (Request $request, ?string $cursor) { $request->cursor = $cursor; $this->cursorCopy = $cursor; }, - fn(Response $response) => $response->next->cursor ?? null, - fn(Response $response) => $response->items ?? [] + fn (Response $response) => $response->next->cursor ?? null, + fn (Response $response) => $response->items ?? [] ); } @@ -129,4 +129,4 @@ private function assertPager(Pager $pager): void $pages->next(); $this->assertNull($pages->current()); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination/property-accessors/tests/Core/Pagination/DeepSetAccessorsTest.php b/seed/php-sdk/pagination/property-accessors/tests/Core/Pagination/DeepSetAccessorsTest.php index a21e7201abb9..73ed5401292f 100644 --- a/seed/php-sdk/pagination/property-accessors/tests/Core/Pagination/DeepSetAccessorsTest.php +++ b/seed/php-sdk/pagination/property-accessors/tests/Core/Pagination/DeepSetAccessorsTest.php @@ -98,4 +98,4 @@ public function testSetNestedProperty(): void $this->assertEquals('testValue', $object->getLevel1()?->getLevel2()?->getLevel3()); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination/property-accessors/tests/Core/Pagination/GeneratorPagerTest/GeneratorPagerTest.php b/seed/php-sdk/pagination/property-accessors/tests/Core/Pagination/GeneratorPagerTest/GeneratorPagerTest.php index 8ada5c5edeb2..47c12833b1a7 100644 --- a/seed/php-sdk/pagination/property-accessors/tests/Core/Pagination/GeneratorPagerTest/GeneratorPagerTest.php +++ b/seed/php-sdk/pagination/property-accessors/tests/Core/Pagination/GeneratorPagerTest/GeneratorPagerTest.php @@ -69,7 +69,7 @@ private function createPager(): Pager new Response(new Data([])), ]); - return new class($responses) extends Pager { + return new class ($responses) extends Pager { /** * @var ArrayIterator */ diff --git a/seed/php-sdk/pagination/property-accessors/tests/Core/Pagination/HasNextPageOffsetPagerTest/HasNextPageOffsetPagerTest.php b/seed/php-sdk/pagination/property-accessors/tests/Core/Pagination/HasNextPageOffsetPagerTest/HasNextPageOffsetPagerTest.php index 7e96cb22ec9c..f04cc27903c3 100644 --- a/seed/php-sdk/pagination/property-accessors/tests/Core/Pagination/HasNextPageOffsetPagerTest/HasNextPageOffsetPagerTest.php +++ b/seed/php-sdk/pagination/property-accessors/tests/Core/Pagination/HasNextPageOffsetPagerTest/HasNextPageOffsetPagerTest.php @@ -81,16 +81,16 @@ function (Request $request) use ($responses) { $responses->next(); return $response; }, - fn(Request $request) => $request->pagination?->page ?? 0, + fn (Request $request) => $request->pagination?->page ?? 0, function (Request $request, int $offset) { - if($request->pagination === null) { + if ($request->pagination === null) { $request->pagination = new Pagination(0); } $request->pagination->page = $offset; }, null, - fn(Response $response) => $response->data->items, - fn(Response $response) => $response->hasNext + fn (Response $response) => $response->data->items, + fn (Response $response) => $response->hasNext ); } @@ -102,9 +102,9 @@ private function assertPager(Pager $pager): void { $pages = iterator_to_array($pager->getPages()); $pageCounter = count($pages); - $itemCounter = array_reduce($pages, fn($carry, $page) => $carry + count($page->getItems()), 0); + $itemCounter = array_reduce($pages, fn ($carry, $page) => $carry + count($page->getItems()), 0); $this->assertEquals(3, $pageCounter); $this->assertEquals(5, $itemCounter); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination/property-accessors/tests/Core/Pagination/IntOffsetPagerTest/IntOffsetPagerTest.php b/seed/php-sdk/pagination/property-accessors/tests/Core/Pagination/IntOffsetPagerTest/IntOffsetPagerTest.php index 71d2343f78e4..226ca279d28a 100644 --- a/seed/php-sdk/pagination/property-accessors/tests/Core/Pagination/IntOffsetPagerTest/IntOffsetPagerTest.php +++ b/seed/php-sdk/pagination/property-accessors/tests/Core/Pagination/IntOffsetPagerTest/IntOffsetPagerTest.php @@ -79,7 +79,7 @@ function (Request $request) use ($responses) { $responses->next(); return $response; }, - fn(Request $request) => $request->pagination?->page ?? 0, + fn (Request $request) => $request->pagination?->page ?? 0, function (Request $request, int $offset) { if ($request->pagination === null) { $request->pagination = new Pagination(0); @@ -87,7 +87,7 @@ function (Request $request, int $offset) { $request->pagination->page = $offset; }, null, - fn(Response $response) => $response->data->items, + fn (Response $response) => $response->data->items, null ); } @@ -100,9 +100,9 @@ private function assertPager(Pager $pager): void { $pages = iterator_to_array($pager->getPages()); $pageCounter = count($pages); - $itemCounter = array_reduce($pages, fn($carry, $page) => $carry + count($page->getItems()), 0); + $itemCounter = array_reduce($pages, fn ($carry, $page) => $carry + count($page->getItems()), 0); $this->assertEquals(3, $pageCounter); $this->assertEquals(3, $itemCounter); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/pagination/property-accessors/tests/Core/Pagination/StepOffsetPagerTest/StepOffsetPagerTest.php b/seed/php-sdk/pagination/property-accessors/tests/Core/Pagination/StepOffsetPagerTest/StepOffsetPagerTest.php index 79b0f27868b0..d44cdc364418 100644 --- a/seed/php-sdk/pagination/property-accessors/tests/Core/Pagination/StepOffsetPagerTest/StepOffsetPagerTest.php +++ b/seed/php-sdk/pagination/property-accessors/tests/Core/Pagination/StepOffsetPagerTest/StepOffsetPagerTest.php @@ -85,7 +85,7 @@ function (Request $request) use ($responses) { $responses->next(); return $response; }, - fn(Request $request) => $request->pagination?->itemOffset ?? 0, + fn (Request $request) => $request->pagination?->itemOffset ?? 0, function (Request $request, int $offset) { if ($request->pagination === null) { $request->pagination = new Pagination(0, 2); @@ -93,8 +93,8 @@ function (Request $request, int $offset) { $request->pagination->itemOffset = $offset; $this->paginationCopy = $request->pagination; }, - fn(Request $request) => $request->pagination?->pageSize, - fn(Response $response) => $response->data->items, + fn (Request $request) => $request->pagination?->pageSize, + fn (Response $response) => $response->data->items, null ); } @@ -128,4 +128,4 @@ private function assertPager(Pager $pager): void $pages->next(); $this->assertNull($pages->current()); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Core/Client/RawClient.php b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Core/Client/RawClient.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Core/Types/ArrayType.php b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Core/Types/Constant.php b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Core/Types/Constant.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Core/Types/Union.php b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Core/Types/Union.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Exceptions/SeedApiException.php b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Exceptions/SeedException.php b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Exceptions/SeedException.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Organizations/OrganizationsClient.php b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Organizations/OrganizationsClient.php index 6e7e6e2d0056..16ae5e6ea752 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Organizations/OrganizationsClient.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Organizations/OrganizationsClient.php @@ -17,7 +17,7 @@ use Seed\Organizations\Requests\SearchOrganizationsRequest; use Seed\Core\Json\JsonDecoder; -class OrganizationsClient +class OrganizationsClient { /** * @var array{ @@ -45,11 +45,10 @@ class OrganizationsClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -69,7 +68,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getOrganization(string $tenantId, string $organizationId, ?array $options = null): Organization { + public function getOrganization(string $tenantId, string $organizationId, ?array $options = null): Organization + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -81,15 +81,15 @@ public function getOrganization(string $tenantId, string $organizationId, ?array $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return Organization::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -121,7 +121,8 @@ public function getOrganization(string $tenantId, string $organizationId, ?array * @throws SeedException * @throws SeedApiException */ - public function getOrganizationUser(GetOrganizationUserRequest $request, ?array $options = null): User { + public function getOrganizationUser(GetOrganizationUserRequest $request, ?array $options = null): User + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -133,15 +134,15 @@ public function getOrganizationUser(GetOrganizationUserRequest $request, ?array $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return User::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -175,10 +176,11 @@ public function getOrganizationUser(GetOrganizationUserRequest $request, ?array * @throws SeedException * @throws SeedApiException */ - public function searchOrganizations(string $tenantId, string $organizationId, SearchOrganizationsRequest $request = new SearchOrganizationsRequest(), ?array $options = null): array { + public function searchOrganizations(string $tenantId, string $organizationId, SearchOrganizationsRequest $request = new SearchOrganizationsRequest(), ?array $options = null): array + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->getLimit() != null){ + if ($request->getLimit() != null) { $query['limit'] = $request->getLimit(); } try { @@ -192,15 +194,15 @@ public function searchOrganizations(string $tenantId, string $organizationId, Se $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, [Organization::class]); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Organizations/Requests/GetOrganizationUserRequest.php b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Organizations/Requests/GetOrganizationUserRequest.php index aa1e1aab26f4..40bf675f8fa0 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Organizations/Requests/GetOrganizationUserRequest.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Organizations/Requests/GetOrganizationUserRequest.php @@ -30,44 +30,60 @@ class GetOrganizationUserRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->tenantId = $values['tenantId'];$this->organizationId = $values['organizationId'];$this->userId = $values['userId']; + ) { + $this->tenantId = $values['tenantId']; + $this->organizationId = $values['organizationId']; + $this->userId = $values['userId']; } /** * @return string */ - public function getTenantId(): string { - return $this->tenantId;} + public function getTenantId(): string + { + return $this->tenantId; + } /** * @param string $value */ - public function setTenantId(string $value): self { - $this->tenantId = $value;return $this;} + public function setTenantId(string $value): self + { + $this->tenantId = $value; + return $this; + } /** * @return string */ - public function getOrganizationId(): string { - return $this->organizationId;} + public function getOrganizationId(): string + { + return $this->organizationId; + } /** * @param string $value */ - public function setOrganizationId(string $value): self { - $this->organizationId = $value;return $this;} + public function setOrganizationId(string $value): self + { + $this->organizationId = $value; + return $this; + } /** * @return string */ - public function getUserId(): string { - return $this->userId;} + public function getUserId(): string + { + return $this->userId; + } /** * @param string $value */ - public function setUserId(string $value): self { - $this->userId = $value;return $this;} + public function setUserId(string $value): self + { + $this->userId = $value; + return $this; + } } diff --git a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Organizations/Requests/SearchOrganizationsRequest.php b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Organizations/Requests/SearchOrganizationsRequest.php index 5661e6f414c1..370e71e95568 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Organizations/Requests/SearchOrganizationsRequest.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Organizations/Requests/SearchOrganizationsRequest.php @@ -18,20 +18,24 @@ class SearchOrganizationsRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->limit = $values['limit'] ?? null; } /** * @return ?int */ - public function getLimit(): ?int { - return $this->limit;} + public function getLimit(): ?int + { + return $this->limit; + } /** * @param ?int $value */ - public function setLimit(?int $value = null): self { - $this->limit = $value;return $this;} + public function setLimit(?int $value = null): self + { + $this->limit = $value; + return $this; + } } diff --git a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Organizations/Types/Organization.php b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Organizations/Types/Organization.php index 11e86d7b02f2..d2b58642b199 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Organizations/Types/Organization.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Organizations/Types/Organization.php @@ -28,39 +28,50 @@ class Organization extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->tags = $values['tags']; + ) { + $this->name = $values['name']; + $this->tags = $values['tags']; } /** * @return string */ - public function getName(): string { - return $this->name;} + public function getName(): string + { + return $this->name; + } /** * @param string $value */ - public function setName(string $value): self { - $this->name = $value;return $this;} + public function setName(string $value): self + { + $this->name = $value; + return $this; + } /** * @return array */ - public function getTags(): array { - return $this->tags;} + public function getTags(): array + { + return $this->tags; + } /** * @param array $value */ - public function setTags(array $value): self { - $this->tags = $value;return $this;} + public function setTags(array $value): self + { + $this->tags = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/SeedClient.php b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/SeedClient.php index ef52ac412a42..62b81ecff63d 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/SeedClient.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/SeedClient.php @@ -7,7 +7,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var OrganizationsClient $organizations @@ -46,26 +46,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->organizations = new OrganizationsClient($this->client, $this->options); $this->user = new UserClient($this->client, $this->options); } diff --git a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/User/Requests/GetUserMetadataRequest.php b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/User/Requests/GetUserMetadataRequest.php index 98fa2db24c6c..31b2da215b8b 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/User/Requests/GetUserMetadataRequest.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/User/Requests/GetUserMetadataRequest.php @@ -30,44 +30,60 @@ class GetUserMetadataRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->tenantId = $values['tenantId'];$this->userId = $values['userId'];$this->version = $values['version']; + ) { + $this->tenantId = $values['tenantId']; + $this->userId = $values['userId']; + $this->version = $values['version']; } /** * @return string */ - public function getTenantId(): string { - return $this->tenantId;} + public function getTenantId(): string + { + return $this->tenantId; + } /** * @param string $value */ - public function setTenantId(string $value): self { - $this->tenantId = $value;return $this;} + public function setTenantId(string $value): self + { + $this->tenantId = $value; + return $this; + } /** * @return string */ - public function getUserId(): string { - return $this->userId;} + public function getUserId(): string + { + return $this->userId; + } /** * @param string $value */ - public function setUserId(string $value): self { - $this->userId = $value;return $this;} + public function setUserId(string $value): self + { + $this->userId = $value; + return $this; + } /** * @return int */ - public function getVersion(): int { - return $this->version;} + public function getVersion(): int + { + return $this->version; + } /** * @param int $value */ - public function setVersion(int $value): self { - $this->version = $value;return $this;} + public function setVersion(int $value): self + { + $this->version = $value; + return $this; + } } diff --git a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/User/Requests/GetUserSpecificsRequest.php b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/User/Requests/GetUserSpecificsRequest.php index 8639c1bf7918..cc2305f74c13 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/User/Requests/GetUserSpecificsRequest.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/User/Requests/GetUserSpecificsRequest.php @@ -36,56 +36,78 @@ class GetUserSpecificsRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->tenantId = $values['tenantId'];$this->userId = $values['userId'];$this->version = $values['version'];$this->thought = $values['thought']; + ) { + $this->tenantId = $values['tenantId']; + $this->userId = $values['userId']; + $this->version = $values['version']; + $this->thought = $values['thought']; } /** * @return string */ - public function getTenantId(): string { - return $this->tenantId;} + public function getTenantId(): string + { + return $this->tenantId; + } /** * @param string $value */ - public function setTenantId(string $value): self { - $this->tenantId = $value;return $this;} + public function setTenantId(string $value): self + { + $this->tenantId = $value; + return $this; + } /** * @return string */ - public function getUserId(): string { - return $this->userId;} + public function getUserId(): string + { + return $this->userId; + } /** * @param string $value */ - public function setUserId(string $value): self { - $this->userId = $value;return $this;} + public function setUserId(string $value): self + { + $this->userId = $value; + return $this; + } /** * @return int */ - public function getVersion(): int { - return $this->version;} + public function getVersion(): int + { + return $this->version; + } /** * @param int $value */ - public function setVersion(int $value): self { - $this->version = $value;return $this;} + public function setVersion(int $value): self + { + $this->version = $value; + return $this; + } /** * @return string */ - public function getThought(): string { - return $this->thought;} + public function getThought(): string + { + return $this->thought; + } /** * @param string $value */ - public function setThought(string $value): self { - $this->thought = $value;return $this;} + public function setThought(string $value): self + { + $this->thought = $value; + return $this; + } } diff --git a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/User/Requests/GetUsersRequest.php b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/User/Requests/GetUsersRequest.php index ff620d157e18..c882d6a55ca1 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/User/Requests/GetUsersRequest.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/User/Requests/GetUsersRequest.php @@ -24,32 +24,42 @@ class GetUsersRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->tenantId = $values['tenantId'];$this->userId = $values['userId']; + ) { + $this->tenantId = $values['tenantId']; + $this->userId = $values['userId']; } /** * @return string */ - public function getTenantId(): string { - return $this->tenantId;} + public function getTenantId(): string + { + return $this->tenantId; + } /** * @param string $value */ - public function setTenantId(string $value): self { - $this->tenantId = $value;return $this;} + public function setTenantId(string $value): self + { + $this->tenantId = $value; + return $this; + } /** * @return string */ - public function getUserId(): string { - return $this->userId;} + public function getUserId(): string + { + return $this->userId; + } /** * @param string $value */ - public function setUserId(string $value): self { - $this->userId = $value;return $this;} + public function setUserId(string $value): self + { + $this->userId = $value; + return $this; + } } diff --git a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/User/Requests/SearchUsersRequest.php b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/User/Requests/SearchUsersRequest.php index 3db5c4fefef4..99c0eebe3d93 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/User/Requests/SearchUsersRequest.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/User/Requests/SearchUsersRequest.php @@ -30,44 +30,60 @@ class SearchUsersRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->tenantId = $values['tenantId'];$this->userId = $values['userId'];$this->limit = $values['limit'] ?? null; + ) { + $this->tenantId = $values['tenantId']; + $this->userId = $values['userId']; + $this->limit = $values['limit'] ?? null; } /** * @return string */ - public function getTenantId(): string { - return $this->tenantId;} + public function getTenantId(): string + { + return $this->tenantId; + } /** * @param string $value */ - public function setTenantId(string $value): self { - $this->tenantId = $value;return $this;} + public function setTenantId(string $value): self + { + $this->tenantId = $value; + return $this; + } /** * @return string */ - public function getUserId(): string { - return $this->userId;} + public function getUserId(): string + { + return $this->userId; + } /** * @param string $value */ - public function setUserId(string $value): self { - $this->userId = $value;return $this;} + public function setUserId(string $value): self + { + $this->userId = $value; + return $this; + } /** * @return ?int */ - public function getLimit(): ?int { - return $this->limit;} + public function getLimit(): ?int + { + return $this->limit; + } /** * @param ?int $value */ - public function setLimit(?int $value = null): self { - $this->limit = $value;return $this;} + public function setLimit(?int $value = null): self + { + $this->limit = $value; + return $this; + } } diff --git a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/User/Requests/UpdateUserRequest.php b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/User/Requests/UpdateUserRequest.php index d1bf847d1114..3871358cb67f 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/User/Requests/UpdateUserRequest.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/User/Requests/UpdateUserRequest.php @@ -31,44 +31,60 @@ class UpdateUserRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->tenantId = $values['tenantId'];$this->userId = $values['userId'];$this->body = $values['body']; + ) { + $this->tenantId = $values['tenantId']; + $this->userId = $values['userId']; + $this->body = $values['body']; } /** * @return string */ - public function getTenantId(): string { - return $this->tenantId;} + public function getTenantId(): string + { + return $this->tenantId; + } /** * @param string $value */ - public function setTenantId(string $value): self { - $this->tenantId = $value;return $this;} + public function setTenantId(string $value): self + { + $this->tenantId = $value; + return $this; + } /** * @return string */ - public function getUserId(): string { - return $this->userId;} + public function getUserId(): string + { + return $this->userId; + } /** * @param string $value */ - public function setUserId(string $value): self { - $this->userId = $value;return $this;} + public function setUserId(string $value): self + { + $this->userId = $value; + return $this; + } /** * @return User */ - public function getBody(): User { - return $this->body;} + public function getBody(): User + { + return $this->body; + } /** * @param User $value */ - public function setBody(User $value): self { - $this->body = $value;return $this;} + public function setBody(User $value): self + { + $this->body = $value; + return $this; + } } diff --git a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/User/Types/User.php b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/User/Types/User.php index f20da4e5cae6..3692ead60318 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/User/Types/User.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/User/Types/User.php @@ -28,39 +28,50 @@ class User extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->tags = $values['tags']; + ) { + $this->name = $values['name']; + $this->tags = $values['tags']; } /** * @return string */ - public function getName(): string { - return $this->name;} + public function getName(): string + { + return $this->name; + } /** * @param string $value */ - public function setName(string $value): self { - $this->name = $value;return $this;} + public function setName(string $value): self + { + $this->name = $value; + return $this; + } /** * @return array */ - public function getTags(): array { - return $this->tags;} + public function getTags(): array + { + return $this->tags; + } /** * @param array $value */ - public function setTags(array $value): self { - $this->tags = $value;return $this;} + public function setTags(array $value): self + { + $this->tags = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/User/UserClient.php b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/User/UserClient.php index 9febba1ec4e6..1641a4bf1fb3 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/User/UserClient.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/User/UserClient.php @@ -19,7 +19,7 @@ use Seed\User\Requests\GetUserMetadataRequest; use Seed\User\Requests\GetUserSpecificsRequest; -class UserClient +class UserClient { /** * @var array{ @@ -47,11 +47,10 @@ class UserClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -70,7 +69,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getUser(GetUsersRequest $request, ?array $options = null): User { + public function getUser(GetUsersRequest $request, ?array $options = null): User + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -82,15 +82,15 @@ public function getUser(GetUsersRequest $request, ?array $options = null): User $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return User::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -123,7 +123,8 @@ public function getUser(GetUsersRequest $request, ?array $options = null): User * @throws SeedException * @throws SeedApiException */ - public function createUser(string $tenantId, User $request, ?array $options = null): User { + public function createUser(string $tenantId, User $request, ?array $options = null): User + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -136,15 +137,15 @@ public function createUser(string $tenantId, User $request, ?array $options = nu $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return User::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -176,7 +177,8 @@ public function createUser(string $tenantId, User $request, ?array $options = nu * @throws SeedException * @throws SeedApiException */ - public function updateUser(UpdateUserRequest $request, ?array $options = null): User { + public function updateUser(UpdateUserRequest $request, ?array $options = null): User + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -189,15 +191,15 @@ public function updateUser(UpdateUserRequest $request, ?array $options = null): $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return User::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -229,10 +231,11 @@ public function updateUser(UpdateUserRequest $request, ?array $options = null): * @throws SeedException * @throws SeedApiException */ - public function searchUsers(SearchUsersRequest $request, ?array $options = null): array { + public function searchUsers(SearchUsersRequest $request, ?array $options = null): array + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->getLimit() != null){ + if ($request->getLimit() != null) { $query['limit'] = $request->getLimit(); } try { @@ -246,15 +249,15 @@ public function searchUsers(SearchUsersRequest $request, ?array $options = null) $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, [User::class]); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -288,7 +291,8 @@ public function searchUsers(SearchUsersRequest $request, ?array $options = null) * @throws SeedException * @throws SeedApiException */ - public function getUserMetadata(GetUserMetadataRequest $request, ?array $options = null): User { + public function getUserMetadata(GetUserMetadataRequest $request, ?array $options = null): User + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -300,15 +304,15 @@ public function getUserMetadata(GetUserMetadataRequest $request, ?array $options $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return User::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -342,7 +346,8 @@ public function getUserMetadata(GetUserMetadataRequest $request, ?array $options * @throws SeedException * @throws SeedApiException */ - public function getUserSpecifics(GetUserSpecificsRequest $request, ?array $options = null): User { + public function getUserSpecifics(GetUserSpecificsRequest $request, ?array $options = null): User + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -354,15 +359,15 @@ public function getUserSpecifics(GetUserSpecificsRequest $request, ?array $optio $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return User::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Utils/File.php b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Utils/File.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters-private/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/path-parameters/inline-path-parameters-private/tests/Core/Client/RawClientTest.php b/seed/php-sdk/path-parameters/inline-path-parameters-private/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters-private/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters-private/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/path-parameters/inline-path-parameters-private/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/path-parameters/inline-path-parameters-private/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters-private/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters-private/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/path-parameters/inline-path-parameters-private/tests/Core/Json/EnumTest.php b/seed/php-sdk/path-parameters/inline-path-parameters-private/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters-private/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters-private/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/path-parameters/inline-path-parameters-private/tests/Core/Json/TraitTest.php b/seed/php-sdk/path-parameters/inline-path-parameters-private/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters-private/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters-private/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/path-parameters/inline-path-parameters-private/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/path-parameters/inline-path-parameters-private/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters-private/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters-private/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/path-parameters/inline-path-parameters/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/path-parameters/inline-path-parameters/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/path-parameters/inline-path-parameters/src/Core/Client/RawClient.php b/seed/php-sdk/path-parameters/inline-path-parameters/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters/src/Core/Client/RawClient.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/path-parameters/inline-path-parameters/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/path-parameters/inline-path-parameters/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/path-parameters/inline-path-parameters/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/path-parameters/inline-path-parameters/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/path-parameters/inline-path-parameters/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/path-parameters/inline-path-parameters/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/path-parameters/inline-path-parameters/src/Core/Types/ArrayType.php b/seed/php-sdk/path-parameters/inline-path-parameters/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/path-parameters/inline-path-parameters/src/Core/Types/Constant.php b/seed/php-sdk/path-parameters/inline-path-parameters/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters/src/Core/Types/Constant.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/path-parameters/inline-path-parameters/src/Core/Types/Union.php b/seed/php-sdk/path-parameters/inline-path-parameters/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters/src/Core/Types/Union.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/path-parameters/inline-path-parameters/src/Exceptions/SeedApiException.php b/seed/php-sdk/path-parameters/inline-path-parameters/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/path-parameters/inline-path-parameters/src/Exceptions/SeedException.php b/seed/php-sdk/path-parameters/inline-path-parameters/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters/src/Exceptions/SeedException.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/path-parameters/inline-path-parameters/src/Organizations/OrganizationsClient.php b/seed/php-sdk/path-parameters/inline-path-parameters/src/Organizations/OrganizationsClient.php index 7667615cb6b2..dfbd284288c0 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters/src/Organizations/OrganizationsClient.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters/src/Organizations/OrganizationsClient.php @@ -17,7 +17,7 @@ use Seed\Organizations\Requests\SearchOrganizationsRequest; use Seed\Core\Json\JsonDecoder; -class OrganizationsClient +class OrganizationsClient { /** * @var array{ @@ -45,11 +45,10 @@ class OrganizationsClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -69,7 +68,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getOrganization(string $tenantId, string $organizationId, ?array $options = null): Organization { + public function getOrganization(string $tenantId, string $organizationId, ?array $options = null): Organization + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -81,15 +81,15 @@ public function getOrganization(string $tenantId, string $organizationId, ?array $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return Organization::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -121,7 +121,8 @@ public function getOrganization(string $tenantId, string $organizationId, ?array * @throws SeedException * @throws SeedApiException */ - public function getOrganizationUser(GetOrganizationUserRequest $request, ?array $options = null): User { + public function getOrganizationUser(GetOrganizationUserRequest $request, ?array $options = null): User + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -133,15 +134,15 @@ public function getOrganizationUser(GetOrganizationUserRequest $request, ?array $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return User::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -175,10 +176,11 @@ public function getOrganizationUser(GetOrganizationUserRequest $request, ?array * @throws SeedException * @throws SeedApiException */ - public function searchOrganizations(string $tenantId, string $organizationId, SearchOrganizationsRequest $request = new SearchOrganizationsRequest(), ?array $options = null): array { + public function searchOrganizations(string $tenantId, string $organizationId, SearchOrganizationsRequest $request = new SearchOrganizationsRequest(), ?array $options = null): array + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->limit != null){ + if ($request->limit != null) { $query['limit'] = $request->limit; } try { @@ -192,15 +194,15 @@ public function searchOrganizations(string $tenantId, string $organizationId, Se $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, [Organization::class]); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/path-parameters/inline-path-parameters/src/Organizations/Requests/GetOrganizationUserRequest.php b/seed/php-sdk/path-parameters/inline-path-parameters/src/Organizations/Requests/GetOrganizationUserRequest.php index 0c90242d6085..157debb75d4f 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters/src/Organizations/Requests/GetOrganizationUserRequest.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters/src/Organizations/Requests/GetOrganizationUserRequest.php @@ -30,8 +30,9 @@ class GetOrganizationUserRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->tenantId = $values['tenantId'];$this->organizationId = $values['organizationId'];$this->userId = $values['userId']; + ) { + $this->tenantId = $values['tenantId']; + $this->organizationId = $values['organizationId']; + $this->userId = $values['userId']; } } diff --git a/seed/php-sdk/path-parameters/inline-path-parameters/src/Organizations/Requests/SearchOrganizationsRequest.php b/seed/php-sdk/path-parameters/inline-path-parameters/src/Organizations/Requests/SearchOrganizationsRequest.php index 5f441f4d0f6e..68d1b5e64d43 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters/src/Organizations/Requests/SearchOrganizationsRequest.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters/src/Organizations/Requests/SearchOrganizationsRequest.php @@ -18,8 +18,7 @@ class SearchOrganizationsRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->limit = $values['limit'] ?? null; } } diff --git a/seed/php-sdk/path-parameters/inline-path-parameters/src/Organizations/Types/Organization.php b/seed/php-sdk/path-parameters/inline-path-parameters/src/Organizations/Types/Organization.php index 1b0d9904d160..91ee760249c3 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters/src/Organizations/Types/Organization.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters/src/Organizations/Types/Organization.php @@ -28,15 +28,16 @@ class Organization extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->tags = $values['tags']; + ) { + $this->name = $values['name']; + $this->tags = $values['tags']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/path-parameters/inline-path-parameters/src/SeedClient.php b/seed/php-sdk/path-parameters/inline-path-parameters/src/SeedClient.php index ef52ac412a42..62b81ecff63d 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters/src/SeedClient.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters/src/SeedClient.php @@ -7,7 +7,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var OrganizationsClient $organizations @@ -46,26 +46,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->organizations = new OrganizationsClient($this->client, $this->options); $this->user = new UserClient($this->client, $this->options); } diff --git a/seed/php-sdk/path-parameters/inline-path-parameters/src/User/Requests/GetUserMetadataRequest.php b/seed/php-sdk/path-parameters/inline-path-parameters/src/User/Requests/GetUserMetadataRequest.php index 4dc39d649bcf..4500a8bb556a 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters/src/User/Requests/GetUserMetadataRequest.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters/src/User/Requests/GetUserMetadataRequest.php @@ -30,8 +30,9 @@ class GetUserMetadataRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->tenantId = $values['tenantId'];$this->userId = $values['userId'];$this->version = $values['version']; + ) { + $this->tenantId = $values['tenantId']; + $this->userId = $values['userId']; + $this->version = $values['version']; } } diff --git a/seed/php-sdk/path-parameters/inline-path-parameters/src/User/Requests/GetUserSpecificsRequest.php b/seed/php-sdk/path-parameters/inline-path-parameters/src/User/Requests/GetUserSpecificsRequest.php index 3abb5bfe37ec..ef9b2d41f585 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters/src/User/Requests/GetUserSpecificsRequest.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters/src/User/Requests/GetUserSpecificsRequest.php @@ -36,8 +36,10 @@ class GetUserSpecificsRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->tenantId = $values['tenantId'];$this->userId = $values['userId'];$this->version = $values['version'];$this->thought = $values['thought']; + ) { + $this->tenantId = $values['tenantId']; + $this->userId = $values['userId']; + $this->version = $values['version']; + $this->thought = $values['thought']; } } diff --git a/seed/php-sdk/path-parameters/inline-path-parameters/src/User/Requests/GetUsersRequest.php b/seed/php-sdk/path-parameters/inline-path-parameters/src/User/Requests/GetUsersRequest.php index c5b00a37fe68..ef1d5b6b8e1c 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters/src/User/Requests/GetUsersRequest.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters/src/User/Requests/GetUsersRequest.php @@ -24,8 +24,8 @@ class GetUsersRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->tenantId = $values['tenantId'];$this->userId = $values['userId']; + ) { + $this->tenantId = $values['tenantId']; + $this->userId = $values['userId']; } } diff --git a/seed/php-sdk/path-parameters/inline-path-parameters/src/User/Requests/SearchUsersRequest.php b/seed/php-sdk/path-parameters/inline-path-parameters/src/User/Requests/SearchUsersRequest.php index 3c3de8c3db26..16eb0c7fb343 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters/src/User/Requests/SearchUsersRequest.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters/src/User/Requests/SearchUsersRequest.php @@ -30,8 +30,9 @@ class SearchUsersRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->tenantId = $values['tenantId'];$this->userId = $values['userId'];$this->limit = $values['limit'] ?? null; + ) { + $this->tenantId = $values['tenantId']; + $this->userId = $values['userId']; + $this->limit = $values['limit'] ?? null; } } diff --git a/seed/php-sdk/path-parameters/inline-path-parameters/src/User/Requests/UpdateUserRequest.php b/seed/php-sdk/path-parameters/inline-path-parameters/src/User/Requests/UpdateUserRequest.php index 4a6fb5d4c6d6..3ef51601e664 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters/src/User/Requests/UpdateUserRequest.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters/src/User/Requests/UpdateUserRequest.php @@ -31,8 +31,9 @@ class UpdateUserRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->tenantId = $values['tenantId'];$this->userId = $values['userId'];$this->body = $values['body']; + ) { + $this->tenantId = $values['tenantId']; + $this->userId = $values['userId']; + $this->body = $values['body']; } } diff --git a/seed/php-sdk/path-parameters/inline-path-parameters/src/User/Types/User.php b/seed/php-sdk/path-parameters/inline-path-parameters/src/User/Types/User.php index 0dc2f6896280..c2d2049a60a4 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters/src/User/Types/User.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters/src/User/Types/User.php @@ -28,15 +28,16 @@ class User extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->tags = $values['tags']; + ) { + $this->name = $values['name']; + $this->tags = $values['tags']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/path-parameters/inline-path-parameters/src/User/UserClient.php b/seed/php-sdk/path-parameters/inline-path-parameters/src/User/UserClient.php index a26683f72a92..49bee47ab591 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters/src/User/UserClient.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters/src/User/UserClient.php @@ -19,7 +19,7 @@ use Seed\User\Requests\GetUserMetadataRequest; use Seed\User\Requests\GetUserSpecificsRequest; -class UserClient +class UserClient { /** * @var array{ @@ -47,11 +47,10 @@ class UserClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -70,7 +69,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getUser(GetUsersRequest $request, ?array $options = null): User { + public function getUser(GetUsersRequest $request, ?array $options = null): User + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -82,15 +82,15 @@ public function getUser(GetUsersRequest $request, ?array $options = null): User $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return User::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -123,7 +123,8 @@ public function getUser(GetUsersRequest $request, ?array $options = null): User * @throws SeedException * @throws SeedApiException */ - public function createUser(string $tenantId, User $request, ?array $options = null): User { + public function createUser(string $tenantId, User $request, ?array $options = null): User + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -136,15 +137,15 @@ public function createUser(string $tenantId, User $request, ?array $options = nu $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return User::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -176,7 +177,8 @@ public function createUser(string $tenantId, User $request, ?array $options = nu * @throws SeedException * @throws SeedApiException */ - public function updateUser(UpdateUserRequest $request, ?array $options = null): User { + public function updateUser(UpdateUserRequest $request, ?array $options = null): User + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -189,15 +191,15 @@ public function updateUser(UpdateUserRequest $request, ?array $options = null): $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return User::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -229,10 +231,11 @@ public function updateUser(UpdateUserRequest $request, ?array $options = null): * @throws SeedException * @throws SeedApiException */ - public function searchUsers(SearchUsersRequest $request, ?array $options = null): array { + public function searchUsers(SearchUsersRequest $request, ?array $options = null): array + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->limit != null){ + if ($request->limit != null) { $query['limit'] = $request->limit; } try { @@ -246,15 +249,15 @@ public function searchUsers(SearchUsersRequest $request, ?array $options = null) $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, [User::class]); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -288,7 +291,8 @@ public function searchUsers(SearchUsersRequest $request, ?array $options = null) * @throws SeedException * @throws SeedApiException */ - public function getUserMetadata(GetUserMetadataRequest $request, ?array $options = null): User { + public function getUserMetadata(GetUserMetadataRequest $request, ?array $options = null): User + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -300,15 +304,15 @@ public function getUserMetadata(GetUserMetadataRequest $request, ?array $options $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return User::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -342,7 +346,8 @@ public function getUserMetadata(GetUserMetadataRequest $request, ?array $options * @throws SeedException * @throws SeedApiException */ - public function getUserSpecifics(GetUserSpecificsRequest $request, ?array $options = null): User { + public function getUserSpecifics(GetUserSpecificsRequest $request, ?array $options = null): User + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -354,15 +359,15 @@ public function getUserSpecifics(GetUserSpecificsRequest $request, ?array $optio $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return User::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/path-parameters/inline-path-parameters/src/Utils/File.php b/seed/php-sdk/path-parameters/inline-path-parameters/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters/src/Utils/File.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/path-parameters/inline-path-parameters/tests/Core/Client/RawClientTest.php b/seed/php-sdk/path-parameters/inline-path-parameters/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/path-parameters/inline-path-parameters/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/path-parameters/inline-path-parameters/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/path-parameters/inline-path-parameters/tests/Core/Json/EnumTest.php b/seed/php-sdk/path-parameters/inline-path-parameters/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/path-parameters/inline-path-parameters/tests/Core/Json/TraitTest.php b/seed/php-sdk/path-parameters/inline-path-parameters/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/path-parameters/inline-path-parameters/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/path-parameters/inline-path-parameters/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/path-parameters/inline-path-parameters/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/path-parameters/inline-path-parameters/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/path-parameters/no-custom-config/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/path-parameters/no-custom-config/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/path-parameters/no-custom-config/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/path-parameters/no-custom-config/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/path-parameters/no-custom-config/src/Core/Client/RawClient.php b/seed/php-sdk/path-parameters/no-custom-config/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/path-parameters/no-custom-config/src/Core/Client/RawClient.php +++ b/seed/php-sdk/path-parameters/no-custom-config/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/path-parameters/no-custom-config/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/path-parameters/no-custom-config/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/path-parameters/no-custom-config/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/path-parameters/no-custom-config/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/path-parameters/no-custom-config/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/path-parameters/no-custom-config/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/path-parameters/no-custom-config/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/path-parameters/no-custom-config/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/path-parameters/no-custom-config/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/path-parameters/no-custom-config/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/path-parameters/no-custom-config/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/path-parameters/no-custom-config/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/path-parameters/no-custom-config/src/Core/Types/ArrayType.php b/seed/php-sdk/path-parameters/no-custom-config/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/path-parameters/no-custom-config/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/path-parameters/no-custom-config/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/path-parameters/no-custom-config/src/Core/Types/Constant.php b/seed/php-sdk/path-parameters/no-custom-config/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/path-parameters/no-custom-config/src/Core/Types/Constant.php +++ b/seed/php-sdk/path-parameters/no-custom-config/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/path-parameters/no-custom-config/src/Core/Types/Union.php b/seed/php-sdk/path-parameters/no-custom-config/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/path-parameters/no-custom-config/src/Core/Types/Union.php +++ b/seed/php-sdk/path-parameters/no-custom-config/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/path-parameters/no-custom-config/src/Exceptions/SeedApiException.php b/seed/php-sdk/path-parameters/no-custom-config/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/path-parameters/no-custom-config/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/path-parameters/no-custom-config/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/path-parameters/no-custom-config/src/Exceptions/SeedException.php b/seed/php-sdk/path-parameters/no-custom-config/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/path-parameters/no-custom-config/src/Exceptions/SeedException.php +++ b/seed/php-sdk/path-parameters/no-custom-config/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/path-parameters/no-custom-config/src/Organizations/OrganizationsClient.php b/seed/php-sdk/path-parameters/no-custom-config/src/Organizations/OrganizationsClient.php index 8e2e03ea2c10..9cdbe2b5b439 100644 --- a/seed/php-sdk/path-parameters/no-custom-config/src/Organizations/OrganizationsClient.php +++ b/seed/php-sdk/path-parameters/no-custom-config/src/Organizations/OrganizationsClient.php @@ -16,7 +16,7 @@ use Seed\Organizations\Requests\SearchOrganizationsRequest; use Seed\Core\Json\JsonDecoder; -class OrganizationsClient +class OrganizationsClient { /** * @var array{ @@ -44,11 +44,10 @@ class OrganizationsClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -68,7 +67,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getOrganization(string $tenantId, string $organizationId, ?array $options = null): Organization { + public function getOrganization(string $tenantId, string $organizationId, ?array $options = null): Organization + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -80,15 +80,15 @@ public function getOrganization(string $tenantId, string $organizationId, ?array $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return Organization::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -122,7 +122,8 @@ public function getOrganization(string $tenantId, string $organizationId, ?array * @throws SeedException * @throws SeedApiException */ - public function getOrganizationUser(string $tenantId, string $organizationId, string $userId, ?array $options = null): User { + public function getOrganizationUser(string $tenantId, string $organizationId, string $userId, ?array $options = null): User + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -134,15 +135,15 @@ public function getOrganizationUser(string $tenantId, string $organizationId, st $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return User::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -176,10 +177,11 @@ public function getOrganizationUser(string $tenantId, string $organizationId, st * @throws SeedException * @throws SeedApiException */ - public function searchOrganizations(string $tenantId, string $organizationId, SearchOrganizationsRequest $request = new SearchOrganizationsRequest(), ?array $options = null): array { + public function searchOrganizations(string $tenantId, string $organizationId, SearchOrganizationsRequest $request = new SearchOrganizationsRequest(), ?array $options = null): array + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->limit != null){ + if ($request->limit != null) { $query['limit'] = $request->limit; } try { @@ -193,15 +195,15 @@ public function searchOrganizations(string $tenantId, string $organizationId, Se $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, [Organization::class]); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/path-parameters/no-custom-config/src/Organizations/Requests/SearchOrganizationsRequest.php b/seed/php-sdk/path-parameters/no-custom-config/src/Organizations/Requests/SearchOrganizationsRequest.php index 5f441f4d0f6e..68d1b5e64d43 100644 --- a/seed/php-sdk/path-parameters/no-custom-config/src/Organizations/Requests/SearchOrganizationsRequest.php +++ b/seed/php-sdk/path-parameters/no-custom-config/src/Organizations/Requests/SearchOrganizationsRequest.php @@ -18,8 +18,7 @@ class SearchOrganizationsRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->limit = $values['limit'] ?? null; } } diff --git a/seed/php-sdk/path-parameters/no-custom-config/src/Organizations/Types/Organization.php b/seed/php-sdk/path-parameters/no-custom-config/src/Organizations/Types/Organization.php index 1b0d9904d160..91ee760249c3 100644 --- a/seed/php-sdk/path-parameters/no-custom-config/src/Organizations/Types/Organization.php +++ b/seed/php-sdk/path-parameters/no-custom-config/src/Organizations/Types/Organization.php @@ -28,15 +28,16 @@ class Organization extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->tags = $values['tags']; + ) { + $this->name = $values['name']; + $this->tags = $values['tags']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/path-parameters/no-custom-config/src/SeedClient.php b/seed/php-sdk/path-parameters/no-custom-config/src/SeedClient.php index ef52ac412a42..62b81ecff63d 100644 --- a/seed/php-sdk/path-parameters/no-custom-config/src/SeedClient.php +++ b/seed/php-sdk/path-parameters/no-custom-config/src/SeedClient.php @@ -7,7 +7,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var OrganizationsClient $organizations @@ -46,26 +46,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->organizations = new OrganizationsClient($this->client, $this->options); $this->user = new UserClient($this->client, $this->options); } diff --git a/seed/php-sdk/path-parameters/no-custom-config/src/User/Requests/SearchUsersRequest.php b/seed/php-sdk/path-parameters/no-custom-config/src/User/Requests/SearchUsersRequest.php index 30b4c1426ec0..64ebcd85bc20 100644 --- a/seed/php-sdk/path-parameters/no-custom-config/src/User/Requests/SearchUsersRequest.php +++ b/seed/php-sdk/path-parameters/no-custom-config/src/User/Requests/SearchUsersRequest.php @@ -18,8 +18,7 @@ class SearchUsersRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->limit = $values['limit'] ?? null; } } diff --git a/seed/php-sdk/path-parameters/no-custom-config/src/User/Requests/UpdateUserRequest.php b/seed/php-sdk/path-parameters/no-custom-config/src/User/Requests/UpdateUserRequest.php index 80e65df1cbf5..054591190501 100644 --- a/seed/php-sdk/path-parameters/no-custom-config/src/User/Requests/UpdateUserRequest.php +++ b/seed/php-sdk/path-parameters/no-custom-config/src/User/Requests/UpdateUserRequest.php @@ -19,8 +19,7 @@ class UpdateUserRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->body = $values['body']; } } diff --git a/seed/php-sdk/path-parameters/no-custom-config/src/User/Types/User.php b/seed/php-sdk/path-parameters/no-custom-config/src/User/Types/User.php index 0dc2f6896280..c2d2049a60a4 100644 --- a/seed/php-sdk/path-parameters/no-custom-config/src/User/Types/User.php +++ b/seed/php-sdk/path-parameters/no-custom-config/src/User/Types/User.php @@ -28,15 +28,16 @@ class User extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->tags = $values['tags']; + ) { + $this->name = $values['name']; + $this->tags = $values['tags']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/path-parameters/no-custom-config/src/User/UserClient.php b/seed/php-sdk/path-parameters/no-custom-config/src/User/UserClient.php index 1e0688603e4a..fcf64ad92bdc 100644 --- a/seed/php-sdk/path-parameters/no-custom-config/src/User/UserClient.php +++ b/seed/php-sdk/path-parameters/no-custom-config/src/User/UserClient.php @@ -16,7 +16,7 @@ use Seed\User\Requests\SearchUsersRequest; use Seed\Core\Json\JsonDecoder; -class UserClient +class UserClient { /** * @var array{ @@ -44,11 +44,10 @@ class UserClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -68,7 +67,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getUser(string $tenantId, string $userId, ?array $options = null): User { + public function getUser(string $tenantId, string $userId, ?array $options = null): User + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -80,15 +80,15 @@ public function getUser(string $tenantId, string $userId, ?array $options = null $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return User::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -121,7 +121,8 @@ public function getUser(string $tenantId, string $userId, ?array $options = null * @throws SeedException * @throws SeedApiException */ - public function createUser(string $tenantId, User $request, ?array $options = null): User { + public function createUser(string $tenantId, User $request, ?array $options = null): User + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -134,15 +135,15 @@ public function createUser(string $tenantId, User $request, ?array $options = nu $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return User::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -176,7 +177,8 @@ public function createUser(string $tenantId, User $request, ?array $options = nu * @throws SeedException * @throws SeedApiException */ - public function updateUser(string $tenantId, string $userId, UpdateUserRequest $request, ?array $options = null): User { + public function updateUser(string $tenantId, string $userId, UpdateUserRequest $request, ?array $options = null): User + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -189,15 +191,15 @@ public function updateUser(string $tenantId, string $userId, UpdateUserRequest $ $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return User::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -231,10 +233,11 @@ public function updateUser(string $tenantId, string $userId, UpdateUserRequest $ * @throws SeedException * @throws SeedApiException */ - public function searchUsers(string $tenantId, string $userId, SearchUsersRequest $request = new SearchUsersRequest(), ?array $options = null): array { + public function searchUsers(string $tenantId, string $userId, SearchUsersRequest $request = new SearchUsersRequest(), ?array $options = null): array + { $options = array_merge($this->options, $options ?? []); $query = []; - if ($request->limit != null){ + if ($request->limit != null) { $query['limit'] = $request->limit; } try { @@ -248,15 +251,15 @@ public function searchUsers(string $tenantId, string $userId, SearchUsersRequest $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, [User::class]); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -292,7 +295,8 @@ public function searchUsers(string $tenantId, string $userId, SearchUsersRequest * @throws SeedException * @throws SeedApiException */ - public function getUserMetadata(string $tenantId, string $userId, int $version, ?array $options = null): User { + public function getUserMetadata(string $tenantId, string $userId, int $version, ?array $options = null): User + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -304,15 +308,15 @@ public function getUserMetadata(string $tenantId, string $userId, int $version, $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return User::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -349,7 +353,8 @@ public function getUserMetadata(string $tenantId, string $userId, int $version, * @throws SeedException * @throws SeedApiException */ - public function getUserSpecifics(string $tenantId, string $userId, int $version, string $thought, ?array $options = null): User { + public function getUserSpecifics(string $tenantId, string $userId, int $version, string $thought, ?array $options = null): User + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -361,15 +366,15 @@ public function getUserSpecifics(string $tenantId, string $userId, int $version, $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return User::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/path-parameters/no-custom-config/src/Utils/File.php b/seed/php-sdk/path-parameters/no-custom-config/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/path-parameters/no-custom-config/src/Utils/File.php +++ b/seed/php-sdk/path-parameters/no-custom-config/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/path-parameters/no-custom-config/tests/Core/Client/RawClientTest.php b/seed/php-sdk/path-parameters/no-custom-config/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/path-parameters/no-custom-config/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/path-parameters/no-custom-config/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/path-parameters/no-custom-config/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/path-parameters/no-custom-config/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/path-parameters/no-custom-config/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/path-parameters/no-custom-config/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/path-parameters/no-custom-config/tests/Core/Json/EnumTest.php b/seed/php-sdk/path-parameters/no-custom-config/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/path-parameters/no-custom-config/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/path-parameters/no-custom-config/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/path-parameters/no-custom-config/tests/Core/Json/TraitTest.php b/seed/php-sdk/path-parameters/no-custom-config/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/path-parameters/no-custom-config/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/path-parameters/no-custom-config/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/path-parameters/no-custom-config/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/path-parameters/no-custom-config/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/path-parameters/no-custom-config/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/path-parameters/no-custom-config/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/plain-text/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/plain-text/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/plain-text/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/plain-text/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/plain-text/src/Core/Client/RawClient.php b/seed/php-sdk/plain-text/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/plain-text/src/Core/Client/RawClient.php +++ b/seed/php-sdk/plain-text/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/plain-text/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/plain-text/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/plain-text/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/plain-text/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/plain-text/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/plain-text/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/plain-text/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/plain-text/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/plain-text/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/plain-text/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/plain-text/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/plain-text/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/plain-text/src/Core/Types/ArrayType.php b/seed/php-sdk/plain-text/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/plain-text/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/plain-text/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/plain-text/src/Core/Types/Constant.php b/seed/php-sdk/plain-text/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/plain-text/src/Core/Types/Constant.php +++ b/seed/php-sdk/plain-text/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/plain-text/src/Core/Types/Union.php b/seed/php-sdk/plain-text/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/plain-text/src/Core/Types/Union.php +++ b/seed/php-sdk/plain-text/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/plain-text/src/Exceptions/SeedApiException.php b/seed/php-sdk/plain-text/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/plain-text/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/plain-text/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/plain-text/src/Exceptions/SeedException.php b/seed/php-sdk/plain-text/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/plain-text/src/Exceptions/SeedException.php +++ b/seed/php-sdk/plain-text/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/plain-text/src/SeedClient.php b/seed/php-sdk/plain-text/src/SeedClient.php index 8d7adcb3b0f2..0fd6a9d1029e 100644 --- a/seed/php-sdk/plain-text/src/SeedClient.php +++ b/seed/php-sdk/plain-text/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var ServiceClient $service @@ -40,26 +40,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->service = new ServiceClient($this->client, $this->options); } } diff --git a/seed/php-sdk/plain-text/src/Service/ServiceClient.php b/seed/php-sdk/plain-text/src/Service/ServiceClient.php index 0671947bce66..3305658ada2c 100644 --- a/seed/php-sdk/plain-text/src/Service/ServiceClient.php +++ b/seed/php-sdk/plain-text/src/Service/ServiceClient.php @@ -11,7 +11,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class ServiceClient +class ServiceClient { /** * @var array{ @@ -39,11 +39,10 @@ class ServiceClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -61,7 +60,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getText(?array $options = null): string { + public function getText(?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -73,12 +73,12 @@ public function getText(?array $options = null): string { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return $response->getBody()->getContents(); } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/plain-text/src/Utils/File.php b/seed/php-sdk/plain-text/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/plain-text/src/Utils/File.php +++ b/seed/php-sdk/plain-text/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/plain-text/tests/Core/Client/RawClientTest.php b/seed/php-sdk/plain-text/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/plain-text/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/plain-text/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/plain-text/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/plain-text/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/plain-text/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/plain-text/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/plain-text/tests/Core/Json/EnumTest.php b/seed/php-sdk/plain-text/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/plain-text/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/plain-text/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/plain-text/tests/Core/Json/TraitTest.php b/seed/php-sdk/plain-text/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/plain-text/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/plain-text/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/plain-text/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/plain-text/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/plain-text/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/plain-text/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/property-access/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/property-access/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/property-access/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/property-access/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/property-access/src/Core/Client/RawClient.php b/seed/php-sdk/property-access/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/property-access/src/Core/Client/RawClient.php +++ b/seed/php-sdk/property-access/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/property-access/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/property-access/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/property-access/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/property-access/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/property-access/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/property-access/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/property-access/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/property-access/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/property-access/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/property-access/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/property-access/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/property-access/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/property-access/src/Core/Types/ArrayType.php b/seed/php-sdk/property-access/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/property-access/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/property-access/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/property-access/src/Core/Types/Constant.php b/seed/php-sdk/property-access/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/property-access/src/Core/Types/Constant.php +++ b/seed/php-sdk/property-access/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/property-access/src/Core/Types/Union.php b/seed/php-sdk/property-access/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/property-access/src/Core/Types/Union.php +++ b/seed/php-sdk/property-access/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/property-access/src/Exceptions/SeedApiException.php b/seed/php-sdk/property-access/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/property-access/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/property-access/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/property-access/src/Exceptions/SeedException.php b/seed/php-sdk/property-access/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/property-access/src/Exceptions/SeedException.php +++ b/seed/php-sdk/property-access/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/property-access/src/SeedClient.php b/seed/php-sdk/property-access/src/SeedClient.php index 3dd0900e184b..174806956d11 100644 --- a/seed/php-sdk/property-access/src/SeedClient.php +++ b/seed/php-sdk/property-access/src/SeedClient.php @@ -13,7 +13,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class SeedClient +class SeedClient { /** * @var array{ @@ -42,22 +42,21 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); @@ -77,7 +76,8 @@ public function __construct( * @throws SeedException * @throws SeedApiException */ - public function createUser(User $request, ?array $options = null): User { + public function createUser(User $request, ?array $options = null): User + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -90,15 +90,15 @@ public function createUser(User $request, ?array $options = null): User { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return User::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/property-access/src/Traits/User.php b/seed/php-sdk/property-access/src/Traits/User.php index c9c399845708..aa63a7d7c5c0 100644 --- a/seed/php-sdk/property-access/src/Traits/User.php +++ b/seed/php-sdk/property-access/src/Traits/User.php @@ -13,7 +13,7 @@ * @property string $password * @property UserProfile $profile */ -trait User +trait User { /** * @var string $id The unique identifier for the user. diff --git a/seed/php-sdk/property-access/src/Types/Admin.php b/seed/php-sdk/property-access/src/Types/Admin.php index 7b79f632b200..ed7572504f96 100644 --- a/seed/php-sdk/property-access/src/Types/Admin.php +++ b/seed/php-sdk/property-access/src/Types/Admin.php @@ -30,15 +30,19 @@ class Admin extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->email = $values['email'];$this->password = $values['password'];$this->profile = $values['profile'];$this->adminLevel = $values['adminLevel']; + ) { + $this->id = $values['id']; + $this->email = $values['email']; + $this->password = $values['password']; + $this->profile = $values['profile']; + $this->adminLevel = $values['adminLevel']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/property-access/src/Types/Foo.php b/seed/php-sdk/property-access/src/Types/Foo.php index eb3b9a4b6cb1..4d5fe0298aa7 100644 --- a/seed/php-sdk/property-access/src/Types/Foo.php +++ b/seed/php-sdk/property-access/src/Types/Foo.php @@ -34,15 +34,17 @@ class Foo extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->normal = $values['normal'];$this->read = $values['read'];$this->write = $values['write']; + ) { + $this->normal = $values['normal']; + $this->read = $values['read']; + $this->write = $values['write']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/property-access/src/Types/User.php b/seed/php-sdk/property-access/src/Types/User.php index 0b421f2a5f01..51579dde9134 100644 --- a/seed/php-sdk/property-access/src/Types/User.php +++ b/seed/php-sdk/property-access/src/Types/User.php @@ -44,15 +44,18 @@ class User extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->email = $values['email'];$this->password = $values['password'];$this->profile = $values['profile']; + ) { + $this->id = $values['id']; + $this->email = $values['email']; + $this->password = $values['password']; + $this->profile = $values['profile']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/property-access/src/Types/UserOrAdminDiscriminated.php b/seed/php-sdk/property-access/src/Types/UserOrAdminDiscriminated.php index f37a643948c7..bbaa4c3357c1 100644 --- a/seed/php-sdk/property-access/src/Types/UserOrAdminDiscriminated.php +++ b/seed/php-sdk/property-access/src/Types/UserOrAdminDiscriminated.php @@ -64,9 +64,11 @@ class UserOrAdminDiscriminated extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->normal = $values['normal'];$this->foo = $values['foo'];$this->type = $values['type'];$this->value = $values['value']; + ) { + $this->normal = $values['normal']; + $this->foo = $values['foo']; + $this->type = $values['type']; + $this->value = $values['value']; } /** @@ -75,7 +77,8 @@ private function __construct( * @param User $user * @return UserOrAdminDiscriminated */ - public static function user(string $normal, Foo $foo, User $user): UserOrAdminDiscriminated { + public static function user(string $normal, Foo $foo, User $user): UserOrAdminDiscriminated + { return new UserOrAdminDiscriminated([ 'normal' => $normal, 'foo' => $foo, @@ -90,7 +93,8 @@ public static function user(string $normal, Foo $foo, User $user): UserOrAdminDi * @param Admin $admin * @return UserOrAdminDiscriminated */ - public static function admin(string $normal, Foo $foo, Admin $admin): UserOrAdminDiscriminated { + public static function admin(string $normal, Foo $foo, Admin $admin): UserOrAdminDiscriminated + { return new UserOrAdminDiscriminated([ 'normal' => $normal, 'foo' => $foo, @@ -104,7 +108,8 @@ public static function admin(string $normal, Foo $foo, Admin $admin): UserOrAdmi * @param Foo $foo * @return UserOrAdminDiscriminated */ - public static function empty(string $normal, Foo $foo): UserOrAdminDiscriminated { + public static function empty(string $normal, Foo $foo): UserOrAdminDiscriminated + { return new UserOrAdminDiscriminated([ 'normal' => $normal, 'foo' => $foo, @@ -116,68 +121,75 @@ public static function empty(string $normal, Foo $foo): UserOrAdminDiscriminated /** * @return bool */ - public function isUser(): bool { - return $this->value instanceof User&& $this->type === 'user'; + public function isUser(): bool + { + return $this->value instanceof User && $this->type === 'user'; } /** * @return User */ - public function asUser(): User { - if (!($this->value instanceof User&& $this->type === 'user')){ + public function asUser(): User + { + if (!($this->value instanceof User && $this->type === 'user')) { throw new Exception( "Expected user; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isAdmin(): bool { - return $this->value instanceof Admin&& $this->type === 'admin'; + public function isAdmin(): bool + { + return $this->value instanceof Admin && $this->type === 'admin'; } /** * @return Admin */ - public function asAdmin(): Admin { - if (!($this->value instanceof Admin&& $this->type === 'admin')){ + public function asAdmin(): Admin + { + if (!($this->value instanceof Admin && $this->type === 'admin')) { throw new Exception( "Expected admin; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isEmpty(): bool { - return is_null($this->value)&& $this->type === 'empty'; + public function isEmpty(): bool + { + return is_null($this->value) && $this->type === 'empty'; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'user': $value = $this->asUser()->jsonSerialize(); $result = array_merge($value, $result); @@ -191,26 +203,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -219,57 +232,58 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('normal', $data)){ + if (!array_key_exists('normal', $data)) { throw new Exception( "JSON data is missing property 'normal'", ); } - if (!(is_string($data['normal']))){ + if (!(is_string($data['normal']))) { throw new Exception( "Expected property 'normal' in JSON data to be string, instead received " . get_debug_type($data['normal']), ); } $args['normal'] = $data['normal']; - - if (!array_key_exists('foo', $data)){ + + if (!array_key_exists('foo', $data)) { throw new Exception( "JSON data is missing property 'foo'", ); } - if (!($data['foo'] instanceof Foo)){ + if (!($data['foo'] instanceof Foo)) { throw new Exception( "Expected property 'foo' in JSON data to be reference, instead received " . get_debug_type($data['foo']), ); } $args['foo'] = $data['foo']; - - if (!array_key_exists('type', $data)){ + + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'user': $args['value'] = User::jsonDeserialize($data); break; case 'admin': - if (!array_key_exists('admin', $data)){ + if (!array_key_exists('admin', $data)) { throw new Exception( "JSON data is missing property 'admin'", ); } - - if (!(is_array($data['admin']))){ + + if (!(is_array($data['admin']))) { throw new Exception( "Expected property 'admin' in JSON data to be array, instead received " . get_debug_type($data['admin']), ); @@ -284,7 +298,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/property-access/src/Types/UserProfile.php b/seed/php-sdk/property-access/src/Types/UserProfile.php index dde2e3dbdc61..9a3acbe8cea6 100644 --- a/seed/php-sdk/property-access/src/Types/UserProfile.php +++ b/seed/php-sdk/property-access/src/Types/UserProfile.php @@ -37,15 +37,17 @@ class UserProfile extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->verification = $values['verification'];$this->ssn = $values['ssn']; + ) { + $this->name = $values['name']; + $this->verification = $values['verification']; + $this->ssn = $values['ssn']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/property-access/src/Types/UserProfileVerification.php b/seed/php-sdk/property-access/src/Types/UserProfileVerification.php index 57deee91ebb0..c504aa41c2d1 100644 --- a/seed/php-sdk/property-access/src/Types/UserProfileVerification.php +++ b/seed/php-sdk/property-access/src/Types/UserProfileVerification.php @@ -23,15 +23,15 @@ class UserProfileVerification extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->verified = $values['verified']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/property-access/src/Utils/File.php b/seed/php-sdk/property-access/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/property-access/src/Utils/File.php +++ b/seed/php-sdk/property-access/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/property-access/tests/Core/Client/RawClientTest.php b/seed/php-sdk/property-access/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/property-access/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/property-access/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/property-access/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/property-access/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/property-access/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/property-access/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/property-access/tests/Core/Json/EnumTest.php b/seed/php-sdk/property-access/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/property-access/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/property-access/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/property-access/tests/Core/Json/TraitTest.php b/seed/php-sdk/property-access/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/property-access/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/property-access/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/property-access/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/property-access/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/property-access/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/property-access/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/public-object/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/public-object/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/public-object/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/public-object/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/public-object/src/Core/Client/RawClient.php b/seed/php-sdk/public-object/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/public-object/src/Core/Client/RawClient.php +++ b/seed/php-sdk/public-object/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/public-object/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/public-object/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/public-object/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/public-object/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/public-object/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/public-object/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/public-object/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/public-object/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/public-object/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/public-object/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/public-object/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/public-object/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/public-object/src/Core/Types/ArrayType.php b/seed/php-sdk/public-object/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/public-object/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/public-object/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/public-object/src/Core/Types/Constant.php b/seed/php-sdk/public-object/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/public-object/src/Core/Types/Constant.php +++ b/seed/php-sdk/public-object/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/public-object/src/Core/Types/Union.php b/seed/php-sdk/public-object/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/public-object/src/Core/Types/Union.php +++ b/seed/php-sdk/public-object/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/public-object/src/Exceptions/SeedApiException.php b/seed/php-sdk/public-object/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/public-object/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/public-object/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/public-object/src/Exceptions/SeedException.php b/seed/php-sdk/public-object/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/public-object/src/Exceptions/SeedException.php +++ b/seed/php-sdk/public-object/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/public-object/src/SeedClient.php b/seed/php-sdk/public-object/src/SeedClient.php index 8d7adcb3b0f2..0fd6a9d1029e 100644 --- a/seed/php-sdk/public-object/src/SeedClient.php +++ b/seed/php-sdk/public-object/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var ServiceClient $service @@ -40,26 +40,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->service = new ServiceClient($this->client, $this->options); } } diff --git a/seed/php-sdk/public-object/src/Service/ServiceClient.php b/seed/php-sdk/public-object/src/Service/ServiceClient.php index b295992e5c44..558b96283341 100644 --- a/seed/php-sdk/public-object/src/Service/ServiceClient.php +++ b/seed/php-sdk/public-object/src/Service/ServiceClient.php @@ -11,7 +11,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class ServiceClient +class ServiceClient { /** * @var array{ @@ -39,11 +39,10 @@ class ServiceClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -60,7 +59,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function get(?array $options = null): void { + public function get(?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -74,7 +74,7 @@ public function get(?array $options = null): void { $statusCode = $response->getStatusCode(); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/public-object/src/Utils/File.php b/seed/php-sdk/public-object/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/public-object/src/Utils/File.php +++ b/seed/php-sdk/public-object/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/public-object/tests/Core/Client/RawClientTest.php b/seed/php-sdk/public-object/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/public-object/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/public-object/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/public-object/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/public-object/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/public-object/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/public-object/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/public-object/tests/Core/Json/EnumTest.php b/seed/php-sdk/public-object/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/public-object/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/public-object/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/public-object/tests/Core/Json/TraitTest.php b/seed/php-sdk/public-object/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/public-object/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/public-object/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/public-object/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/public-object/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/public-object/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/public-object/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/query-parameters-openapi-as-objects/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/query-parameters-openapi-as-objects/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/query-parameters-openapi-as-objects/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/query-parameters-openapi-as-objects/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/query-parameters-openapi-as-objects/src/Core/Client/RawClient.php b/seed/php-sdk/query-parameters-openapi-as-objects/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/query-parameters-openapi-as-objects/src/Core/Client/RawClient.php +++ b/seed/php-sdk/query-parameters-openapi-as-objects/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/query-parameters-openapi-as-objects/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/query-parameters-openapi-as-objects/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/query-parameters-openapi-as-objects/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/query-parameters-openapi-as-objects/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/query-parameters-openapi-as-objects/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/query-parameters-openapi-as-objects/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/query-parameters-openapi-as-objects/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/query-parameters-openapi-as-objects/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/query-parameters-openapi-as-objects/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/query-parameters-openapi-as-objects/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/query-parameters-openapi-as-objects/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/query-parameters-openapi-as-objects/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/query-parameters-openapi-as-objects/src/Core/Types/ArrayType.php b/seed/php-sdk/query-parameters-openapi-as-objects/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/query-parameters-openapi-as-objects/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/query-parameters-openapi-as-objects/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/query-parameters-openapi-as-objects/src/Core/Types/Constant.php b/seed/php-sdk/query-parameters-openapi-as-objects/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/query-parameters-openapi-as-objects/src/Core/Types/Constant.php +++ b/seed/php-sdk/query-parameters-openapi-as-objects/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/query-parameters-openapi-as-objects/src/Core/Types/Union.php b/seed/php-sdk/query-parameters-openapi-as-objects/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/query-parameters-openapi-as-objects/src/Core/Types/Union.php +++ b/seed/php-sdk/query-parameters-openapi-as-objects/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/query-parameters-openapi-as-objects/src/Exceptions/SeedApiException.php b/seed/php-sdk/query-parameters-openapi-as-objects/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/query-parameters-openapi-as-objects/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/query-parameters-openapi-as-objects/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/query-parameters-openapi-as-objects/src/Exceptions/SeedException.php b/seed/php-sdk/query-parameters-openapi-as-objects/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/query-parameters-openapi-as-objects/src/Exceptions/SeedException.php +++ b/seed/php-sdk/query-parameters-openapi-as-objects/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/query-parameters-openapi-as-objects/src/Requests/SearchRequest.php b/seed/php-sdk/query-parameters-openapi-as-objects/src/Requests/SearchRequest.php index 606bd45c2845..4e4dd5e87233 100644 --- a/seed/php-sdk/query-parameters-openapi-as-objects/src/Requests/SearchRequest.php +++ b/seed/php-sdk/query-parameters-openapi-as-objects/src/Requests/SearchRequest.php @@ -131,8 +131,22 @@ class SearchRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->limit = $values['limit'];$this->id = $values['id'];$this->date = $values['date'];$this->deadline = $values['deadline'];$this->bytes = $values['bytes'];$this->user = $values['user'];$this->userList = $values['userList'] ?? null;$this->optionalDeadline = $values['optionalDeadline'] ?? null;$this->keyValue = $values['keyValue'] ?? null;$this->optionalString = $values['optionalString'] ?? null;$this->nestedUser = $values['nestedUser'] ?? null;$this->optionalUser = $values['optionalUser'] ?? null;$this->excludeUser = $values['excludeUser'] ?? null;$this->filter = $values['filter'] ?? null;$this->neighbor = $values['neighbor'] ?? null;$this->neighborRequired = $values['neighborRequired']; + ) { + $this->limit = $values['limit']; + $this->id = $values['id']; + $this->date = $values['date']; + $this->deadline = $values['deadline']; + $this->bytes = $values['bytes']; + $this->user = $values['user']; + $this->userList = $values['userList'] ?? null; + $this->optionalDeadline = $values['optionalDeadline'] ?? null; + $this->keyValue = $values['keyValue'] ?? null; + $this->optionalString = $values['optionalString'] ?? null; + $this->nestedUser = $values['nestedUser'] ?? null; + $this->optionalUser = $values['optionalUser'] ?? null; + $this->excludeUser = $values['excludeUser'] ?? null; + $this->filter = $values['filter'] ?? null; + $this->neighbor = $values['neighbor'] ?? null; + $this->neighborRequired = $values['neighborRequired']; } } diff --git a/seed/php-sdk/query-parameters-openapi-as-objects/src/SeedClient.php b/seed/php-sdk/query-parameters-openapi-as-objects/src/SeedClient.php index 7d2d18905fb1..e9334d384769 100644 --- a/seed/php-sdk/query-parameters-openapi-as-objects/src/SeedClient.php +++ b/seed/php-sdk/query-parameters-openapi-as-objects/src/SeedClient.php @@ -18,7 +18,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class SeedClient +class SeedClient { /** * @var array{ @@ -47,22 +47,21 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); @@ -82,7 +81,8 @@ public function __construct( * @throws SeedException * @throws SeedApiException */ - public function search(SearchRequest $request, ?array $options = null): SearchResponse { + public function search(SearchRequest $request, ?array $options = null): SearchResponse + { $options = array_merge($this->options, $options ?? []); $query = []; $query['limit'] = $request->limit; @@ -92,31 +92,31 @@ public function search(SearchRequest $request, ?array $options = null): SearchRe $query['bytes'] = $request->bytes; $query['user'] = $request->user; $query['neighborRequired'] = JsonSerializer::serializeUnion($request->neighborRequired, new Union(User::class, NestedUser::class, 'string', 'integer')); - if ($request->userList != null){ + if ($request->userList != null) { $query['userList'] = $request->userList; } - if ($request->optionalDeadline != null){ + if ($request->optionalDeadline != null) { $query['optionalDeadline'] = JsonSerializer::serializeDateTime($request->optionalDeadline); } - if ($request->keyValue != null){ + if ($request->keyValue != null) { $query['keyValue'] = $request->keyValue; } - if ($request->optionalString != null){ + if ($request->optionalString != null) { $query['optionalString'] = $request->optionalString; } - if ($request->nestedUser != null){ + if ($request->nestedUser != null) { $query['nestedUser'] = $request->nestedUser; } - if ($request->optionalUser != null){ + if ($request->optionalUser != null) { $query['optionalUser'] = $request->optionalUser; } - if ($request->excludeUser != null){ + if ($request->excludeUser != null) { $query['excludeUser'] = $request->excludeUser; } - if ($request->filter != null){ + if ($request->filter != null) { $query['filter'] = $request->filter; } - if ($request->neighbor != null){ + if ($request->neighbor != null) { $query['neighbor'] = JsonSerializer::serializeUnion($request->neighbor, new Union(User::class, NestedUser::class, 'string', 'integer')); } try { @@ -130,15 +130,15 @@ public function search(SearchRequest $request, ?array $options = null): SearchRe $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return SearchResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/query-parameters-openapi-as-objects/src/Types/NestedUser.php b/seed/php-sdk/query-parameters-openapi-as-objects/src/Types/NestedUser.php index 001fdeef2586..55af08badf3d 100644 --- a/seed/php-sdk/query-parameters-openapi-as-objects/src/Types/NestedUser.php +++ b/seed/php-sdk/query-parameters-openapi-as-objects/src/Types/NestedUser.php @@ -27,15 +27,16 @@ class NestedUser extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->name = $values['name'] ?? null;$this->user = $values['user'] ?? null; + ) { + $this->name = $values['name'] ?? null; + $this->user = $values['user'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/query-parameters-openapi-as-objects/src/Types/SearchResponse.php b/seed/php-sdk/query-parameters-openapi-as-objects/src/Types/SearchResponse.php index 5b45aeb820ad..1976b4f0b0ad 100644 --- a/seed/php-sdk/query-parameters-openapi-as-objects/src/Types/SearchResponse.php +++ b/seed/php-sdk/query-parameters-openapi-as-objects/src/Types/SearchResponse.php @@ -21,15 +21,15 @@ class SearchResponse extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->results = $values['results'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/query-parameters-openapi-as-objects/src/Types/User.php b/seed/php-sdk/query-parameters-openapi-as-objects/src/Types/User.php index d2073133a61b..b91cee63a45e 100644 --- a/seed/php-sdk/query-parameters-openapi-as-objects/src/Types/User.php +++ b/seed/php-sdk/query-parameters-openapi-as-objects/src/Types/User.php @@ -28,15 +28,16 @@ class User extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->name = $values['name'] ?? null;$this->tags = $values['tags'] ?? null; + ) { + $this->name = $values['name'] ?? null; + $this->tags = $values['tags'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/query-parameters-openapi-as-objects/src/Utils/File.php b/seed/php-sdk/query-parameters-openapi-as-objects/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/query-parameters-openapi-as-objects/src/Utils/File.php +++ b/seed/php-sdk/query-parameters-openapi-as-objects/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/query-parameters-openapi-as-objects/tests/Core/Client/RawClientTest.php b/seed/php-sdk/query-parameters-openapi-as-objects/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/query-parameters-openapi-as-objects/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/query-parameters-openapi-as-objects/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/query-parameters-openapi-as-objects/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/query-parameters-openapi-as-objects/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/query-parameters-openapi-as-objects/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/query-parameters-openapi-as-objects/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/query-parameters-openapi-as-objects/tests/Core/Json/EnumTest.php b/seed/php-sdk/query-parameters-openapi-as-objects/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/query-parameters-openapi-as-objects/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/query-parameters-openapi-as-objects/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/query-parameters-openapi-as-objects/tests/Core/Json/TraitTest.php b/seed/php-sdk/query-parameters-openapi-as-objects/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/query-parameters-openapi-as-objects/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/query-parameters-openapi-as-objects/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/query-parameters-openapi-as-objects/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/query-parameters-openapi-as-objects/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/query-parameters-openapi-as-objects/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/query-parameters-openapi-as-objects/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/query-parameters-openapi/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/query-parameters-openapi/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/query-parameters-openapi/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/query-parameters-openapi/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/query-parameters-openapi/src/Core/Client/RawClient.php b/seed/php-sdk/query-parameters-openapi/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/query-parameters-openapi/src/Core/Client/RawClient.php +++ b/seed/php-sdk/query-parameters-openapi/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/query-parameters-openapi/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/query-parameters-openapi/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/query-parameters-openapi/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/query-parameters-openapi/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/query-parameters-openapi/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/query-parameters-openapi/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/query-parameters-openapi/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/query-parameters-openapi/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/query-parameters-openapi/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/query-parameters-openapi/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/query-parameters-openapi/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/query-parameters-openapi/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/query-parameters-openapi/src/Core/Types/ArrayType.php b/seed/php-sdk/query-parameters-openapi/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/query-parameters-openapi/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/query-parameters-openapi/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/query-parameters-openapi/src/Core/Types/Constant.php b/seed/php-sdk/query-parameters-openapi/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/query-parameters-openapi/src/Core/Types/Constant.php +++ b/seed/php-sdk/query-parameters-openapi/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/query-parameters-openapi/src/Core/Types/Union.php b/seed/php-sdk/query-parameters-openapi/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/query-parameters-openapi/src/Core/Types/Union.php +++ b/seed/php-sdk/query-parameters-openapi/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/query-parameters-openapi/src/Exceptions/SeedApiException.php b/seed/php-sdk/query-parameters-openapi/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/query-parameters-openapi/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/query-parameters-openapi/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/query-parameters-openapi/src/Exceptions/SeedException.php b/seed/php-sdk/query-parameters-openapi/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/query-parameters-openapi/src/Exceptions/SeedException.php +++ b/seed/php-sdk/query-parameters-openapi/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/query-parameters-openapi/src/Requests/SearchRequest.php b/seed/php-sdk/query-parameters-openapi/src/Requests/SearchRequest.php index 606bd45c2845..4e4dd5e87233 100644 --- a/seed/php-sdk/query-parameters-openapi/src/Requests/SearchRequest.php +++ b/seed/php-sdk/query-parameters-openapi/src/Requests/SearchRequest.php @@ -131,8 +131,22 @@ class SearchRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->limit = $values['limit'];$this->id = $values['id'];$this->date = $values['date'];$this->deadline = $values['deadline'];$this->bytes = $values['bytes'];$this->user = $values['user'];$this->userList = $values['userList'] ?? null;$this->optionalDeadline = $values['optionalDeadline'] ?? null;$this->keyValue = $values['keyValue'] ?? null;$this->optionalString = $values['optionalString'] ?? null;$this->nestedUser = $values['nestedUser'] ?? null;$this->optionalUser = $values['optionalUser'] ?? null;$this->excludeUser = $values['excludeUser'] ?? null;$this->filter = $values['filter'] ?? null;$this->neighbor = $values['neighbor'] ?? null;$this->neighborRequired = $values['neighborRequired']; + ) { + $this->limit = $values['limit']; + $this->id = $values['id']; + $this->date = $values['date']; + $this->deadline = $values['deadline']; + $this->bytes = $values['bytes']; + $this->user = $values['user']; + $this->userList = $values['userList'] ?? null; + $this->optionalDeadline = $values['optionalDeadline'] ?? null; + $this->keyValue = $values['keyValue'] ?? null; + $this->optionalString = $values['optionalString'] ?? null; + $this->nestedUser = $values['nestedUser'] ?? null; + $this->optionalUser = $values['optionalUser'] ?? null; + $this->excludeUser = $values['excludeUser'] ?? null; + $this->filter = $values['filter'] ?? null; + $this->neighbor = $values['neighbor'] ?? null; + $this->neighborRequired = $values['neighborRequired']; } } diff --git a/seed/php-sdk/query-parameters-openapi/src/SeedClient.php b/seed/php-sdk/query-parameters-openapi/src/SeedClient.php index 7d2d18905fb1..e9334d384769 100644 --- a/seed/php-sdk/query-parameters-openapi/src/SeedClient.php +++ b/seed/php-sdk/query-parameters-openapi/src/SeedClient.php @@ -18,7 +18,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class SeedClient +class SeedClient { /** * @var array{ @@ -47,22 +47,21 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); @@ -82,7 +81,8 @@ public function __construct( * @throws SeedException * @throws SeedApiException */ - public function search(SearchRequest $request, ?array $options = null): SearchResponse { + public function search(SearchRequest $request, ?array $options = null): SearchResponse + { $options = array_merge($this->options, $options ?? []); $query = []; $query['limit'] = $request->limit; @@ -92,31 +92,31 @@ public function search(SearchRequest $request, ?array $options = null): SearchRe $query['bytes'] = $request->bytes; $query['user'] = $request->user; $query['neighborRequired'] = JsonSerializer::serializeUnion($request->neighborRequired, new Union(User::class, NestedUser::class, 'string', 'integer')); - if ($request->userList != null){ + if ($request->userList != null) { $query['userList'] = $request->userList; } - if ($request->optionalDeadline != null){ + if ($request->optionalDeadline != null) { $query['optionalDeadline'] = JsonSerializer::serializeDateTime($request->optionalDeadline); } - if ($request->keyValue != null){ + if ($request->keyValue != null) { $query['keyValue'] = $request->keyValue; } - if ($request->optionalString != null){ + if ($request->optionalString != null) { $query['optionalString'] = $request->optionalString; } - if ($request->nestedUser != null){ + if ($request->nestedUser != null) { $query['nestedUser'] = $request->nestedUser; } - if ($request->optionalUser != null){ + if ($request->optionalUser != null) { $query['optionalUser'] = $request->optionalUser; } - if ($request->excludeUser != null){ + if ($request->excludeUser != null) { $query['excludeUser'] = $request->excludeUser; } - if ($request->filter != null){ + if ($request->filter != null) { $query['filter'] = $request->filter; } - if ($request->neighbor != null){ + if ($request->neighbor != null) { $query['neighbor'] = JsonSerializer::serializeUnion($request->neighbor, new Union(User::class, NestedUser::class, 'string', 'integer')); } try { @@ -130,15 +130,15 @@ public function search(SearchRequest $request, ?array $options = null): SearchRe $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return SearchResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/query-parameters-openapi/src/Types/NestedUser.php b/seed/php-sdk/query-parameters-openapi/src/Types/NestedUser.php index 001fdeef2586..55af08badf3d 100644 --- a/seed/php-sdk/query-parameters-openapi/src/Types/NestedUser.php +++ b/seed/php-sdk/query-parameters-openapi/src/Types/NestedUser.php @@ -27,15 +27,16 @@ class NestedUser extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->name = $values['name'] ?? null;$this->user = $values['user'] ?? null; + ) { + $this->name = $values['name'] ?? null; + $this->user = $values['user'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/query-parameters-openapi/src/Types/SearchResponse.php b/seed/php-sdk/query-parameters-openapi/src/Types/SearchResponse.php index 5b45aeb820ad..1976b4f0b0ad 100644 --- a/seed/php-sdk/query-parameters-openapi/src/Types/SearchResponse.php +++ b/seed/php-sdk/query-parameters-openapi/src/Types/SearchResponse.php @@ -21,15 +21,15 @@ class SearchResponse extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->results = $values['results'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/query-parameters-openapi/src/Types/User.php b/seed/php-sdk/query-parameters-openapi/src/Types/User.php index d2073133a61b..b91cee63a45e 100644 --- a/seed/php-sdk/query-parameters-openapi/src/Types/User.php +++ b/seed/php-sdk/query-parameters-openapi/src/Types/User.php @@ -28,15 +28,16 @@ class User extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->name = $values['name'] ?? null;$this->tags = $values['tags'] ?? null; + ) { + $this->name = $values['name'] ?? null; + $this->tags = $values['tags'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/query-parameters-openapi/src/Utils/File.php b/seed/php-sdk/query-parameters-openapi/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/query-parameters-openapi/src/Utils/File.php +++ b/seed/php-sdk/query-parameters-openapi/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/query-parameters-openapi/tests/Core/Client/RawClientTest.php b/seed/php-sdk/query-parameters-openapi/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/query-parameters-openapi/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/query-parameters-openapi/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/query-parameters-openapi/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/query-parameters-openapi/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/query-parameters-openapi/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/query-parameters-openapi/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/query-parameters-openapi/tests/Core/Json/EnumTest.php b/seed/php-sdk/query-parameters-openapi/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/query-parameters-openapi/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/query-parameters-openapi/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/query-parameters-openapi/tests/Core/Json/TraitTest.php b/seed/php-sdk/query-parameters-openapi/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/query-parameters-openapi/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/query-parameters-openapi/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/query-parameters-openapi/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/query-parameters-openapi/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/query-parameters-openapi/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/query-parameters-openapi/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/query-parameters/no-custom-config/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/query-parameters/no-custom-config/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/query-parameters/no-custom-config/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/query-parameters/no-custom-config/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/query-parameters/no-custom-config/src/Core/Client/RawClient.php b/seed/php-sdk/query-parameters/no-custom-config/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/query-parameters/no-custom-config/src/Core/Client/RawClient.php +++ b/seed/php-sdk/query-parameters/no-custom-config/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/query-parameters/no-custom-config/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/query-parameters/no-custom-config/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/query-parameters/no-custom-config/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/query-parameters/no-custom-config/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/query-parameters/no-custom-config/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/query-parameters/no-custom-config/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/query-parameters/no-custom-config/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/query-parameters/no-custom-config/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/query-parameters/no-custom-config/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/query-parameters/no-custom-config/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/query-parameters/no-custom-config/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/query-parameters/no-custom-config/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/query-parameters/no-custom-config/src/Core/Types/ArrayType.php b/seed/php-sdk/query-parameters/no-custom-config/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/query-parameters/no-custom-config/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/query-parameters/no-custom-config/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/query-parameters/no-custom-config/src/Core/Types/Constant.php b/seed/php-sdk/query-parameters/no-custom-config/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/query-parameters/no-custom-config/src/Core/Types/Constant.php +++ b/seed/php-sdk/query-parameters/no-custom-config/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/query-parameters/no-custom-config/src/Core/Types/Union.php b/seed/php-sdk/query-parameters/no-custom-config/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/query-parameters/no-custom-config/src/Core/Types/Union.php +++ b/seed/php-sdk/query-parameters/no-custom-config/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/query-parameters/no-custom-config/src/Exceptions/SeedApiException.php b/seed/php-sdk/query-parameters/no-custom-config/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/query-parameters/no-custom-config/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/query-parameters/no-custom-config/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/query-parameters/no-custom-config/src/Exceptions/SeedException.php b/seed/php-sdk/query-parameters/no-custom-config/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/query-parameters/no-custom-config/src/Exceptions/SeedException.php +++ b/seed/php-sdk/query-parameters/no-custom-config/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/query-parameters/no-custom-config/src/SeedClient.php b/seed/php-sdk/query-parameters/no-custom-config/src/SeedClient.php index a13bf5acc053..ee87d1cb303f 100644 --- a/seed/php-sdk/query-parameters/no-custom-config/src/SeedClient.php +++ b/seed/php-sdk/query-parameters/no-custom-config/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var UserClient $user @@ -40,26 +40,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->user = new UserClient($this->client, $this->options); } } diff --git a/seed/php-sdk/query-parameters/no-custom-config/src/User/Requests/GetUsersRequest.php b/seed/php-sdk/query-parameters/no-custom-config/src/User/Requests/GetUsersRequest.php index 6d9760db2e36..16a13f200586 100644 --- a/seed/php-sdk/query-parameters/no-custom-config/src/User/Requests/GetUsersRequest.php +++ b/seed/php-sdk/query-parameters/no-custom-config/src/User/Requests/GetUsersRequest.php @@ -99,8 +99,20 @@ class GetUsersRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->limit = $values['limit'];$this->id = $values['id'];$this->date = $values['date'];$this->deadline = $values['deadline'];$this->bytes = $values['bytes'];$this->user = $values['user'];$this->userList = $values['userList'];$this->optionalDeadline = $values['optionalDeadline'] ?? null;$this->keyValue = $values['keyValue'];$this->optionalString = $values['optionalString'] ?? null;$this->nestedUser = $values['nestedUser'];$this->optionalUser = $values['optionalUser'] ?? null;$this->excludeUser = $values['excludeUser'];$this->filter = $values['filter']; + ) { + $this->limit = $values['limit']; + $this->id = $values['id']; + $this->date = $values['date']; + $this->deadline = $values['deadline']; + $this->bytes = $values['bytes']; + $this->user = $values['user']; + $this->userList = $values['userList']; + $this->optionalDeadline = $values['optionalDeadline'] ?? null; + $this->keyValue = $values['keyValue']; + $this->optionalString = $values['optionalString'] ?? null; + $this->nestedUser = $values['nestedUser']; + $this->optionalUser = $values['optionalUser'] ?? null; + $this->excludeUser = $values['excludeUser']; + $this->filter = $values['filter']; } } diff --git a/seed/php-sdk/query-parameters/no-custom-config/src/User/Types/NestedUser.php b/seed/php-sdk/query-parameters/no-custom-config/src/User/Types/NestedUser.php index 812bfc1d35c6..a4a728f20ee0 100644 --- a/seed/php-sdk/query-parameters/no-custom-config/src/User/Types/NestedUser.php +++ b/seed/php-sdk/query-parameters/no-custom-config/src/User/Types/NestedUser.php @@ -27,15 +27,16 @@ class NestedUser extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->user = $values['user']; + ) { + $this->name = $values['name']; + $this->user = $values['user']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/query-parameters/no-custom-config/src/User/Types/User.php b/seed/php-sdk/query-parameters/no-custom-config/src/User/Types/User.php index 0dc2f6896280..c2d2049a60a4 100644 --- a/seed/php-sdk/query-parameters/no-custom-config/src/User/Types/User.php +++ b/seed/php-sdk/query-parameters/no-custom-config/src/User/Types/User.php @@ -28,15 +28,16 @@ class User extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->tags = $values['tags']; + ) { + $this->name = $values['name']; + $this->tags = $values['tags']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/query-parameters/no-custom-config/src/User/UserClient.php b/seed/php-sdk/query-parameters/no-custom-config/src/User/UserClient.php index f26c92a3c0d3..719a0d088ce9 100644 --- a/seed/php-sdk/query-parameters/no-custom-config/src/User/UserClient.php +++ b/seed/php-sdk/query-parameters/no-custom-config/src/User/UserClient.php @@ -15,7 +15,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class UserClient +class UserClient { /** * @var array{ @@ -43,11 +43,10 @@ class UserClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -66,7 +65,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getUsername(GetUsersRequest $request, ?array $options = null): User { + public function getUsername(GetUsersRequest $request, ?array $options = null): User + { $options = array_merge($this->options, $options ?? []); $query = []; $query['limit'] = $request->limit; @@ -80,13 +80,13 @@ public function getUsername(GetUsersRequest $request, ?array $options = null): U $query['nestedUser'] = $request->nestedUser; $query['excludeUser'] = $request->excludeUser; $query['filter'] = $request->filter; - if ($request->optionalDeadline != null){ + if ($request->optionalDeadline != null) { $query['optionalDeadline'] = JsonSerializer::serializeDateTime($request->optionalDeadline); } - if ($request->optionalString != null){ + if ($request->optionalString != null) { $query['optionalString'] = $request->optionalString; } - if ($request->optionalUser != null){ + if ($request->optionalUser != null) { $query['optionalUser'] = $request->optionalUser; } try { @@ -100,15 +100,15 @@ public function getUsername(GetUsersRequest $request, ?array $options = null): U $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return User::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/query-parameters/no-custom-config/src/Utils/File.php b/seed/php-sdk/query-parameters/no-custom-config/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/query-parameters/no-custom-config/src/Utils/File.php +++ b/seed/php-sdk/query-parameters/no-custom-config/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/query-parameters/no-custom-config/tests/Core/Client/RawClientTest.php b/seed/php-sdk/query-parameters/no-custom-config/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/query-parameters/no-custom-config/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/query-parameters/no-custom-config/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/query-parameters/no-custom-config/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/query-parameters/no-custom-config/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/query-parameters/no-custom-config/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/query-parameters/no-custom-config/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/query-parameters/no-custom-config/tests/Core/Json/EnumTest.php b/seed/php-sdk/query-parameters/no-custom-config/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/query-parameters/no-custom-config/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/query-parameters/no-custom-config/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/query-parameters/no-custom-config/tests/Core/Json/TraitTest.php b/seed/php-sdk/query-parameters/no-custom-config/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/query-parameters/no-custom-config/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/query-parameters/no-custom-config/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/query-parameters/no-custom-config/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/query-parameters/no-custom-config/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/query-parameters/no-custom-config/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/query-parameters/no-custom-config/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/query-parameters/private/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/query-parameters/private/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/query-parameters/private/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/query-parameters/private/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/query-parameters/private/src/Core/Client/RawClient.php b/seed/php-sdk/query-parameters/private/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/query-parameters/private/src/Core/Client/RawClient.php +++ b/seed/php-sdk/query-parameters/private/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/query-parameters/private/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/query-parameters/private/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/query-parameters/private/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/query-parameters/private/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/query-parameters/private/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/query-parameters/private/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/query-parameters/private/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/query-parameters/private/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/query-parameters/private/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/query-parameters/private/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/query-parameters/private/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/query-parameters/private/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/query-parameters/private/src/Core/Types/ArrayType.php b/seed/php-sdk/query-parameters/private/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/query-parameters/private/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/query-parameters/private/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/query-parameters/private/src/Core/Types/Constant.php b/seed/php-sdk/query-parameters/private/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/query-parameters/private/src/Core/Types/Constant.php +++ b/seed/php-sdk/query-parameters/private/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/query-parameters/private/src/Core/Types/Union.php b/seed/php-sdk/query-parameters/private/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/query-parameters/private/src/Core/Types/Union.php +++ b/seed/php-sdk/query-parameters/private/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/query-parameters/private/src/Exceptions/SeedApiException.php b/seed/php-sdk/query-parameters/private/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/query-parameters/private/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/query-parameters/private/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/query-parameters/private/src/Exceptions/SeedException.php b/seed/php-sdk/query-parameters/private/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/query-parameters/private/src/Exceptions/SeedException.php +++ b/seed/php-sdk/query-parameters/private/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/query-parameters/private/src/SeedClient.php b/seed/php-sdk/query-parameters/private/src/SeedClient.php index a13bf5acc053..ee87d1cb303f 100644 --- a/seed/php-sdk/query-parameters/private/src/SeedClient.php +++ b/seed/php-sdk/query-parameters/private/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var UserClient $user @@ -40,26 +40,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->user = new UserClient($this->client, $this->options); } } diff --git a/seed/php-sdk/query-parameters/private/src/User/Requests/GetUsersRequest.php b/seed/php-sdk/query-parameters/private/src/User/Requests/GetUsersRequest.php index 1e67e8fa90c7..50309c00b77a 100644 --- a/seed/php-sdk/query-parameters/private/src/User/Requests/GetUsersRequest.php +++ b/seed/php-sdk/query-parameters/private/src/User/Requests/GetUsersRequest.php @@ -99,176 +99,258 @@ class GetUsersRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->limit = $values['limit'];$this->id = $values['id'];$this->date = $values['date'];$this->deadline = $values['deadline'];$this->bytes = $values['bytes'];$this->user = $values['user'];$this->userList = $values['userList'];$this->optionalDeadline = $values['optionalDeadline'] ?? null;$this->keyValue = $values['keyValue'];$this->optionalString = $values['optionalString'] ?? null;$this->nestedUser = $values['nestedUser'];$this->optionalUser = $values['optionalUser'] ?? null;$this->excludeUser = $values['excludeUser'];$this->filter = $values['filter']; + ) { + $this->limit = $values['limit']; + $this->id = $values['id']; + $this->date = $values['date']; + $this->deadline = $values['deadline']; + $this->bytes = $values['bytes']; + $this->user = $values['user']; + $this->userList = $values['userList']; + $this->optionalDeadline = $values['optionalDeadline'] ?? null; + $this->keyValue = $values['keyValue']; + $this->optionalString = $values['optionalString'] ?? null; + $this->nestedUser = $values['nestedUser']; + $this->optionalUser = $values['optionalUser'] ?? null; + $this->excludeUser = $values['excludeUser']; + $this->filter = $values['filter']; } /** * @return int */ - public function getLimit(): int { - return $this->limit;} + public function getLimit(): int + { + return $this->limit; + } /** * @param int $value */ - public function setLimit(int $value): self { - $this->limit = $value;return $this;} + public function setLimit(int $value): self + { + $this->limit = $value; + return $this; + } /** * @return string */ - public function getId(): string { - return $this->id;} + public function getId(): string + { + return $this->id; + } /** * @param string $value */ - public function setId(string $value): self { - $this->id = $value;return $this;} + public function setId(string $value): self + { + $this->id = $value; + return $this; + } /** * @return DateTime */ - public function getDate(): DateTime { - return $this->date;} + public function getDate(): DateTime + { + return $this->date; + } /** * @param DateTime $value */ - public function setDate(DateTime $value): self { - $this->date = $value;return $this;} + public function setDate(DateTime $value): self + { + $this->date = $value; + return $this; + } /** * @return DateTime */ - public function getDeadline(): DateTime { - return $this->deadline;} + public function getDeadline(): DateTime + { + return $this->deadline; + } /** * @param DateTime $value */ - public function setDeadline(DateTime $value): self { - $this->deadline = $value;return $this;} + public function setDeadline(DateTime $value): self + { + $this->deadline = $value; + return $this; + } /** * @return string */ - public function getBytes(): string { - return $this->bytes;} + public function getBytes(): string + { + return $this->bytes; + } /** * @param string $value */ - public function setBytes(string $value): self { - $this->bytes = $value;return $this;} + public function setBytes(string $value): self + { + $this->bytes = $value; + return $this; + } /** * @return User */ - public function getUser(): User { - return $this->user;} + public function getUser(): User + { + return $this->user; + } /** * @param User $value */ - public function setUser(User $value): self { - $this->user = $value;return $this;} + public function setUser(User $value): self + { + $this->user = $value; + return $this; + } /** * @return array */ - public function getUserList(): array { - return $this->userList;} + public function getUserList(): array + { + return $this->userList; + } /** * @param array $value */ - public function setUserList(array $value): self { - $this->userList = $value;return $this;} + public function setUserList(array $value): self + { + $this->userList = $value; + return $this; + } /** * @return ?DateTime */ - public function getOptionalDeadline(): ?DateTime { - return $this->optionalDeadline;} + public function getOptionalDeadline(): ?DateTime + { + return $this->optionalDeadline; + } /** * @param ?DateTime $value */ - public function setOptionalDeadline(?DateTime $value = null): self { - $this->optionalDeadline = $value;return $this;} + public function setOptionalDeadline(?DateTime $value = null): self + { + $this->optionalDeadline = $value; + return $this; + } /** * @return array */ - public function getKeyValue(): array { - return $this->keyValue;} + public function getKeyValue(): array + { + return $this->keyValue; + } /** * @param array $value */ - public function setKeyValue(array $value): self { - $this->keyValue = $value;return $this;} + public function setKeyValue(array $value): self + { + $this->keyValue = $value; + return $this; + } /** * @return ?string */ - public function getOptionalString(): ?string { - return $this->optionalString;} + public function getOptionalString(): ?string + { + return $this->optionalString; + } /** * @param ?string $value */ - public function setOptionalString(?string $value = null): self { - $this->optionalString = $value;return $this;} + public function setOptionalString(?string $value = null): self + { + $this->optionalString = $value; + return $this; + } /** * @return NestedUser */ - public function getNestedUser(): NestedUser { - return $this->nestedUser;} + public function getNestedUser(): NestedUser + { + return $this->nestedUser; + } /** * @param NestedUser $value */ - public function setNestedUser(NestedUser $value): self { - $this->nestedUser = $value;return $this;} + public function setNestedUser(NestedUser $value): self + { + $this->nestedUser = $value; + return $this; + } /** * @return ?User */ - public function getOptionalUser(): ?User { - return $this->optionalUser;} + public function getOptionalUser(): ?User + { + return $this->optionalUser; + } /** * @param ?User $value */ - public function setOptionalUser(?User $value = null): self { - $this->optionalUser = $value;return $this;} + public function setOptionalUser(?User $value = null): self + { + $this->optionalUser = $value; + return $this; + } /** * @return array */ - public function getExcludeUser(): array { - return $this->excludeUser;} + public function getExcludeUser(): array + { + return $this->excludeUser; + } /** * @param array $value */ - public function setExcludeUser(array $value): self { - $this->excludeUser = $value;return $this;} + public function setExcludeUser(array $value): self + { + $this->excludeUser = $value; + return $this; + } /** * @return array */ - public function getFilter(): array { - return $this->filter;} + public function getFilter(): array + { + return $this->filter; + } /** * @param array $value */ - public function setFilter(array $value): self { - $this->filter = $value;return $this;} + public function setFilter(array $value): self + { + $this->filter = $value; + return $this; + } } diff --git a/seed/php-sdk/query-parameters/private/src/User/Types/NestedUser.php b/seed/php-sdk/query-parameters/private/src/User/Types/NestedUser.php index 87437034c6bb..1e3b2cd84eb0 100644 --- a/seed/php-sdk/query-parameters/private/src/User/Types/NestedUser.php +++ b/seed/php-sdk/query-parameters/private/src/User/Types/NestedUser.php @@ -27,39 +27,50 @@ class NestedUser extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->user = $values['user']; + ) { + $this->name = $values['name']; + $this->user = $values['user']; } /** * @return string */ - public function getName(): string { - return $this->name;} + public function getName(): string + { + return $this->name; + } /** * @param string $value */ - public function setName(string $value): self { - $this->name = $value;return $this;} + public function setName(string $value): self + { + $this->name = $value; + return $this; + } /** * @return User */ - public function getUser(): User { - return $this->user;} + public function getUser(): User + { + return $this->user; + } /** * @param User $value */ - public function setUser(User $value): self { - $this->user = $value;return $this;} + public function setUser(User $value): self + { + $this->user = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/query-parameters/private/src/User/Types/User.php b/seed/php-sdk/query-parameters/private/src/User/Types/User.php index f20da4e5cae6..3692ead60318 100644 --- a/seed/php-sdk/query-parameters/private/src/User/Types/User.php +++ b/seed/php-sdk/query-parameters/private/src/User/Types/User.php @@ -28,39 +28,50 @@ class User extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->tags = $values['tags']; + ) { + $this->name = $values['name']; + $this->tags = $values['tags']; } /** * @return string */ - public function getName(): string { - return $this->name;} + public function getName(): string + { + return $this->name; + } /** * @param string $value */ - public function setName(string $value): self { - $this->name = $value;return $this;} + public function setName(string $value): self + { + $this->name = $value; + return $this; + } /** * @return array */ - public function getTags(): array { - return $this->tags;} + public function getTags(): array + { + return $this->tags; + } /** * @param array $value */ - public function setTags(array $value): self { - $this->tags = $value;return $this;} + public function setTags(array $value): self + { + $this->tags = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/query-parameters/private/src/User/UserClient.php b/seed/php-sdk/query-parameters/private/src/User/UserClient.php index 915ba72bb52d..ec26bf49510e 100644 --- a/seed/php-sdk/query-parameters/private/src/User/UserClient.php +++ b/seed/php-sdk/query-parameters/private/src/User/UserClient.php @@ -15,7 +15,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class UserClient +class UserClient { /** * @var array{ @@ -43,11 +43,10 @@ class UserClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -66,7 +65,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getUsername(GetUsersRequest $request, ?array $options = null): User { + public function getUsername(GetUsersRequest $request, ?array $options = null): User + { $options = array_merge($this->options, $options ?? []); $query = []; $query['limit'] = $request->getLimit(); @@ -80,13 +80,13 @@ public function getUsername(GetUsersRequest $request, ?array $options = null): U $query['nestedUser'] = $request->getNestedUser(); $query['excludeUser'] = $request->getExcludeUser(); $query['filter'] = $request->getFilter(); - if ($request->getOptionalDeadline() != null){ + if ($request->getOptionalDeadline() != null) { $query['optionalDeadline'] = JsonSerializer::serializeDateTime($request->getOptionalDeadline()); } - if ($request->getOptionalString() != null){ + if ($request->getOptionalString() != null) { $query['optionalString'] = $request->getOptionalString(); } - if ($request->getOptionalUser() != null){ + if ($request->getOptionalUser() != null) { $query['optionalUser'] = $request->getOptionalUser(); } try { @@ -100,15 +100,15 @@ public function getUsername(GetUsersRequest $request, ?array $options = null): U $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return User::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/query-parameters/private/src/Utils/File.php b/seed/php-sdk/query-parameters/private/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/query-parameters/private/src/Utils/File.php +++ b/seed/php-sdk/query-parameters/private/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/query-parameters/private/tests/Core/Client/RawClientTest.php b/seed/php-sdk/query-parameters/private/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/query-parameters/private/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/query-parameters/private/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/query-parameters/private/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/query-parameters/private/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/query-parameters/private/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/query-parameters/private/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/query-parameters/private/tests/Core/Json/EnumTest.php b/seed/php-sdk/query-parameters/private/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/query-parameters/private/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/query-parameters/private/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/query-parameters/private/tests/Core/Json/TraitTest.php b/seed/php-sdk/query-parameters/private/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/query-parameters/private/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/query-parameters/private/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/query-parameters/private/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/query-parameters/private/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/query-parameters/private/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/query-parameters/private/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/request-parameters/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/request-parameters/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/request-parameters/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/request-parameters/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/request-parameters/src/Core/Client/RawClient.php b/seed/php-sdk/request-parameters/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/request-parameters/src/Core/Client/RawClient.php +++ b/seed/php-sdk/request-parameters/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/request-parameters/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/request-parameters/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/request-parameters/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/request-parameters/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/request-parameters/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/request-parameters/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/request-parameters/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/request-parameters/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/request-parameters/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/request-parameters/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/request-parameters/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/request-parameters/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/request-parameters/src/Core/Types/ArrayType.php b/seed/php-sdk/request-parameters/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/request-parameters/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/request-parameters/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/request-parameters/src/Core/Types/Constant.php b/seed/php-sdk/request-parameters/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/request-parameters/src/Core/Types/Constant.php +++ b/seed/php-sdk/request-parameters/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/request-parameters/src/Core/Types/Union.php b/seed/php-sdk/request-parameters/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/request-parameters/src/Core/Types/Union.php +++ b/seed/php-sdk/request-parameters/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/request-parameters/src/Exceptions/SeedApiException.php b/seed/php-sdk/request-parameters/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/request-parameters/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/request-parameters/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/request-parameters/src/Exceptions/SeedException.php b/seed/php-sdk/request-parameters/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/request-parameters/src/Exceptions/SeedException.php +++ b/seed/php-sdk/request-parameters/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/request-parameters/src/SeedClient.php b/seed/php-sdk/request-parameters/src/SeedClient.php index a13bf5acc053..ee87d1cb303f 100644 --- a/seed/php-sdk/request-parameters/src/SeedClient.php +++ b/seed/php-sdk/request-parameters/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var UserClient $user @@ -40,26 +40,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->user = new UserClient($this->client, $this->options); } } diff --git a/seed/php-sdk/request-parameters/src/User/Requests/CreateUsernameReferencedRequest.php b/seed/php-sdk/request-parameters/src/User/Requests/CreateUsernameReferencedRequest.php index e0ed9d49afea..e8d098d1bf74 100644 --- a/seed/php-sdk/request-parameters/src/User/Requests/CreateUsernameReferencedRequest.php +++ b/seed/php-sdk/request-parameters/src/User/Requests/CreateUsernameReferencedRequest.php @@ -25,8 +25,8 @@ class CreateUsernameReferencedRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->tags = $values['tags'];$this->body = $values['body']; + ) { + $this->tags = $values['tags']; + $this->body = $values['body']; } } diff --git a/seed/php-sdk/request-parameters/src/User/Requests/CreateUsernameRequest.php b/seed/php-sdk/request-parameters/src/User/Requests/CreateUsernameRequest.php index 32d73087e041..902151d528ef 100644 --- a/seed/php-sdk/request-parameters/src/User/Requests/CreateUsernameRequest.php +++ b/seed/php-sdk/request-parameters/src/User/Requests/CreateUsernameRequest.php @@ -40,8 +40,10 @@ class CreateUsernameRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->tags = $values['tags'];$this->username = $values['username'];$this->password = $values['password'];$this->name = $values['name']; + ) { + $this->tags = $values['tags']; + $this->username = $values['username']; + $this->password = $values['password']; + $this->name = $values['name']; } } diff --git a/seed/php-sdk/request-parameters/src/User/Requests/GetUsersRequest.php b/seed/php-sdk/request-parameters/src/User/Requests/GetUsersRequest.php index fd31713fb07b..2c043269ba47 100644 --- a/seed/php-sdk/request-parameters/src/User/Requests/GetUsersRequest.php +++ b/seed/php-sdk/request-parameters/src/User/Requests/GetUsersRequest.php @@ -111,8 +111,22 @@ class GetUsersRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->limit = $values['limit'];$this->id = $values['id'];$this->date = $values['date'];$this->deadline = $values['deadline'];$this->bytes = $values['bytes'];$this->user = $values['user'];$this->userList = $values['userList'];$this->optionalDeadline = $values['optionalDeadline'] ?? null;$this->keyValue = $values['keyValue'];$this->optionalString = $values['optionalString'] ?? null;$this->nestedUser = $values['nestedUser'];$this->optionalUser = $values['optionalUser'] ?? null;$this->excludeUser = $values['excludeUser'];$this->filter = $values['filter'];$this->longParam = $values['longParam'];$this->bigIntParam = $values['bigIntParam']; + ) { + $this->limit = $values['limit']; + $this->id = $values['id']; + $this->date = $values['date']; + $this->deadline = $values['deadline']; + $this->bytes = $values['bytes']; + $this->user = $values['user']; + $this->userList = $values['userList']; + $this->optionalDeadline = $values['optionalDeadline'] ?? null; + $this->keyValue = $values['keyValue']; + $this->optionalString = $values['optionalString'] ?? null; + $this->nestedUser = $values['nestedUser']; + $this->optionalUser = $values['optionalUser'] ?? null; + $this->excludeUser = $values['excludeUser']; + $this->filter = $values['filter']; + $this->longParam = $values['longParam']; + $this->bigIntParam = $values['bigIntParam']; } } diff --git a/seed/php-sdk/request-parameters/src/User/Types/CreateUsernameBody.php b/seed/php-sdk/request-parameters/src/User/Types/CreateUsernameBody.php index d6b412b6787d..670dbc5bd91b 100644 --- a/seed/php-sdk/request-parameters/src/User/Types/CreateUsernameBody.php +++ b/seed/php-sdk/request-parameters/src/User/Types/CreateUsernameBody.php @@ -34,15 +34,17 @@ class CreateUsernameBody extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->username = $values['username'];$this->password = $values['password'];$this->name = $values['name']; + ) { + $this->username = $values['username']; + $this->password = $values['password']; + $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/request-parameters/src/User/Types/CreateUsernameBodyOptionalProperties.php b/seed/php-sdk/request-parameters/src/User/Types/CreateUsernameBodyOptionalProperties.php index 3a66c8969bf0..001a06ff5086 100644 --- a/seed/php-sdk/request-parameters/src/User/Types/CreateUsernameBodyOptionalProperties.php +++ b/seed/php-sdk/request-parameters/src/User/Types/CreateUsernameBodyOptionalProperties.php @@ -34,15 +34,17 @@ class CreateUsernameBodyOptionalProperties extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->username = $values['username'] ?? null;$this->password = $values['password'] ?? null;$this->name = $values['name'] ?? null; + ) { + $this->username = $values['username'] ?? null; + $this->password = $values['password'] ?? null; + $this->name = $values['name'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/request-parameters/src/User/Types/NestedUser.php b/seed/php-sdk/request-parameters/src/User/Types/NestedUser.php index 812bfc1d35c6..a4a728f20ee0 100644 --- a/seed/php-sdk/request-parameters/src/User/Types/NestedUser.php +++ b/seed/php-sdk/request-parameters/src/User/Types/NestedUser.php @@ -27,15 +27,16 @@ class NestedUser extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->user = $values['user']; + ) { + $this->name = $values['name']; + $this->user = $values['user']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/request-parameters/src/User/Types/User.php b/seed/php-sdk/request-parameters/src/User/Types/User.php index 0dc2f6896280..c2d2049a60a4 100644 --- a/seed/php-sdk/request-parameters/src/User/Types/User.php +++ b/seed/php-sdk/request-parameters/src/User/Types/User.php @@ -28,15 +28,16 @@ class User extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->tags = $values['tags']; + ) { + $this->name = $values['name']; + $this->tags = $values['tags']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/request-parameters/src/User/UserClient.php b/seed/php-sdk/request-parameters/src/User/UserClient.php index b197622078a8..555ed3b1e814 100644 --- a/seed/php-sdk/request-parameters/src/User/UserClient.php +++ b/seed/php-sdk/request-parameters/src/User/UserClient.php @@ -18,7 +18,7 @@ use Seed\Core\Json\JsonSerializer; use JsonException; -class UserClient +class UserClient { /** * @var array{ @@ -46,11 +46,10 @@ class UserClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -68,7 +67,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function createUsername(CreateUsernameRequest $request, ?array $options = null): void { + public function createUsername(CreateUsernameRequest $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); $query = []; $query['tags'] = $request->tags; @@ -84,12 +84,12 @@ public function createUsername(CreateUsernameRequest $request, ?array $options = $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -120,7 +120,8 @@ public function createUsername(CreateUsernameRequest $request, ?array $options = * @throws SeedException * @throws SeedApiException */ - public function createUsernameWithReferencedType(CreateUsernameReferencedRequest $request, ?array $options = null): void { + public function createUsernameWithReferencedType(CreateUsernameReferencedRequest $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); $query = []; $query['tags'] = $request->tags; @@ -136,12 +137,12 @@ public function createUsernameWithReferencedType(CreateUsernameReferencedRequest $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -172,7 +173,8 @@ public function createUsernameWithReferencedType(CreateUsernameReferencedRequest * @throws SeedException * @throws SeedApiException */ - public function createUsernameOptional(?CreateUsernameBodyOptionalProperties $request = null, ?array $options = null): void { + public function createUsernameOptional(?CreateUsernameBodyOptionalProperties $request = null, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -185,12 +187,12 @@ public function createUsernameOptional(?CreateUsernameBodyOptionalProperties $re $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -222,7 +224,8 @@ public function createUsernameOptional(?CreateUsernameBodyOptionalProperties $re * @throws SeedException * @throws SeedApiException */ - public function getUsername(GetUsersRequest $request, ?array $options = null): User { + public function getUsername(GetUsersRequest $request, ?array $options = null): User + { $options = array_merge($this->options, $options ?? []); $query = []; $query['limit'] = $request->limit; @@ -238,13 +241,13 @@ public function getUsername(GetUsersRequest $request, ?array $options = null): U $query['filter'] = $request->filter; $query['longParam'] = $request->longParam; $query['bigIntParam'] = $request->bigIntParam; - if ($request->optionalDeadline != null){ + if ($request->optionalDeadline != null) { $query['optionalDeadline'] = JsonSerializer::serializeDateTime($request->optionalDeadline); } - if ($request->optionalString != null){ + if ($request->optionalString != null) { $query['optionalString'] = $request->optionalString; } - if ($request->optionalUser != null){ + if ($request->optionalUser != null) { $query['optionalUser'] = $request->optionalUser; } try { @@ -258,15 +261,15 @@ public function getUsername(GetUsersRequest $request, ?array $options = null): U $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return User::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/request-parameters/src/Utils/File.php b/seed/php-sdk/request-parameters/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/request-parameters/src/Utils/File.php +++ b/seed/php-sdk/request-parameters/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/request-parameters/tests/Core/Client/RawClientTest.php b/seed/php-sdk/request-parameters/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/request-parameters/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/request-parameters/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/request-parameters/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/request-parameters/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/request-parameters/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/request-parameters/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/request-parameters/tests/Core/Json/EnumTest.php b/seed/php-sdk/request-parameters/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/request-parameters/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/request-parameters/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/request-parameters/tests/Core/Json/TraitTest.php b/seed/php-sdk/request-parameters/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/request-parameters/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/request-parameters/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/request-parameters/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/request-parameters/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/request-parameters/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/request-parameters/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/required-nullable/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/required-nullable/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/required-nullable/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/required-nullable/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/required-nullable/src/Core/Client/RawClient.php b/seed/php-sdk/required-nullable/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/required-nullable/src/Core/Client/RawClient.php +++ b/seed/php-sdk/required-nullable/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/required-nullable/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/required-nullable/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/required-nullable/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/required-nullable/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/required-nullable/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/required-nullable/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/required-nullable/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/required-nullable/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/required-nullable/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/required-nullable/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/required-nullable/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/required-nullable/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/required-nullable/src/Core/Types/ArrayType.php b/seed/php-sdk/required-nullable/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/required-nullable/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/required-nullable/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/required-nullable/src/Core/Types/Constant.php b/seed/php-sdk/required-nullable/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/required-nullable/src/Core/Types/Constant.php +++ b/seed/php-sdk/required-nullable/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/required-nullable/src/Core/Types/Union.php b/seed/php-sdk/required-nullable/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/required-nullable/src/Core/Types/Union.php +++ b/seed/php-sdk/required-nullable/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/required-nullable/src/Exceptions/SeedApiException.php b/seed/php-sdk/required-nullable/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/required-nullable/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/required-nullable/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/required-nullable/src/Exceptions/SeedException.php b/seed/php-sdk/required-nullable/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/required-nullable/src/Exceptions/SeedException.php +++ b/seed/php-sdk/required-nullable/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/required-nullable/src/Requests/GetFooRequest.php b/seed/php-sdk/required-nullable/src/Requests/GetFooRequest.php index 785f3fbc9ecf..36e99b94c691 100644 --- a/seed/php-sdk/required-nullable/src/Requests/GetFooRequest.php +++ b/seed/php-sdk/required-nullable/src/Requests/GetFooRequest.php @@ -36,8 +36,10 @@ class GetFooRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->optionalBaz = $values['optionalBaz'] ?? null;$this->optionalNullableBaz = $values['optionalNullableBaz'] ?? null;$this->requiredBaz = $values['requiredBaz'];$this->requiredNullableBaz = $values['requiredNullableBaz'] ?? null; + ) { + $this->optionalBaz = $values['optionalBaz'] ?? null; + $this->optionalNullableBaz = $values['optionalNullableBaz'] ?? null; + $this->requiredBaz = $values['requiredBaz']; + $this->requiredNullableBaz = $values['requiredNullableBaz'] ?? null; } } diff --git a/seed/php-sdk/required-nullable/src/Requests/UpdateFooRequest.php b/seed/php-sdk/required-nullable/src/Requests/UpdateFooRequest.php index 39a7e56c4996..0699dc4e9f86 100644 --- a/seed/php-sdk/required-nullable/src/Requests/UpdateFooRequest.php +++ b/seed/php-sdk/required-nullable/src/Requests/UpdateFooRequest.php @@ -40,8 +40,10 @@ class UpdateFooRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->xIdempotencyKey = $values['xIdempotencyKey'];$this->nullableText = $values['nullableText'] ?? null;$this->nullableNumber = $values['nullableNumber'] ?? null;$this->nonNullableText = $values['nonNullableText'] ?? null; + ) { + $this->xIdempotencyKey = $values['xIdempotencyKey']; + $this->nullableText = $values['nullableText'] ?? null; + $this->nullableNumber = $values['nullableNumber'] ?? null; + $this->nonNullableText = $values['nonNullableText'] ?? null; } } diff --git a/seed/php-sdk/required-nullable/src/SeedClient.php b/seed/php-sdk/required-nullable/src/SeedClient.php index fd3b6a868f86..5296d6fa7e25 100644 --- a/seed/php-sdk/required-nullable/src/SeedClient.php +++ b/seed/php-sdk/required-nullable/src/SeedClient.php @@ -15,7 +15,7 @@ use Psr\Http\Client\ClientExceptionInterface; use Seed\Requests\UpdateFooRequest; -class SeedClient +class SeedClient { /** * @var array{ @@ -44,22 +44,21 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); @@ -79,15 +78,16 @@ public function __construct( * @throws SeedException * @throws SeedApiException */ - public function getFoo(GetFooRequest $request, ?array $options = null): Foo { + public function getFoo(GetFooRequest $request, ?array $options = null): Foo + { $options = array_merge($this->options, $options ?? []); $query = []; $query['required_baz'] = $request->requiredBaz; $query['required_nullable_baz'] = $request->requiredNullableBaz; - if ($request->optionalBaz != null){ + if ($request->optionalBaz != null) { $query['optional_baz'] = $request->optionalBaz; } - if ($request->optionalNullableBaz != null){ + if ($request->optionalNullableBaz != null) { $query['optional_nullable_baz'] = $request->optionalNullableBaz; } try { @@ -101,15 +101,15 @@ public function getFoo(GetFooRequest $request, ?array $options = null): Foo { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return Foo::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -142,7 +142,8 @@ public function getFoo(GetFooRequest $request, ?array $options = null): Foo { * @throws SeedException * @throws SeedApiException */ - public function updateFoo(string $id, UpdateFooRequest $request, ?array $options = null): Foo { + public function updateFoo(string $id, UpdateFooRequest $request, ?array $options = null): Foo + { $options = array_merge($this->options, $options ?? []); $headers = []; $headers['X-Idempotency-Key'] = $request->xIdempotencyKey; @@ -158,15 +159,15 @@ public function updateFoo(string $id, UpdateFooRequest $request, ?array $options $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return Foo::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/required-nullable/src/Types/Foo.php b/seed/php-sdk/required-nullable/src/Types/Foo.php index db408319656e..ad02f8983419 100644 --- a/seed/php-sdk/required-nullable/src/Types/Foo.php +++ b/seed/php-sdk/required-nullable/src/Types/Foo.php @@ -41,15 +41,18 @@ class Foo extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->bar = $values['bar'] ?? null;$this->nullableBar = $values['nullableBar'] ?? null;$this->nullableRequiredBar = $values['nullableRequiredBar'] ?? null;$this->requiredBar = $values['requiredBar']; + ) { + $this->bar = $values['bar'] ?? null; + $this->nullableBar = $values['nullableBar'] ?? null; + $this->nullableRequiredBar = $values['nullableRequiredBar'] ?? null; + $this->requiredBar = $values['requiredBar']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/required-nullable/src/Utils/File.php b/seed/php-sdk/required-nullable/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/required-nullable/src/Utils/File.php +++ b/seed/php-sdk/required-nullable/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/required-nullable/tests/Core/Client/RawClientTest.php b/seed/php-sdk/required-nullable/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/required-nullable/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/required-nullable/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/required-nullable/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/required-nullable/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/required-nullable/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/required-nullable/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/required-nullable/tests/Core/Json/EnumTest.php b/seed/php-sdk/required-nullable/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/required-nullable/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/required-nullable/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/required-nullable/tests/Core/Json/TraitTest.php b/seed/php-sdk/required-nullable/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/required-nullable/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/required-nullable/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/required-nullable/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/required-nullable/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/required-nullable/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/required-nullable/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/reserved-keywords/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/reserved-keywords/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/reserved-keywords/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/reserved-keywords/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/reserved-keywords/src/Core/Client/RawClient.php b/seed/php-sdk/reserved-keywords/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/reserved-keywords/src/Core/Client/RawClient.php +++ b/seed/php-sdk/reserved-keywords/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/reserved-keywords/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/reserved-keywords/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/reserved-keywords/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/reserved-keywords/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/reserved-keywords/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/reserved-keywords/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/reserved-keywords/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/reserved-keywords/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/reserved-keywords/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/reserved-keywords/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/reserved-keywords/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/reserved-keywords/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/reserved-keywords/src/Core/Types/ArrayType.php b/seed/php-sdk/reserved-keywords/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/reserved-keywords/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/reserved-keywords/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/reserved-keywords/src/Core/Types/Constant.php b/seed/php-sdk/reserved-keywords/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/reserved-keywords/src/Core/Types/Constant.php +++ b/seed/php-sdk/reserved-keywords/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/reserved-keywords/src/Core/Types/Union.php b/seed/php-sdk/reserved-keywords/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/reserved-keywords/src/Core/Types/Union.php +++ b/seed/php-sdk/reserved-keywords/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/reserved-keywords/src/Exceptions/SeedApiException.php b/seed/php-sdk/reserved-keywords/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/reserved-keywords/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/reserved-keywords/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/reserved-keywords/src/Exceptions/SeedException.php b/seed/php-sdk/reserved-keywords/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/reserved-keywords/src/Exceptions/SeedException.php +++ b/seed/php-sdk/reserved-keywords/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/reserved-keywords/src/Package/PackageClient.php b/seed/php-sdk/reserved-keywords/src/Package/PackageClient.php index 7005d5b754bb..0557cd2df378 100644 --- a/seed/php-sdk/reserved-keywords/src/Package/PackageClient.php +++ b/seed/php-sdk/reserved-keywords/src/Package/PackageClient.php @@ -12,7 +12,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class PackageClient +class PackageClient { /** * @var array{ @@ -40,11 +40,10 @@ class PackageClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -62,7 +61,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function test(TestRequest $request, ?array $options = null): void { + public function test(TestRequest $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); $query = []; $query['for'] = $request->for; @@ -77,12 +77,12 @@ public function test(TestRequest $request, ?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/reserved-keywords/src/Package/Requests/TestRequest.php b/seed/php-sdk/reserved-keywords/src/Package/Requests/TestRequest.php index 5b45c8241572..8d037b88dd81 100644 --- a/seed/php-sdk/reserved-keywords/src/Package/Requests/TestRequest.php +++ b/seed/php-sdk/reserved-keywords/src/Package/Requests/TestRequest.php @@ -18,8 +18,7 @@ class TestRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->for = $values['for']; } } diff --git a/seed/php-sdk/reserved-keywords/src/Package/Types/Package.php b/seed/php-sdk/reserved-keywords/src/Package/Types/Package.php index 5bc60ab53cbb..99e4356f5b44 100644 --- a/seed/php-sdk/reserved-keywords/src/Package/Types/Package.php +++ b/seed/php-sdk/reserved-keywords/src/Package/Types/Package.php @@ -20,15 +20,15 @@ class Package extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/reserved-keywords/src/Package/Types/Record.php b/seed/php-sdk/reserved-keywords/src/Package/Types/Record.php index 8da3269b1636..46afe7010104 100644 --- a/seed/php-sdk/reserved-keywords/src/Package/Types/Record.php +++ b/seed/php-sdk/reserved-keywords/src/Package/Types/Record.php @@ -28,15 +28,16 @@ class Record extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->foo = $values['foo'];$this->_3D = $values['_3D']; + ) { + $this->foo = $values['foo']; + $this->_3D = $values['_3D']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/reserved-keywords/src/SeedClient.php b/seed/php-sdk/reserved-keywords/src/SeedClient.php index 74dd9c8b4fa1..b590d8a1aa51 100644 --- a/seed/php-sdk/reserved-keywords/src/SeedClient.php +++ b/seed/php-sdk/reserved-keywords/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var PackageClient $package @@ -40,26 +40,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->package = new PackageClient($this->client, $this->options); } } diff --git a/seed/php-sdk/reserved-keywords/src/Utils/File.php b/seed/php-sdk/reserved-keywords/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/reserved-keywords/src/Utils/File.php +++ b/seed/php-sdk/reserved-keywords/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/reserved-keywords/tests/Core/Client/RawClientTest.php b/seed/php-sdk/reserved-keywords/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/reserved-keywords/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/reserved-keywords/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/reserved-keywords/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/reserved-keywords/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/reserved-keywords/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/reserved-keywords/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/reserved-keywords/tests/Core/Json/EnumTest.php b/seed/php-sdk/reserved-keywords/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/reserved-keywords/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/reserved-keywords/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/reserved-keywords/tests/Core/Json/TraitTest.php b/seed/php-sdk/reserved-keywords/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/reserved-keywords/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/reserved-keywords/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/reserved-keywords/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/reserved-keywords/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/reserved-keywords/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/reserved-keywords/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/response-property/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/response-property/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/response-property/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/response-property/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/response-property/src/Core/Client/RawClient.php b/seed/php-sdk/response-property/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/response-property/src/Core/Client/RawClient.php +++ b/seed/php-sdk/response-property/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/response-property/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/response-property/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/response-property/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/response-property/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/response-property/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/response-property/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/response-property/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/response-property/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/response-property/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/response-property/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/response-property/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/response-property/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/response-property/src/Core/Types/ArrayType.php b/seed/php-sdk/response-property/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/response-property/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/response-property/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/response-property/src/Core/Types/Constant.php b/seed/php-sdk/response-property/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/response-property/src/Core/Types/Constant.php +++ b/seed/php-sdk/response-property/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/response-property/src/Core/Types/Union.php b/seed/php-sdk/response-property/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/response-property/src/Core/Types/Union.php +++ b/seed/php-sdk/response-property/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/response-property/src/Exceptions/SeedApiException.php b/seed/php-sdk/response-property/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/response-property/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/response-property/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/response-property/src/Exceptions/SeedException.php b/seed/php-sdk/response-property/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/response-property/src/Exceptions/SeedException.php +++ b/seed/php-sdk/response-property/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/response-property/src/SeedClient.php b/seed/php-sdk/response-property/src/SeedClient.php index 8d7adcb3b0f2..0fd6a9d1029e 100644 --- a/seed/php-sdk/response-property/src/SeedClient.php +++ b/seed/php-sdk/response-property/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var ServiceClient $service @@ -40,26 +40,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->service = new ServiceClient($this->client, $this->options); } } diff --git a/seed/php-sdk/response-property/src/Service/ServiceClient.php b/seed/php-sdk/response-property/src/Service/ServiceClient.php index f8515c50f171..335594196014 100644 --- a/seed/php-sdk/response-property/src/Service/ServiceClient.php +++ b/seed/php-sdk/response-property/src/Service/ServiceClient.php @@ -15,7 +15,7 @@ use Seed\Types\StringResponse; use Seed\Service\Types\WithDocs; -class ServiceClient +class ServiceClient { /** * @var array{ @@ -43,11 +43,10 @@ class ServiceClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -66,7 +65,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getMovie(string $request, ?array $options = null): Response { + public function getMovie(string $request, ?array $options = null): Response + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -79,15 +79,15 @@ public function getMovie(string $request, ?array $options = null): Response { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return Response::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -119,7 +119,8 @@ public function getMovie(string $request, ?array $options = null): Response { * @throws SeedException * @throws SeedApiException */ - public function getMovieDocs(string $request, ?array $options = null): Response { + public function getMovieDocs(string $request, ?array $options = null): Response + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -132,15 +133,15 @@ public function getMovieDocs(string $request, ?array $options = null): Response $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return Response::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -172,7 +173,8 @@ public function getMovieDocs(string $request, ?array $options = null): Response * @throws SeedException * @throws SeedApiException */ - public function getMovieName(string $request, ?array $options = null): StringResponse { + public function getMovieName(string $request, ?array $options = null): StringResponse + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -185,15 +187,15 @@ public function getMovieName(string $request, ?array $options = null): StringRes $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return StringResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -225,7 +227,8 @@ public function getMovieName(string $request, ?array $options = null): StringRes * @throws SeedException * @throws SeedApiException */ - public function getMovieMetadata(string $request, ?array $options = null): Response { + public function getMovieMetadata(string $request, ?array $options = null): Response + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -238,15 +241,15 @@ public function getMovieMetadata(string $request, ?array $options = null): Respo $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return Response::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -278,7 +281,8 @@ public function getMovieMetadata(string $request, ?array $options = null): Respo * @throws SeedException * @throws SeedApiException */ - public function getOptionalMovie(string $request, ?array $options = null): ?Response { + public function getOptionalMovie(string $request, ?array $options = null): ?Response + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -291,18 +295,18 @@ public function getOptionalMovie(string $request, ?array $options = null): ?Resp $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); - if (empty($json)){ + if (empty($json)) { return null; } return Response::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -334,7 +338,8 @@ public function getOptionalMovie(string $request, ?array $options = null): ?Resp * @throws SeedException * @throws SeedApiException */ - public function getOptionalMovieDocs(string $request, ?array $options = null): ?WithDocs { + public function getOptionalMovieDocs(string $request, ?array $options = null): ?WithDocs + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -347,18 +352,18 @@ public function getOptionalMovieDocs(string $request, ?array $options = null): ? $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); - if (empty($json)){ + if (empty($json)) { return null; } return WithDocs::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -390,7 +395,8 @@ public function getOptionalMovieDocs(string $request, ?array $options = null): ? * @throws SeedException * @throws SeedApiException */ - public function getOptionalMovieName(string $request, ?array $options = null): ?StringResponse { + public function getOptionalMovieName(string $request, ?array $options = null): ?StringResponse + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -403,18 +409,18 @@ public function getOptionalMovieName(string $request, ?array $options = null): ? $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); - if (empty($json)){ + if (empty($json)) { return null; } return StringResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/response-property/src/Service/Traits/WithDocs.php b/seed/php-sdk/response-property/src/Service/Traits/WithDocs.php index 55675e093259..2fcb1eed8168 100644 --- a/seed/php-sdk/response-property/src/Service/Traits/WithDocs.php +++ b/seed/php-sdk/response-property/src/Service/Traits/WithDocs.php @@ -7,7 +7,7 @@ /** * @property string $docs */ -trait WithDocs +trait WithDocs { /** * @var string $docs diff --git a/seed/php-sdk/response-property/src/Service/Types/Movie.php b/seed/php-sdk/response-property/src/Service/Types/Movie.php index efd53173afa8..a6525340023c 100644 --- a/seed/php-sdk/response-property/src/Service/Types/Movie.php +++ b/seed/php-sdk/response-property/src/Service/Types/Movie.php @@ -27,15 +27,16 @@ class Movie extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->name = $values['name']; + ) { + $this->id = $values['id']; + $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/response-property/src/Service/Types/Response.php b/seed/php-sdk/response-property/src/Service/Types/Response.php index a0dbb752b0d2..0d9d74b0933f 100644 --- a/seed/php-sdk/response-property/src/Service/Types/Response.php +++ b/seed/php-sdk/response-property/src/Service/Types/Response.php @@ -9,7 +9,8 @@ class Response extends JsonSerializableType { - use WithMetadata,WithDocs; + use WithMetadata; + use WithDocs; /** * @var Movie $data @@ -26,15 +27,17 @@ class Response extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->metadata = $values['metadata'];$this->docs = $values['docs'];$this->data = $values['data']; + ) { + $this->metadata = $values['metadata']; + $this->docs = $values['docs']; + $this->data = $values['data']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/response-property/src/Service/Types/WithDocs.php b/seed/php-sdk/response-property/src/Service/Types/WithDocs.php index 2e24b0902de3..0b9fcc85015b 100644 --- a/seed/php-sdk/response-property/src/Service/Types/WithDocs.php +++ b/seed/php-sdk/response-property/src/Service/Types/WithDocs.php @@ -20,15 +20,15 @@ class WithDocs extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->docs = $values['docs']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/response-property/src/Traits/WithMetadata.php b/seed/php-sdk/response-property/src/Traits/WithMetadata.php index a6f0f4de6fe5..944006bda0fc 100644 --- a/seed/php-sdk/response-property/src/Traits/WithMetadata.php +++ b/seed/php-sdk/response-property/src/Traits/WithMetadata.php @@ -8,7 +8,7 @@ /** * @property array $metadata */ -trait WithMetadata +trait WithMetadata { /** * @var array $metadata diff --git a/seed/php-sdk/response-property/src/Types/StringResponse.php b/seed/php-sdk/response-property/src/Types/StringResponse.php index 01c7c5e45b49..f413c9c7e449 100644 --- a/seed/php-sdk/response-property/src/Types/StringResponse.php +++ b/seed/php-sdk/response-property/src/Types/StringResponse.php @@ -20,15 +20,15 @@ class StringResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->data = $values['data']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/response-property/src/Types/WithMetadata.php b/seed/php-sdk/response-property/src/Types/WithMetadata.php index 6531bc7f271c..a59296450b09 100644 --- a/seed/php-sdk/response-property/src/Types/WithMetadata.php +++ b/seed/php-sdk/response-property/src/Types/WithMetadata.php @@ -21,15 +21,15 @@ class WithMetadata extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->metadata = $values['metadata']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/response-property/src/Utils/File.php b/seed/php-sdk/response-property/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/response-property/src/Utils/File.php +++ b/seed/php-sdk/response-property/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/response-property/tests/Core/Client/RawClientTest.php b/seed/php-sdk/response-property/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/response-property/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/response-property/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/response-property/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/response-property/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/response-property/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/response-property/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/response-property/tests/Core/Json/EnumTest.php b/seed/php-sdk/response-property/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/response-property/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/response-property/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/response-property/tests/Core/Json/TraitTest.php b/seed/php-sdk/response-property/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/response-property/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/response-property/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/response-property/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/response-property/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/response-property/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/response-property/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/server-sent-event-examples/src/Completions/CompletionsClient.php b/seed/php-sdk/server-sent-event-examples/src/Completions/CompletionsClient.php index 7e7dcf812e93..5a34aa3070b0 100644 --- a/seed/php-sdk/server-sent-event-examples/src/Completions/CompletionsClient.php +++ b/seed/php-sdk/server-sent-event-examples/src/Completions/CompletionsClient.php @@ -12,7 +12,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class CompletionsClient +class CompletionsClient { /** * @var array{ @@ -40,11 +40,10 @@ class CompletionsClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -62,7 +61,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function stream(StreamCompletionRequest $request, ?array $options = null): void { + public function stream(StreamCompletionRequest $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -77,7 +77,7 @@ public function stream(StreamCompletionRequest $request, ?array $options = null) $statusCode = $response->getStatusCode(); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/server-sent-event-examples/src/Completions/Requests/StreamCompletionRequest.php b/seed/php-sdk/server-sent-event-examples/src/Completions/Requests/StreamCompletionRequest.php index 2daddab5e8a2..97b40294829d 100644 --- a/seed/php-sdk/server-sent-event-examples/src/Completions/Requests/StreamCompletionRequest.php +++ b/seed/php-sdk/server-sent-event-examples/src/Completions/Requests/StreamCompletionRequest.php @@ -20,8 +20,7 @@ class StreamCompletionRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->query = $values['query']; } } diff --git a/seed/php-sdk/server-sent-event-examples/src/Completions/Types/StreamedCompletion.php b/seed/php-sdk/server-sent-event-examples/src/Completions/Types/StreamedCompletion.php index 0b569651b794..2223f50383e1 100644 --- a/seed/php-sdk/server-sent-event-examples/src/Completions/Types/StreamedCompletion.php +++ b/seed/php-sdk/server-sent-event-examples/src/Completions/Types/StreamedCompletion.php @@ -27,15 +27,16 @@ class StreamedCompletion extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->delta = $values['delta'];$this->tokens = $values['tokens'] ?? null; + ) { + $this->delta = $values['delta']; + $this->tokens = $values['tokens'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/server-sent-event-examples/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/server-sent-event-examples/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/server-sent-event-examples/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/server-sent-event-examples/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/server-sent-event-examples/src/Core/Client/RawClient.php b/seed/php-sdk/server-sent-event-examples/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/server-sent-event-examples/src/Core/Client/RawClient.php +++ b/seed/php-sdk/server-sent-event-examples/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/server-sent-event-examples/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/server-sent-event-examples/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/server-sent-event-examples/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/server-sent-event-examples/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/server-sent-event-examples/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/server-sent-event-examples/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/server-sent-event-examples/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/server-sent-event-examples/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/server-sent-event-examples/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/server-sent-event-examples/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/server-sent-event-examples/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/server-sent-event-examples/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/server-sent-event-examples/src/Core/Types/ArrayType.php b/seed/php-sdk/server-sent-event-examples/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/server-sent-event-examples/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/server-sent-event-examples/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/server-sent-event-examples/src/Core/Types/Constant.php b/seed/php-sdk/server-sent-event-examples/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/server-sent-event-examples/src/Core/Types/Constant.php +++ b/seed/php-sdk/server-sent-event-examples/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/server-sent-event-examples/src/Core/Types/Union.php b/seed/php-sdk/server-sent-event-examples/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/server-sent-event-examples/src/Core/Types/Union.php +++ b/seed/php-sdk/server-sent-event-examples/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/server-sent-event-examples/src/Exceptions/SeedApiException.php b/seed/php-sdk/server-sent-event-examples/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/server-sent-event-examples/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/server-sent-event-examples/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/server-sent-event-examples/src/Exceptions/SeedException.php b/seed/php-sdk/server-sent-event-examples/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/server-sent-event-examples/src/Exceptions/SeedException.php +++ b/seed/php-sdk/server-sent-event-examples/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/server-sent-event-examples/src/SeedClient.php b/seed/php-sdk/server-sent-event-examples/src/SeedClient.php index 1530ca7ea228..c61375c254c5 100644 --- a/seed/php-sdk/server-sent-event-examples/src/SeedClient.php +++ b/seed/php-sdk/server-sent-event-examples/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var CompletionsClient $completions @@ -40,26 +40,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->completions = new CompletionsClient($this->client, $this->options); } } diff --git a/seed/php-sdk/server-sent-event-examples/src/Utils/File.php b/seed/php-sdk/server-sent-event-examples/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/server-sent-event-examples/src/Utils/File.php +++ b/seed/php-sdk/server-sent-event-examples/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/server-sent-event-examples/tests/Core/Client/RawClientTest.php b/seed/php-sdk/server-sent-event-examples/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/server-sent-event-examples/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/server-sent-event-examples/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/server-sent-event-examples/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/server-sent-event-examples/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/server-sent-event-examples/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/server-sent-event-examples/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/server-sent-event-examples/tests/Core/Json/EnumTest.php b/seed/php-sdk/server-sent-event-examples/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/server-sent-event-examples/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/server-sent-event-examples/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/server-sent-event-examples/tests/Core/Json/TraitTest.php b/seed/php-sdk/server-sent-event-examples/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/server-sent-event-examples/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/server-sent-event-examples/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/server-sent-event-examples/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/server-sent-event-examples/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/server-sent-event-examples/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/server-sent-event-examples/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/server-sent-events/src/Completions/CompletionsClient.php b/seed/php-sdk/server-sent-events/src/Completions/CompletionsClient.php index 7e7dcf812e93..5a34aa3070b0 100644 --- a/seed/php-sdk/server-sent-events/src/Completions/CompletionsClient.php +++ b/seed/php-sdk/server-sent-events/src/Completions/CompletionsClient.php @@ -12,7 +12,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class CompletionsClient +class CompletionsClient { /** * @var array{ @@ -40,11 +40,10 @@ class CompletionsClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -62,7 +61,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function stream(StreamCompletionRequest $request, ?array $options = null): void { + public function stream(StreamCompletionRequest $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -77,7 +77,7 @@ public function stream(StreamCompletionRequest $request, ?array $options = null) $statusCode = $response->getStatusCode(); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/server-sent-events/src/Completions/Requests/StreamCompletionRequest.php b/seed/php-sdk/server-sent-events/src/Completions/Requests/StreamCompletionRequest.php index 2daddab5e8a2..97b40294829d 100644 --- a/seed/php-sdk/server-sent-events/src/Completions/Requests/StreamCompletionRequest.php +++ b/seed/php-sdk/server-sent-events/src/Completions/Requests/StreamCompletionRequest.php @@ -20,8 +20,7 @@ class StreamCompletionRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->query = $values['query']; } } diff --git a/seed/php-sdk/server-sent-events/src/Completions/Types/StreamedCompletion.php b/seed/php-sdk/server-sent-events/src/Completions/Types/StreamedCompletion.php index 0b569651b794..2223f50383e1 100644 --- a/seed/php-sdk/server-sent-events/src/Completions/Types/StreamedCompletion.php +++ b/seed/php-sdk/server-sent-events/src/Completions/Types/StreamedCompletion.php @@ -27,15 +27,16 @@ class StreamedCompletion extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->delta = $values['delta'];$this->tokens = $values['tokens'] ?? null; + ) { + $this->delta = $values['delta']; + $this->tokens = $values['tokens'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/server-sent-events/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/server-sent-events/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/server-sent-events/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/server-sent-events/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/server-sent-events/src/Core/Client/RawClient.php b/seed/php-sdk/server-sent-events/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/server-sent-events/src/Core/Client/RawClient.php +++ b/seed/php-sdk/server-sent-events/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/server-sent-events/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/server-sent-events/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/server-sent-events/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/server-sent-events/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/server-sent-events/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/server-sent-events/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/server-sent-events/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/server-sent-events/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/server-sent-events/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/server-sent-events/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/server-sent-events/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/server-sent-events/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/server-sent-events/src/Core/Types/ArrayType.php b/seed/php-sdk/server-sent-events/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/server-sent-events/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/server-sent-events/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/server-sent-events/src/Core/Types/Constant.php b/seed/php-sdk/server-sent-events/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/server-sent-events/src/Core/Types/Constant.php +++ b/seed/php-sdk/server-sent-events/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/server-sent-events/src/Core/Types/Union.php b/seed/php-sdk/server-sent-events/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/server-sent-events/src/Core/Types/Union.php +++ b/seed/php-sdk/server-sent-events/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/server-sent-events/src/Exceptions/SeedApiException.php b/seed/php-sdk/server-sent-events/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/server-sent-events/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/server-sent-events/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/server-sent-events/src/Exceptions/SeedException.php b/seed/php-sdk/server-sent-events/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/server-sent-events/src/Exceptions/SeedException.php +++ b/seed/php-sdk/server-sent-events/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/server-sent-events/src/SeedClient.php b/seed/php-sdk/server-sent-events/src/SeedClient.php index 1530ca7ea228..c61375c254c5 100644 --- a/seed/php-sdk/server-sent-events/src/SeedClient.php +++ b/seed/php-sdk/server-sent-events/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var CompletionsClient $completions @@ -40,26 +40,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->completions = new CompletionsClient($this->client, $this->options); } } diff --git a/seed/php-sdk/server-sent-events/src/Utils/File.php b/seed/php-sdk/server-sent-events/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/server-sent-events/src/Utils/File.php +++ b/seed/php-sdk/server-sent-events/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/server-sent-events/tests/Core/Client/RawClientTest.php b/seed/php-sdk/server-sent-events/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/server-sent-events/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/server-sent-events/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/server-sent-events/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/server-sent-events/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/server-sent-events/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/server-sent-events/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/server-sent-events/tests/Core/Json/EnumTest.php b/seed/php-sdk/server-sent-events/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/server-sent-events/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/server-sent-events/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/server-sent-events/tests/Core/Json/TraitTest.php b/seed/php-sdk/server-sent-events/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/server-sent-events/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/server-sent-events/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/server-sent-events/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/server-sent-events/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/server-sent-events/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/server-sent-events/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/simple-api/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/simple-api/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/simple-api/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/simple-api/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/simple-api/src/Core/Client/RawClient.php b/seed/php-sdk/simple-api/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/simple-api/src/Core/Client/RawClient.php +++ b/seed/php-sdk/simple-api/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/simple-api/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/simple-api/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/simple-api/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/simple-api/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/simple-api/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/simple-api/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/simple-api/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/simple-api/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/simple-api/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/simple-api/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/simple-api/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/simple-api/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/simple-api/src/Core/Types/ArrayType.php b/seed/php-sdk/simple-api/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/simple-api/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/simple-api/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/simple-api/src/Core/Types/Constant.php b/seed/php-sdk/simple-api/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/simple-api/src/Core/Types/Constant.php +++ b/seed/php-sdk/simple-api/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/simple-api/src/Core/Types/Union.php b/seed/php-sdk/simple-api/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/simple-api/src/Core/Types/Union.php +++ b/seed/php-sdk/simple-api/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/simple-api/src/Environments.php b/seed/php-sdk/simple-api/src/Environments.php index f4f91f685a8c..071fa5ce2a06 100644 --- a/seed/php-sdk/simple-api/src/Environments.php +++ b/seed/php-sdk/simple-api/src/Environments.php @@ -2,8 +2,8 @@ namespace Seed; -enum Environments - : string { +enum Environments: string +{ case Production = "https://api.example.com"; case Staging = "https://staging-api.example.com"; } diff --git a/seed/php-sdk/simple-api/src/Exceptions/SeedApiException.php b/seed/php-sdk/simple-api/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/simple-api/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/simple-api/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/simple-api/src/Exceptions/SeedException.php b/seed/php-sdk/simple-api/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/simple-api/src/Exceptions/SeedException.php +++ b/seed/php-sdk/simple-api/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/simple-api/src/SeedClient.php b/seed/php-sdk/simple-api/src/SeedClient.php index 3e7e153250c4..526e1eefe684 100644 --- a/seed/php-sdk/simple-api/src/SeedClient.php +++ b/seed/php-sdk/simple-api/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var UserClient $user @@ -42,8 +42,7 @@ class SeedClient public function __construct( string $token, ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'Authorization' => "Bearer $token", 'X-Fern-Language' => 'PHP', @@ -51,18 +50,18 @@ public function __construct( 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->user = new UserClient($this->client, $this->options); } } diff --git a/seed/php-sdk/simple-api/src/User/Types/User.php b/seed/php-sdk/simple-api/src/User/Types/User.php index 1305e9c0c8b7..081ef406dc5c 100644 --- a/seed/php-sdk/simple-api/src/User/Types/User.php +++ b/seed/php-sdk/simple-api/src/User/Types/User.php @@ -34,15 +34,17 @@ class User extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->name = $values['name'];$this->email = $values['email']; + ) { + $this->id = $values['id']; + $this->name = $values['name']; + $this->email = $values['email']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/simple-api/src/User/UserClient.php b/seed/php-sdk/simple-api/src/User/UserClient.php index dd396dfdaa61..95934e051d58 100644 --- a/seed/php-sdk/simple-api/src/User/UserClient.php +++ b/seed/php-sdk/simple-api/src/User/UserClient.php @@ -13,7 +13,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class UserClient +class UserClient { /** * @var array{ @@ -41,11 +41,10 @@ class UserClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -64,7 +63,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function get(string $id, ?array $options = null): User { + public function get(string $id, ?array $options = null): User + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -76,15 +76,15 @@ public function get(string $id, ?array $options = null): User { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return User::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/simple-api/src/Utils/File.php b/seed/php-sdk/simple-api/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/simple-api/src/Utils/File.php +++ b/seed/php-sdk/simple-api/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/simple-api/tests/Core/Client/RawClientTest.php b/seed/php-sdk/simple-api/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/simple-api/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/simple-api/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/simple-api/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/simple-api/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/simple-api/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/simple-api/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/simple-api/tests/Core/Json/EnumTest.php b/seed/php-sdk/simple-api/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/simple-api/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/simple-api/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/simple-api/tests/Core/Json/TraitTest.php b/seed/php-sdk/simple-api/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/simple-api/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/simple-api/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/simple-api/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/simple-api/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/simple-api/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/simple-api/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/simple-fhir/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/simple-fhir/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/simple-fhir/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/simple-fhir/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/simple-fhir/src/Core/Client/RawClient.php b/seed/php-sdk/simple-fhir/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/simple-fhir/src/Core/Client/RawClient.php +++ b/seed/php-sdk/simple-fhir/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/simple-fhir/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/simple-fhir/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/simple-fhir/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/simple-fhir/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/simple-fhir/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/simple-fhir/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/simple-fhir/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/simple-fhir/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/simple-fhir/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/simple-fhir/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/simple-fhir/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/simple-fhir/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/simple-fhir/src/Core/Types/ArrayType.php b/seed/php-sdk/simple-fhir/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/simple-fhir/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/simple-fhir/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/simple-fhir/src/Core/Types/Constant.php b/seed/php-sdk/simple-fhir/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/simple-fhir/src/Core/Types/Constant.php +++ b/seed/php-sdk/simple-fhir/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/simple-fhir/src/Core/Types/Union.php b/seed/php-sdk/simple-fhir/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/simple-fhir/src/Core/Types/Union.php +++ b/seed/php-sdk/simple-fhir/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/simple-fhir/src/Exceptions/SeedApiException.php b/seed/php-sdk/simple-fhir/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/simple-fhir/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/simple-fhir/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/simple-fhir/src/Exceptions/SeedException.php b/seed/php-sdk/simple-fhir/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/simple-fhir/src/Exceptions/SeedException.php +++ b/seed/php-sdk/simple-fhir/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/simple-fhir/src/SeedClient.php b/seed/php-sdk/simple-fhir/src/SeedClient.php index 20dc87780ce1..8b42b7985e33 100644 --- a/seed/php-sdk/simple-fhir/src/SeedClient.php +++ b/seed/php-sdk/simple-fhir/src/SeedClient.php @@ -13,7 +13,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class SeedClient +class SeedClient { /** * @var array{ @@ -42,22 +42,21 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); @@ -77,7 +76,8 @@ public function __construct( * @throws SeedException * @throws SeedApiException */ - public function getAccount(string $accountId, ?array $options = null): Account { + public function getAccount(string $accountId, ?array $options = null): Account + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -89,15 +89,15 @@ public function getAccount(string $accountId, ?array $options = null): Account { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return Account::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/simple-fhir/src/Traits/BaseResource.php b/seed/php-sdk/simple-fhir/src/Traits/BaseResource.php index cacf087bc034..2b9eefd60dd6 100644 --- a/seed/php-sdk/simple-fhir/src/Traits/BaseResource.php +++ b/seed/php-sdk/simple-fhir/src/Traits/BaseResource.php @@ -21,7 +21,7 @@ * )> $relatedResources * @property Memo $memo */ -trait BaseResource +trait BaseResource { /** * @var string $id diff --git a/seed/php-sdk/simple-fhir/src/Types/Account.php b/seed/php-sdk/simple-fhir/src/Types/Account.php index e34944de1bec..54615988cd74 100644 --- a/seed/php-sdk/simple-fhir/src/Types/Account.php +++ b/seed/php-sdk/simple-fhir/src/Types/Account.php @@ -52,15 +52,21 @@ class Account extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->relatedResources = $values['relatedResources'];$this->memo = $values['memo'];$this->resourceType = $values['resourceType'];$this->name = $values['name'];$this->patient = $values['patient'] ?? null;$this->practitioner = $values['practitioner'] ?? null; + ) { + $this->id = $values['id']; + $this->relatedResources = $values['relatedResources']; + $this->memo = $values['memo']; + $this->resourceType = $values['resourceType']; + $this->name = $values['name']; + $this->patient = $values['patient'] ?? null; + $this->practitioner = $values['practitioner'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/simple-fhir/src/Types/BaseResource.php b/seed/php-sdk/simple-fhir/src/Types/BaseResource.php index 2178151f80a7..5b431e4f0c67 100644 --- a/seed/php-sdk/simple-fhir/src/Types/BaseResource.php +++ b/seed/php-sdk/simple-fhir/src/Types/BaseResource.php @@ -46,15 +46,17 @@ class BaseResource extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->relatedResources = $values['relatedResources'];$this->memo = $values['memo']; + ) { + $this->id = $values['id']; + $this->relatedResources = $values['relatedResources']; + $this->memo = $values['memo']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/simple-fhir/src/Types/Memo.php b/seed/php-sdk/simple-fhir/src/Types/Memo.php index 18c64dba7670..91d1cc66aa07 100644 --- a/seed/php-sdk/simple-fhir/src/Types/Memo.php +++ b/seed/php-sdk/simple-fhir/src/Types/Memo.php @@ -27,15 +27,16 @@ class Memo extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->description = $values['description'];$this->account = $values['account'] ?? null; + ) { + $this->description = $values['description']; + $this->account = $values['account'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/simple-fhir/src/Types/Patient.php b/seed/php-sdk/simple-fhir/src/Types/Patient.php index 61ad48c740d8..b17318fe449d 100644 --- a/seed/php-sdk/simple-fhir/src/Types/Patient.php +++ b/seed/php-sdk/simple-fhir/src/Types/Patient.php @@ -46,15 +46,20 @@ class Patient extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->relatedResources = $values['relatedResources'];$this->memo = $values['memo'];$this->resourceType = $values['resourceType'];$this->name = $values['name'];$this->scripts = $values['scripts']; + ) { + $this->id = $values['id']; + $this->relatedResources = $values['relatedResources']; + $this->memo = $values['memo']; + $this->resourceType = $values['resourceType']; + $this->name = $values['name']; + $this->scripts = $values['scripts']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/simple-fhir/src/Types/Practitioner.php b/seed/php-sdk/simple-fhir/src/Types/Practitioner.php index b8558d097791..45a977a5e1a0 100644 --- a/seed/php-sdk/simple-fhir/src/Types/Practitioner.php +++ b/seed/php-sdk/simple-fhir/src/Types/Practitioner.php @@ -38,15 +38,19 @@ class Practitioner extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->relatedResources = $values['relatedResources'];$this->memo = $values['memo'];$this->resourceType = $values['resourceType'];$this->name = $values['name']; + ) { + $this->id = $values['id']; + $this->relatedResources = $values['relatedResources']; + $this->memo = $values['memo']; + $this->resourceType = $values['resourceType']; + $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/simple-fhir/src/Types/Script.php b/seed/php-sdk/simple-fhir/src/Types/Script.php index dc02b5bf06fa..41713e84e9f7 100644 --- a/seed/php-sdk/simple-fhir/src/Types/Script.php +++ b/seed/php-sdk/simple-fhir/src/Types/Script.php @@ -38,15 +38,19 @@ class Script extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->relatedResources = $values['relatedResources'];$this->memo = $values['memo'];$this->resourceType = $values['resourceType'];$this->name = $values['name']; + ) { + $this->id = $values['id']; + $this->relatedResources = $values['relatedResources']; + $this->memo = $values['memo']; + $this->resourceType = $values['resourceType']; + $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/simple-fhir/src/Utils/File.php b/seed/php-sdk/simple-fhir/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/simple-fhir/src/Utils/File.php +++ b/seed/php-sdk/simple-fhir/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/simple-fhir/tests/Core/Client/RawClientTest.php b/seed/php-sdk/simple-fhir/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/simple-fhir/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/simple-fhir/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/simple-fhir/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/simple-fhir/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/simple-fhir/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/simple-fhir/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/simple-fhir/tests/Core/Json/EnumTest.php b/seed/php-sdk/simple-fhir/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/simple-fhir/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/simple-fhir/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/simple-fhir/tests/Core/Json/TraitTest.php b/seed/php-sdk/simple-fhir/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/simple-fhir/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/simple-fhir/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/simple-fhir/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/simple-fhir/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/simple-fhir/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/simple-fhir/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/single-url-environment-default/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/single-url-environment-default/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/single-url-environment-default/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/single-url-environment-default/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/single-url-environment-default/src/Core/Client/RawClient.php b/seed/php-sdk/single-url-environment-default/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/single-url-environment-default/src/Core/Client/RawClient.php +++ b/seed/php-sdk/single-url-environment-default/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/single-url-environment-default/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/single-url-environment-default/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/single-url-environment-default/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/single-url-environment-default/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/single-url-environment-default/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/single-url-environment-default/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/single-url-environment-default/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/single-url-environment-default/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/single-url-environment-default/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/single-url-environment-default/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/single-url-environment-default/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/single-url-environment-default/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/single-url-environment-default/src/Core/Types/ArrayType.php b/seed/php-sdk/single-url-environment-default/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/single-url-environment-default/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/single-url-environment-default/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/single-url-environment-default/src/Core/Types/Constant.php b/seed/php-sdk/single-url-environment-default/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/single-url-environment-default/src/Core/Types/Constant.php +++ b/seed/php-sdk/single-url-environment-default/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/single-url-environment-default/src/Core/Types/Union.php b/seed/php-sdk/single-url-environment-default/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/single-url-environment-default/src/Core/Types/Union.php +++ b/seed/php-sdk/single-url-environment-default/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/single-url-environment-default/src/Dummy/DummyClient.php b/seed/php-sdk/single-url-environment-default/src/Dummy/DummyClient.php index b75ec5b58e67..ed8e48eacc05 100644 --- a/seed/php-sdk/single-url-environment-default/src/Dummy/DummyClient.php +++ b/seed/php-sdk/single-url-environment-default/src/Dummy/DummyClient.php @@ -14,7 +14,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class DummyClient +class DummyClient { /** * @var array{ @@ -42,11 +42,10 @@ class DummyClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -64,7 +63,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getDummy(?array $options = null): string { + public function getDummy(?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -76,15 +76,15 @@ public function getDummy(?array $options = null): string { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/single-url-environment-default/src/Environments.php b/seed/php-sdk/single-url-environment-default/src/Environments.php index fd74f6324708..f49ef601cec1 100644 --- a/seed/php-sdk/single-url-environment-default/src/Environments.php +++ b/seed/php-sdk/single-url-environment-default/src/Environments.php @@ -2,8 +2,8 @@ namespace Seed; -enum Environments - : string { +enum Environments: string +{ case Production = "https://production.com/api"; case Staging = "https://staging.com/api"; } diff --git a/seed/php-sdk/single-url-environment-default/src/Exceptions/SeedApiException.php b/seed/php-sdk/single-url-environment-default/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/single-url-environment-default/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/single-url-environment-default/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/single-url-environment-default/src/Exceptions/SeedException.php b/seed/php-sdk/single-url-environment-default/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/single-url-environment-default/src/Exceptions/SeedException.php +++ b/seed/php-sdk/single-url-environment-default/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/single-url-environment-default/src/SeedClient.php b/seed/php-sdk/single-url-environment-default/src/SeedClient.php index 1b7f12777f12..35ffb9b5f682 100644 --- a/seed/php-sdk/single-url-environment-default/src/SeedClient.php +++ b/seed/php-sdk/single-url-environment-default/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var DummyClient $dummy @@ -42,8 +42,7 @@ class SeedClient public function __construct( string $token, ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'Authorization' => "Bearer $token", 'X-Fern-Language' => 'PHP', @@ -51,18 +50,18 @@ public function __construct( 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->dummy = new DummyClient($this->client, $this->options); } } diff --git a/seed/php-sdk/single-url-environment-default/src/Utils/File.php b/seed/php-sdk/single-url-environment-default/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/single-url-environment-default/src/Utils/File.php +++ b/seed/php-sdk/single-url-environment-default/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/single-url-environment-default/tests/Core/Client/RawClientTest.php b/seed/php-sdk/single-url-environment-default/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/single-url-environment-default/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/single-url-environment-default/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/single-url-environment-default/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/single-url-environment-default/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/single-url-environment-default/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/single-url-environment-default/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/single-url-environment-default/tests/Core/Json/EnumTest.php b/seed/php-sdk/single-url-environment-default/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/single-url-environment-default/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/single-url-environment-default/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/single-url-environment-default/tests/Core/Json/TraitTest.php b/seed/php-sdk/single-url-environment-default/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/single-url-environment-default/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/single-url-environment-default/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/single-url-environment-default/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/single-url-environment-default/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/single-url-environment-default/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/single-url-environment-default/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/single-url-environment-no-default/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/single-url-environment-no-default/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/single-url-environment-no-default/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/single-url-environment-no-default/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/single-url-environment-no-default/src/Core/Client/RawClient.php b/seed/php-sdk/single-url-environment-no-default/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/single-url-environment-no-default/src/Core/Client/RawClient.php +++ b/seed/php-sdk/single-url-environment-no-default/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/single-url-environment-no-default/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/single-url-environment-no-default/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/single-url-environment-no-default/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/single-url-environment-no-default/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/single-url-environment-no-default/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/single-url-environment-no-default/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/single-url-environment-no-default/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/single-url-environment-no-default/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/single-url-environment-no-default/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/single-url-environment-no-default/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/single-url-environment-no-default/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/single-url-environment-no-default/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/single-url-environment-no-default/src/Core/Types/ArrayType.php b/seed/php-sdk/single-url-environment-no-default/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/single-url-environment-no-default/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/single-url-environment-no-default/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/single-url-environment-no-default/src/Core/Types/Constant.php b/seed/php-sdk/single-url-environment-no-default/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/single-url-environment-no-default/src/Core/Types/Constant.php +++ b/seed/php-sdk/single-url-environment-no-default/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/single-url-environment-no-default/src/Core/Types/Union.php b/seed/php-sdk/single-url-environment-no-default/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/single-url-environment-no-default/src/Core/Types/Union.php +++ b/seed/php-sdk/single-url-environment-no-default/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/single-url-environment-no-default/src/Dummy/DummyClient.php b/seed/php-sdk/single-url-environment-no-default/src/Dummy/DummyClient.php index 1fe039c12c39..94dfc4ade2cb 100644 --- a/seed/php-sdk/single-url-environment-no-default/src/Dummy/DummyClient.php +++ b/seed/php-sdk/single-url-environment-no-default/src/Dummy/DummyClient.php @@ -13,7 +13,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class DummyClient +class DummyClient { /** * @var array{ @@ -41,11 +41,10 @@ class DummyClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -63,7 +62,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getDummy(?array $options = null): string { + public function getDummy(?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -75,15 +75,15 @@ public function getDummy(?array $options = null): string { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/single-url-environment-no-default/src/Environments.php b/seed/php-sdk/single-url-environment-no-default/src/Environments.php index fd74f6324708..f49ef601cec1 100644 --- a/seed/php-sdk/single-url-environment-no-default/src/Environments.php +++ b/seed/php-sdk/single-url-environment-no-default/src/Environments.php @@ -2,8 +2,8 @@ namespace Seed; -enum Environments - : string { +enum Environments: string +{ case Production = "https://production.com/api"; case Staging = "https://staging.com/api"; } diff --git a/seed/php-sdk/single-url-environment-no-default/src/Exceptions/SeedApiException.php b/seed/php-sdk/single-url-environment-no-default/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/single-url-environment-no-default/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/single-url-environment-no-default/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/single-url-environment-no-default/src/Exceptions/SeedException.php b/seed/php-sdk/single-url-environment-no-default/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/single-url-environment-no-default/src/Exceptions/SeedException.php +++ b/seed/php-sdk/single-url-environment-no-default/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/single-url-environment-no-default/src/SeedClient.php b/seed/php-sdk/single-url-environment-no-default/src/SeedClient.php index 1b7f12777f12..35ffb9b5f682 100644 --- a/seed/php-sdk/single-url-environment-no-default/src/SeedClient.php +++ b/seed/php-sdk/single-url-environment-no-default/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var DummyClient $dummy @@ -42,8 +42,7 @@ class SeedClient public function __construct( string $token, ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'Authorization' => "Bearer $token", 'X-Fern-Language' => 'PHP', @@ -51,18 +50,18 @@ public function __construct( 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->dummy = new DummyClient($this->client, $this->options); } } diff --git a/seed/php-sdk/single-url-environment-no-default/src/Utils/File.php b/seed/php-sdk/single-url-environment-no-default/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/single-url-environment-no-default/src/Utils/File.php +++ b/seed/php-sdk/single-url-environment-no-default/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/single-url-environment-no-default/tests/Core/Client/RawClientTest.php b/seed/php-sdk/single-url-environment-no-default/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/single-url-environment-no-default/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/single-url-environment-no-default/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/single-url-environment-no-default/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/single-url-environment-no-default/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/single-url-environment-no-default/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/single-url-environment-no-default/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/single-url-environment-no-default/tests/Core/Json/EnumTest.php b/seed/php-sdk/single-url-environment-no-default/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/single-url-environment-no-default/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/single-url-environment-no-default/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/single-url-environment-no-default/tests/Core/Json/TraitTest.php b/seed/php-sdk/single-url-environment-no-default/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/single-url-environment-no-default/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/single-url-environment-no-default/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/single-url-environment-no-default/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/single-url-environment-no-default/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/single-url-environment-no-default/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/single-url-environment-no-default/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/streaming-parameter/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/streaming-parameter/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/streaming-parameter/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/streaming-parameter/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/streaming-parameter/src/Core/Client/RawClient.php b/seed/php-sdk/streaming-parameter/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/streaming-parameter/src/Core/Client/RawClient.php +++ b/seed/php-sdk/streaming-parameter/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/streaming-parameter/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/streaming-parameter/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/streaming-parameter/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/streaming-parameter/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/streaming-parameter/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/streaming-parameter/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/streaming-parameter/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/streaming-parameter/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/streaming-parameter/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/streaming-parameter/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/streaming-parameter/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/streaming-parameter/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/streaming-parameter/src/Core/Types/ArrayType.php b/seed/php-sdk/streaming-parameter/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/streaming-parameter/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/streaming-parameter/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/streaming-parameter/src/Core/Types/Constant.php b/seed/php-sdk/streaming-parameter/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/streaming-parameter/src/Core/Types/Constant.php +++ b/seed/php-sdk/streaming-parameter/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/streaming-parameter/src/Core/Types/Union.php b/seed/php-sdk/streaming-parameter/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/streaming-parameter/src/Core/Types/Union.php +++ b/seed/php-sdk/streaming-parameter/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/streaming-parameter/src/Dummy/DummyClient.php b/seed/php-sdk/streaming-parameter/src/Dummy/DummyClient.php index 8c5558b0ab81..ef87547b03ed 100644 --- a/seed/php-sdk/streaming-parameter/src/Dummy/DummyClient.php +++ b/seed/php-sdk/streaming-parameter/src/Dummy/DummyClient.php @@ -12,7 +12,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class DummyClient +class DummyClient { /** * @var array{ @@ -40,11 +40,10 @@ class DummyClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -62,7 +61,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function generate(GenerateRequest $request, ?array $options = null): void { + public function generate(GenerateRequest $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -77,7 +77,7 @@ public function generate(GenerateRequest $request, ?array $options = null): void $statusCode = $response->getStatusCode(); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/streaming-parameter/src/Dummy/Requests/GenerateRequest.php b/seed/php-sdk/streaming-parameter/src/Dummy/Requests/GenerateRequest.php index 345ee2283c2b..0d7ab8665f05 100644 --- a/seed/php-sdk/streaming-parameter/src/Dummy/Requests/GenerateRequest.php +++ b/seed/php-sdk/streaming-parameter/src/Dummy/Requests/GenerateRequest.php @@ -27,8 +27,8 @@ class GenerateRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->stream = $values['stream'];$this->numEvents = $values['numEvents']; + ) { + $this->stream = $values['stream']; + $this->numEvents = $values['numEvents']; } } diff --git a/seed/php-sdk/streaming-parameter/src/Dummy/Types/RegularResponse.php b/seed/php-sdk/streaming-parameter/src/Dummy/Types/RegularResponse.php index e042233d7907..6852b7f3275b 100644 --- a/seed/php-sdk/streaming-parameter/src/Dummy/Types/RegularResponse.php +++ b/seed/php-sdk/streaming-parameter/src/Dummy/Types/RegularResponse.php @@ -27,15 +27,16 @@ class RegularResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->name = $values['name'] ?? null; + ) { + $this->id = $values['id']; + $this->name = $values['name'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/streaming-parameter/src/Dummy/Types/StreamResponse.php b/seed/php-sdk/streaming-parameter/src/Dummy/Types/StreamResponse.php index 7764c8340fa6..603f02db6f15 100644 --- a/seed/php-sdk/streaming-parameter/src/Dummy/Types/StreamResponse.php +++ b/seed/php-sdk/streaming-parameter/src/Dummy/Types/StreamResponse.php @@ -27,15 +27,16 @@ class StreamResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->name = $values['name'] ?? null; + ) { + $this->id = $values['id']; + $this->name = $values['name'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/streaming-parameter/src/Exceptions/SeedApiException.php b/seed/php-sdk/streaming-parameter/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/streaming-parameter/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/streaming-parameter/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/streaming-parameter/src/Exceptions/SeedException.php b/seed/php-sdk/streaming-parameter/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/streaming-parameter/src/Exceptions/SeedException.php +++ b/seed/php-sdk/streaming-parameter/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/streaming-parameter/src/SeedClient.php b/seed/php-sdk/streaming-parameter/src/SeedClient.php index e4e22e9207c5..d76ce9fb3fc3 100644 --- a/seed/php-sdk/streaming-parameter/src/SeedClient.php +++ b/seed/php-sdk/streaming-parameter/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var DummyClient $dummy @@ -40,26 +40,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->dummy = new DummyClient($this->client, $this->options); } } diff --git a/seed/php-sdk/streaming-parameter/src/Utils/File.php b/seed/php-sdk/streaming-parameter/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/streaming-parameter/src/Utils/File.php +++ b/seed/php-sdk/streaming-parameter/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/streaming-parameter/tests/Core/Client/RawClientTest.php b/seed/php-sdk/streaming-parameter/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/streaming-parameter/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/streaming-parameter/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/streaming-parameter/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/streaming-parameter/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/streaming-parameter/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/streaming-parameter/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/streaming-parameter/tests/Core/Json/EnumTest.php b/seed/php-sdk/streaming-parameter/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/streaming-parameter/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/streaming-parameter/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/streaming-parameter/tests/Core/Json/TraitTest.php b/seed/php-sdk/streaming-parameter/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/streaming-parameter/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/streaming-parameter/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/streaming-parameter/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/streaming-parameter/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/streaming-parameter/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/streaming-parameter/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/streaming/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/streaming/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/streaming/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/streaming/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/streaming/src/Core/Client/RawClient.php b/seed/php-sdk/streaming/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/streaming/src/Core/Client/RawClient.php +++ b/seed/php-sdk/streaming/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/streaming/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/streaming/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/streaming/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/streaming/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/streaming/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/streaming/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/streaming/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/streaming/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/streaming/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/streaming/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/streaming/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/streaming/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/streaming/src/Core/Types/ArrayType.php b/seed/php-sdk/streaming/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/streaming/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/streaming/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/streaming/src/Core/Types/Constant.php b/seed/php-sdk/streaming/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/streaming/src/Core/Types/Constant.php +++ b/seed/php-sdk/streaming/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/streaming/src/Core/Types/Union.php b/seed/php-sdk/streaming/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/streaming/src/Core/Types/Union.php +++ b/seed/php-sdk/streaming/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/streaming/src/Dummy/DummyClient.php b/seed/php-sdk/streaming/src/Dummy/DummyClient.php index 4f16378d4d6b..495cd1f84405 100644 --- a/seed/php-sdk/streaming/src/Dummy/DummyClient.php +++ b/seed/php-sdk/streaming/src/Dummy/DummyClient.php @@ -15,7 +15,7 @@ use Seed\Dummy\Types\StreamResponse; use JsonException; -class DummyClient +class DummyClient { /** * @var array{ @@ -43,11 +43,10 @@ class DummyClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -65,7 +64,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function generateStream(GenerateStreamRequest $request, ?array $options = null): void { + public function generateStream(GenerateStreamRequest $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -80,7 +80,7 @@ public function generateStream(GenerateStreamRequest $request, ?array $options = $statusCode = $response->getStatusCode(); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -112,7 +112,8 @@ public function generateStream(GenerateStreamRequest $request, ?array $options = * @throws SeedException * @throws SeedApiException */ - public function generate(Generateequest $request, ?array $options = null): StreamResponse { + public function generate(Generateequest $request, ?array $options = null): StreamResponse + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -125,15 +126,15 @@ public function generate(Generateequest $request, ?array $options = null): Strea $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return StreamResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/streaming/src/Dummy/Requests/GenerateStreamRequest.php b/seed/php-sdk/streaming/src/Dummy/Requests/GenerateStreamRequest.php index 166c80c40b60..4053d9bbfe84 100644 --- a/seed/php-sdk/streaming/src/Dummy/Requests/GenerateStreamRequest.php +++ b/seed/php-sdk/streaming/src/Dummy/Requests/GenerateStreamRequest.php @@ -27,8 +27,8 @@ class GenerateStreamRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->stream = $values['stream'];$this->numEvents = $values['numEvents']; + ) { + $this->stream = $values['stream']; + $this->numEvents = $values['numEvents']; } } diff --git a/seed/php-sdk/streaming/src/Dummy/Requests/Generateequest.php b/seed/php-sdk/streaming/src/Dummy/Requests/Generateequest.php index 46c0489423c9..e4e2755d3922 100644 --- a/seed/php-sdk/streaming/src/Dummy/Requests/Generateequest.php +++ b/seed/php-sdk/streaming/src/Dummy/Requests/Generateequest.php @@ -27,8 +27,8 @@ class Generateequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->stream = $values['stream'];$this->numEvents = $values['numEvents']; + ) { + $this->stream = $values['stream']; + $this->numEvents = $values['numEvents']; } } diff --git a/seed/php-sdk/streaming/src/Dummy/Types/StreamResponse.php b/seed/php-sdk/streaming/src/Dummy/Types/StreamResponse.php index 7764c8340fa6..603f02db6f15 100644 --- a/seed/php-sdk/streaming/src/Dummy/Types/StreamResponse.php +++ b/seed/php-sdk/streaming/src/Dummy/Types/StreamResponse.php @@ -27,15 +27,16 @@ class StreamResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->name = $values['name'] ?? null; + ) { + $this->id = $values['id']; + $this->name = $values['name'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/streaming/src/Exceptions/SeedApiException.php b/seed/php-sdk/streaming/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/streaming/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/streaming/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/streaming/src/Exceptions/SeedException.php b/seed/php-sdk/streaming/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/streaming/src/Exceptions/SeedException.php +++ b/seed/php-sdk/streaming/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/streaming/src/SeedClient.php b/seed/php-sdk/streaming/src/SeedClient.php index e4e22e9207c5..d76ce9fb3fc3 100644 --- a/seed/php-sdk/streaming/src/SeedClient.php +++ b/seed/php-sdk/streaming/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var DummyClient $dummy @@ -40,26 +40,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->dummy = new DummyClient($this->client, $this->options); } } diff --git a/seed/php-sdk/streaming/src/Utils/File.php b/seed/php-sdk/streaming/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/streaming/src/Utils/File.php +++ b/seed/php-sdk/streaming/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/streaming/tests/Core/Client/RawClientTest.php b/seed/php-sdk/streaming/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/streaming/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/streaming/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/streaming/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/streaming/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/streaming/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/streaming/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/streaming/tests/Core/Json/EnumTest.php b/seed/php-sdk/streaming/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/streaming/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/streaming/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/streaming/tests/Core/Json/TraitTest.php b/seed/php-sdk/streaming/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/streaming/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/streaming/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/streaming/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/streaming/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/streaming/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/streaming/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/trace/src/Admin/AdminClient.php b/seed/php-sdk/trace/src/Admin/AdminClient.php index 7ff4acac1fdf..6f2f116ff485 100644 --- a/seed/php-sdk/trace/src/Admin/AdminClient.php +++ b/seed/php-sdk/trace/src/Admin/AdminClient.php @@ -20,7 +20,7 @@ use Seed\Core\Json\JsonSerializer; use Seed\Admin\Requests\StoreTracedWorkspaceRequest; -class AdminClient +class AdminClient { /** * @var array{ @@ -48,11 +48,10 @@ class AdminClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -71,7 +70,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function updateTestSubmissionStatus(string $submissionId, TestSubmissionStatus $request, ?array $options = null): void { + public function updateTestSubmissionStatus(string $submissionId, TestSubmissionStatus $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -84,12 +84,12 @@ public function updateTestSubmissionStatus(string $submissionId, TestSubmissionS $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -121,7 +121,8 @@ public function updateTestSubmissionStatus(string $submissionId, TestSubmissionS * @throws SeedException * @throws SeedApiException */ - public function sendTestSubmissionUpdate(string $submissionId, TestSubmissionUpdate $request, ?array $options = null): void { + public function sendTestSubmissionUpdate(string $submissionId, TestSubmissionUpdate $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -134,12 +135,12 @@ public function sendTestSubmissionUpdate(string $submissionId, TestSubmissionUpd $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -171,7 +172,8 @@ public function sendTestSubmissionUpdate(string $submissionId, TestSubmissionUpd * @throws SeedException * @throws SeedApiException */ - public function updateWorkspaceSubmissionStatus(string $submissionId, WorkspaceSubmissionStatus $request, ?array $options = null): void { + public function updateWorkspaceSubmissionStatus(string $submissionId, WorkspaceSubmissionStatus $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -184,12 +186,12 @@ public function updateWorkspaceSubmissionStatus(string $submissionId, WorkspaceS $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -221,7 +223,8 @@ public function updateWorkspaceSubmissionStatus(string $submissionId, WorkspaceS * @throws SeedException * @throws SeedApiException */ - public function sendWorkspaceSubmissionUpdate(string $submissionId, WorkspaceSubmissionUpdate $request, ?array $options = null): void { + public function sendWorkspaceSubmissionUpdate(string $submissionId, WorkspaceSubmissionUpdate $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -234,12 +237,12 @@ public function sendWorkspaceSubmissionUpdate(string $submissionId, WorkspaceSub $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -272,7 +275,8 @@ public function sendWorkspaceSubmissionUpdate(string $submissionId, WorkspaceSub * @throws SeedException * @throws SeedApiException */ - public function storeTracedTestCase(string $submissionId, string $testCaseId, StoreTracedTestCaseRequest $request, ?array $options = null): void { + public function storeTracedTestCase(string $submissionId, string $testCaseId, StoreTracedTestCaseRequest $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -285,12 +289,12 @@ public function storeTracedTestCase(string $submissionId, string $testCaseId, St $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -323,7 +327,8 @@ public function storeTracedTestCase(string $submissionId, string $testCaseId, St * @throws SeedException * @throws SeedApiException */ - public function storeTracedTestCaseV2(string $submissionId, string $testCaseId, array $request, ?array $options = null): void { + public function storeTracedTestCaseV2(string $submissionId, string $testCaseId, array $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -336,12 +341,12 @@ public function storeTracedTestCaseV2(string $submissionId, string $testCaseId, $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -373,7 +378,8 @@ public function storeTracedTestCaseV2(string $submissionId, string $testCaseId, * @throws SeedException * @throws SeedApiException */ - public function storeTracedWorkspace(string $submissionId, StoreTracedWorkspaceRequest $request, ?array $options = null): void { + public function storeTracedWorkspace(string $submissionId, StoreTracedWorkspaceRequest $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -386,12 +392,12 @@ public function storeTracedWorkspace(string $submissionId, StoreTracedWorkspaceR $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -423,7 +429,8 @@ public function storeTracedWorkspace(string $submissionId, StoreTracedWorkspaceR * @throws SeedException * @throws SeedApiException */ - public function storeTracedWorkspaceV2(string $submissionId, array $request, ?array $options = null): void { + public function storeTracedWorkspaceV2(string $submissionId, array $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -436,12 +443,12 @@ public function storeTracedWorkspaceV2(string $submissionId, array $request, ?ar $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/trace/src/Admin/Requests/StoreTracedTestCaseRequest.php b/seed/php-sdk/trace/src/Admin/Requests/StoreTracedTestCaseRequest.php index 0f02e7c36362..c101a5a90ec1 100644 --- a/seed/php-sdk/trace/src/Admin/Requests/StoreTracedTestCaseRequest.php +++ b/seed/php-sdk/trace/src/Admin/Requests/StoreTracedTestCaseRequest.php @@ -30,8 +30,8 @@ class StoreTracedTestCaseRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->result = $values['result'];$this->traceResponses = $values['traceResponses']; + ) { + $this->result = $values['result']; + $this->traceResponses = $values['traceResponses']; } } diff --git a/seed/php-sdk/trace/src/Admin/Requests/StoreTracedWorkspaceRequest.php b/seed/php-sdk/trace/src/Admin/Requests/StoreTracedWorkspaceRequest.php index e8207d2a0dfe..01de8dd92574 100644 --- a/seed/php-sdk/trace/src/Admin/Requests/StoreTracedWorkspaceRequest.php +++ b/seed/php-sdk/trace/src/Admin/Requests/StoreTracedWorkspaceRequest.php @@ -30,8 +30,8 @@ class StoreTracedWorkspaceRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->workspaceRunDetails = $values['workspaceRunDetails'];$this->traceResponses = $values['traceResponses']; + ) { + $this->workspaceRunDetails = $values['workspaceRunDetails']; + $this->traceResponses = $values['traceResponses']; } } diff --git a/seed/php-sdk/trace/src/Admin/Types/Test.php b/seed/php-sdk/trace/src/Admin/Types/Test.php index 0113d781a6e6..1b25e884ac91 100644 --- a/seed/php-sdk/trace/src/Admin/Types/Test.php +++ b/seed/php-sdk/trace/src/Admin/Types/Test.php @@ -40,16 +40,17 @@ class Test extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param bool $and * @return Test */ - public static function and(bool $and): Test { + public static function and(bool $and): Test + { return new Test([ 'type' => 'and', 'value' => $and, @@ -60,7 +61,8 @@ public static function and(bool $and): Test { * @param bool $or * @return Test */ - public static function or(bool $or): Test { + public static function or(bool $or): Test + { return new Test([ 'type' => 'or', 'value' => $or, @@ -70,61 +72,67 @@ public static function or(bool $or): Test { /** * @return bool */ - public function isAnd_(): bool { - return is_bool($this->value)&& $this->type === 'and'; + public function isAnd_(): bool + { + return is_bool($this->value) && $this->type === 'and'; } /** * @return bool */ - public function asAnd_(): bool { - if (!(is_bool($this->value)&& $this->type === 'and')){ + public function asAnd_(): bool + { + if (!(is_bool($this->value) && $this->type === 'and')) { throw new Exception( "Expected and; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isOr_(): bool { - return is_bool($this->value)&& $this->type === 'or'; + public function isOr_(): bool + { + return is_bool($this->value) && $this->type === 'or'; } /** * @return bool */ - public function asOr_(): bool { - if (!(is_bool($this->value)&& $this->type === 'or')){ + public function asOr_(): bool + { + if (!(is_bool($this->value) && $this->type === 'or')) { throw new Exception( "Expected or; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'and': $value = $this->value; $result['and'] = $value; @@ -135,26 +143,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -163,38 +172,39 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'and': - if (!array_key_exists('and', $data)){ + if (!array_key_exists('and', $data)) { throw new Exception( "JSON data is missing property 'and'", ); } - + $args['value'] = $data['and']; break; case 'or': - if (!array_key_exists('or', $data)){ + if (!array_key_exists('or', $data)) { throw new Exception( "JSON data is missing property 'or'", ); } - + $args['value'] = $data['or']; break; case '_unknown': @@ -202,7 +212,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/trace/src/Commons/Types/BinaryTreeNodeAndTreeValue.php b/seed/php-sdk/trace/src/Commons/Types/BinaryTreeNodeAndTreeValue.php index 17800df4c46f..1a1ee96bb3b1 100644 --- a/seed/php-sdk/trace/src/Commons/Types/BinaryTreeNodeAndTreeValue.php +++ b/seed/php-sdk/trace/src/Commons/Types/BinaryTreeNodeAndTreeValue.php @@ -27,15 +27,16 @@ class BinaryTreeNodeAndTreeValue extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->nodeId = $values['nodeId'];$this->fullTree = $values['fullTree']; + ) { + $this->nodeId = $values['nodeId']; + $this->fullTree = $values['fullTree']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Commons/Types/BinaryTreeNodeValue.php b/seed/php-sdk/trace/src/Commons/Types/BinaryTreeNodeValue.php index ca64ca77e1ff..07eeb550ef34 100644 --- a/seed/php-sdk/trace/src/Commons/Types/BinaryTreeNodeValue.php +++ b/seed/php-sdk/trace/src/Commons/Types/BinaryTreeNodeValue.php @@ -41,15 +41,18 @@ class BinaryTreeNodeValue extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->nodeId = $values['nodeId'];$this->val = $values['val'];$this->right = $values['right'] ?? null;$this->left = $values['left'] ?? null; + ) { + $this->nodeId = $values['nodeId']; + $this->val = $values['val']; + $this->right = $values['right'] ?? null; + $this->left = $values['left'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Commons/Types/BinaryTreeValue.php b/seed/php-sdk/trace/src/Commons/Types/BinaryTreeValue.php index a5902c2c23a2..0253d79bc62d 100644 --- a/seed/php-sdk/trace/src/Commons/Types/BinaryTreeValue.php +++ b/seed/php-sdk/trace/src/Commons/Types/BinaryTreeValue.php @@ -28,15 +28,16 @@ class BinaryTreeValue extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->root = $values['root'] ?? null;$this->nodes = $values['nodes']; + ) { + $this->root = $values['root'] ?? null; + $this->nodes = $values['nodes']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Commons/Types/DebugKeyValuePairs.php b/seed/php-sdk/trace/src/Commons/Types/DebugKeyValuePairs.php index a88828941b0d..32852aba765b 100644 --- a/seed/php-sdk/trace/src/Commons/Types/DebugKeyValuePairs.php +++ b/seed/php-sdk/trace/src/Commons/Types/DebugKeyValuePairs.php @@ -27,15 +27,16 @@ class DebugKeyValuePairs extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->key = $values['key'];$this->value = $values['value']; + ) { + $this->key = $values['key']; + $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Commons/Types/DebugMapValue.php b/seed/php-sdk/trace/src/Commons/Types/DebugMapValue.php index 2b948a570c58..292c3a3f2c8f 100644 --- a/seed/php-sdk/trace/src/Commons/Types/DebugMapValue.php +++ b/seed/php-sdk/trace/src/Commons/Types/DebugMapValue.php @@ -21,15 +21,15 @@ class DebugMapValue extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->keyValuePairs = $values['keyValuePairs']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Commons/Types/DebugVariableValue.php b/seed/php-sdk/trace/src/Commons/Types/DebugVariableValue.php index e0793e8bf7a3..d2f5acc227ea 100644 --- a/seed/php-sdk/trace/src/Commons/Types/DebugVariableValue.php +++ b/seed/php-sdk/trace/src/Commons/Types/DebugVariableValue.php @@ -82,16 +82,17 @@ class DebugVariableValue extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param int $integerValue * @return DebugVariableValue */ - public static function integerValue(int $integerValue): DebugVariableValue { + public static function integerValue(int $integerValue): DebugVariableValue + { return new DebugVariableValue([ 'type' => 'integerValue', 'value' => $integerValue, @@ -102,7 +103,8 @@ public static function integerValue(int $integerValue): DebugVariableValue { * @param bool $booleanValue * @return DebugVariableValue */ - public static function booleanValue(bool $booleanValue): DebugVariableValue { + public static function booleanValue(bool $booleanValue): DebugVariableValue + { return new DebugVariableValue([ 'type' => 'booleanValue', 'value' => $booleanValue, @@ -113,7 +115,8 @@ public static function booleanValue(bool $booleanValue): DebugVariableValue { * @param float $doubleValue * @return DebugVariableValue */ - public static function doubleValue(float $doubleValue): DebugVariableValue { + public static function doubleValue(float $doubleValue): DebugVariableValue + { return new DebugVariableValue([ 'type' => 'doubleValue', 'value' => $doubleValue, @@ -124,7 +127,8 @@ public static function doubleValue(float $doubleValue): DebugVariableValue { * @param string $stringValue * @return DebugVariableValue */ - public static function stringValue(string $stringValue): DebugVariableValue { + public static function stringValue(string $stringValue): DebugVariableValue + { return new DebugVariableValue([ 'type' => 'stringValue', 'value' => $stringValue, @@ -135,7 +139,8 @@ public static function stringValue(string $stringValue): DebugVariableValue { * @param string $charValue * @return DebugVariableValue */ - public static function charValue(string $charValue): DebugVariableValue { + public static function charValue(string $charValue): DebugVariableValue + { return new DebugVariableValue([ 'type' => 'charValue', 'value' => $charValue, @@ -146,7 +151,8 @@ public static function charValue(string $charValue): DebugVariableValue { * @param DebugMapValue $mapValue * @return DebugVariableValue */ - public static function mapValue(DebugMapValue $mapValue): DebugVariableValue { + public static function mapValue(DebugMapValue $mapValue): DebugVariableValue + { return new DebugVariableValue([ 'type' => 'mapValue', 'value' => $mapValue, @@ -157,7 +163,8 @@ public static function mapValue(DebugMapValue $mapValue): DebugVariableValue { * @param array $listValue * @return DebugVariableValue */ - public static function listValue(array $listValue): DebugVariableValue { + public static function listValue(array $listValue): DebugVariableValue + { return new DebugVariableValue([ 'type' => 'listValue', 'value' => $listValue, @@ -168,7 +175,8 @@ public static function listValue(array $listValue): DebugVariableValue { * @param BinaryTreeNodeAndTreeValue $binaryTreeNodeValue * @return DebugVariableValue */ - public static function binaryTreeNodeValue(BinaryTreeNodeAndTreeValue $binaryTreeNodeValue): DebugVariableValue { + public static function binaryTreeNodeValue(BinaryTreeNodeAndTreeValue $binaryTreeNodeValue): DebugVariableValue + { return new DebugVariableValue([ 'type' => 'binaryTreeNodeValue', 'value' => $binaryTreeNodeValue, @@ -179,7 +187,8 @@ public static function binaryTreeNodeValue(BinaryTreeNodeAndTreeValue $binaryTre * @param SinglyLinkedListNodeAndListValue $singlyLinkedListNodeValue * @return DebugVariableValue */ - public static function singlyLinkedListNodeValue(SinglyLinkedListNodeAndListValue $singlyLinkedListNodeValue): DebugVariableValue { + public static function singlyLinkedListNodeValue(SinglyLinkedListNodeAndListValue $singlyLinkedListNodeValue): DebugVariableValue + { return new DebugVariableValue([ 'type' => 'singlyLinkedListNodeValue', 'value' => $singlyLinkedListNodeValue, @@ -190,7 +199,8 @@ public static function singlyLinkedListNodeValue(SinglyLinkedListNodeAndListValu * @param DoublyLinkedListNodeAndListValue $doublyLinkedListNodeValue * @return DebugVariableValue */ - public static function doublyLinkedListNodeValue(DoublyLinkedListNodeAndListValue $doublyLinkedListNodeValue): DebugVariableValue { + public static function doublyLinkedListNodeValue(DoublyLinkedListNodeAndListValue $doublyLinkedListNodeValue): DebugVariableValue + { return new DebugVariableValue([ 'type' => 'doublyLinkedListNodeValue', 'value' => $doublyLinkedListNodeValue, @@ -200,7 +210,8 @@ public static function doublyLinkedListNodeValue(DoublyLinkedListNodeAndListValu /** * @return DebugVariableValue */ - public static function undefinedValue(): DebugVariableValue { + public static function undefinedValue(): DebugVariableValue + { return new DebugVariableValue([ 'type' => 'undefinedValue', 'value' => null, @@ -210,7 +221,8 @@ public static function undefinedValue(): DebugVariableValue { /** * @return DebugVariableValue */ - public static function nullValue(): DebugVariableValue { + public static function nullValue(): DebugVariableValue + { return new DebugVariableValue([ 'type' => 'nullValue', 'value' => null, @@ -221,7 +233,8 @@ public static function nullValue(): DebugVariableValue { * @param GenericValue $genericValue * @return DebugVariableValue */ - public static function genericValue(GenericValue $genericValue): DebugVariableValue { + public static function genericValue(GenericValue $genericValue): DebugVariableValue + { return new DebugVariableValue([ 'type' => 'genericValue', 'value' => $genericValue, @@ -231,255 +244,281 @@ public static function genericValue(GenericValue $genericValue): DebugVariableVa /** * @return bool */ - public function isIntegerValue(): bool { - return is_int($this->value)&& $this->type === 'integerValue'; + public function isIntegerValue(): bool + { + return is_int($this->value) && $this->type === 'integerValue'; } /** * @return int */ - public function asIntegerValue(): int { - if (!(is_int($this->value)&& $this->type === 'integerValue')){ + public function asIntegerValue(): int + { + if (!(is_int($this->value) && $this->type === 'integerValue')) { throw new Exception( "Expected integerValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isBooleanValue(): bool { - return is_bool($this->value)&& $this->type === 'booleanValue'; + public function isBooleanValue(): bool + { + return is_bool($this->value) && $this->type === 'booleanValue'; } /** * @return bool */ - public function asBooleanValue(): bool { - if (!(is_bool($this->value)&& $this->type === 'booleanValue')){ + public function asBooleanValue(): bool + { + if (!(is_bool($this->value) && $this->type === 'booleanValue')) { throw new Exception( "Expected booleanValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isDoubleValue(): bool { - return is_float($this->value)&& $this->type === 'doubleValue'; + public function isDoubleValue(): bool + { + return is_float($this->value) && $this->type === 'doubleValue'; } /** * @return float */ - public function asDoubleValue(): float { - if (!(is_float($this->value)&& $this->type === 'doubleValue')){ + public function asDoubleValue(): float + { + if (!(is_float($this->value) && $this->type === 'doubleValue')) { throw new Exception( "Expected doubleValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isStringValue(): bool { - return is_string($this->value)&& $this->type === 'stringValue'; + public function isStringValue(): bool + { + return is_string($this->value) && $this->type === 'stringValue'; } /** * @return string */ - public function asStringValue(): string { - if (!(is_string($this->value)&& $this->type === 'stringValue')){ + public function asStringValue(): string + { + if (!(is_string($this->value) && $this->type === 'stringValue')) { throw new Exception( "Expected stringValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isCharValue(): bool { - return is_string($this->value)&& $this->type === 'charValue'; + public function isCharValue(): bool + { + return is_string($this->value) && $this->type === 'charValue'; } /** * @return string */ - public function asCharValue(): string { - if (!(is_string($this->value)&& $this->type === 'charValue')){ + public function asCharValue(): string + { + if (!(is_string($this->value) && $this->type === 'charValue')) { throw new Exception( "Expected charValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isMapValue(): bool { - return $this->value instanceof DebugMapValue&& $this->type === 'mapValue'; + public function isMapValue(): bool + { + return $this->value instanceof DebugMapValue && $this->type === 'mapValue'; } /** * @return DebugMapValue */ - public function asMapValue(): DebugMapValue { - if (!($this->value instanceof DebugMapValue&& $this->type === 'mapValue')){ + public function asMapValue(): DebugMapValue + { + if (!($this->value instanceof DebugMapValue && $this->type === 'mapValue')) { throw new Exception( "Expected mapValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isListValue(): bool { - return is_array($this->value)&& $this->type === 'listValue'; + public function isListValue(): bool + { + return is_array($this->value) && $this->type === 'listValue'; } /** * @return array */ - public function asListValue(): array { - if (!(is_array($this->value)&& $this->type === 'listValue')){ + public function asListValue(): array + { + if (!(is_array($this->value) && $this->type === 'listValue')) { throw new Exception( "Expected listValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isBinaryTreeNodeValue(): bool { - return $this->value instanceof BinaryTreeNodeAndTreeValue&& $this->type === 'binaryTreeNodeValue'; + public function isBinaryTreeNodeValue(): bool + { + return $this->value instanceof BinaryTreeNodeAndTreeValue && $this->type === 'binaryTreeNodeValue'; } /** * @return BinaryTreeNodeAndTreeValue */ - public function asBinaryTreeNodeValue(): BinaryTreeNodeAndTreeValue { - if (!($this->value instanceof BinaryTreeNodeAndTreeValue&& $this->type === 'binaryTreeNodeValue')){ + public function asBinaryTreeNodeValue(): BinaryTreeNodeAndTreeValue + { + if (!($this->value instanceof BinaryTreeNodeAndTreeValue && $this->type === 'binaryTreeNodeValue')) { throw new Exception( "Expected binaryTreeNodeValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isSinglyLinkedListNodeValue(): bool { - return $this->value instanceof SinglyLinkedListNodeAndListValue&& $this->type === 'singlyLinkedListNodeValue'; + public function isSinglyLinkedListNodeValue(): bool + { + return $this->value instanceof SinglyLinkedListNodeAndListValue && $this->type === 'singlyLinkedListNodeValue'; } /** * @return SinglyLinkedListNodeAndListValue */ - public function asSinglyLinkedListNodeValue(): SinglyLinkedListNodeAndListValue { - if (!($this->value instanceof SinglyLinkedListNodeAndListValue&& $this->type === 'singlyLinkedListNodeValue')){ + public function asSinglyLinkedListNodeValue(): SinglyLinkedListNodeAndListValue + { + if (!($this->value instanceof SinglyLinkedListNodeAndListValue && $this->type === 'singlyLinkedListNodeValue')) { throw new Exception( "Expected singlyLinkedListNodeValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isDoublyLinkedListNodeValue(): bool { - return $this->value instanceof DoublyLinkedListNodeAndListValue&& $this->type === 'doublyLinkedListNodeValue'; + public function isDoublyLinkedListNodeValue(): bool + { + return $this->value instanceof DoublyLinkedListNodeAndListValue && $this->type === 'doublyLinkedListNodeValue'; } /** * @return DoublyLinkedListNodeAndListValue */ - public function asDoublyLinkedListNodeValue(): DoublyLinkedListNodeAndListValue { - if (!($this->value instanceof DoublyLinkedListNodeAndListValue&& $this->type === 'doublyLinkedListNodeValue')){ + public function asDoublyLinkedListNodeValue(): DoublyLinkedListNodeAndListValue + { + if (!($this->value instanceof DoublyLinkedListNodeAndListValue && $this->type === 'doublyLinkedListNodeValue')) { throw new Exception( "Expected doublyLinkedListNodeValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isUndefinedValue(): bool { - return is_null($this->value)&& $this->type === 'undefinedValue'; + public function isUndefinedValue(): bool + { + return is_null($this->value) && $this->type === 'undefinedValue'; } /** * @return bool */ - public function isNullValue(): bool { - return is_null($this->value)&& $this->type === 'nullValue'; + public function isNullValue(): bool + { + return is_null($this->value) && $this->type === 'nullValue'; } /** * @return bool */ - public function isGenericValue(): bool { - return $this->value instanceof GenericValue&& $this->type === 'genericValue'; + public function isGenericValue(): bool + { + return $this->value instanceof GenericValue && $this->type === 'genericValue'; } /** * @return GenericValue */ - public function asGenericValue(): GenericValue { - if (!($this->value instanceof GenericValue&& $this->type === 'genericValue')){ + public function asGenericValue(): GenericValue + { + if (!($this->value instanceof GenericValue && $this->type === 'genericValue')) { throw new Exception( "Expected genericValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'integerValue': $value = $this->value; $result['integerValue'] = $value; @@ -532,26 +571,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -560,77 +600,78 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'integerValue': - if (!array_key_exists('integerValue', $data)){ + if (!array_key_exists('integerValue', $data)) { throw new Exception( "JSON data is missing property 'integerValue'", ); } - + $args['value'] = $data['integerValue']; break; case 'booleanValue': - if (!array_key_exists('booleanValue', $data)){ + if (!array_key_exists('booleanValue', $data)) { throw new Exception( "JSON data is missing property 'booleanValue'", ); } - + $args['value'] = $data['booleanValue']; break; case 'doubleValue': - if (!array_key_exists('doubleValue', $data)){ + if (!array_key_exists('doubleValue', $data)) { throw new Exception( "JSON data is missing property 'doubleValue'", ); } - + $args['value'] = $data['doubleValue']; break; case 'stringValue': - if (!array_key_exists('stringValue', $data)){ + if (!array_key_exists('stringValue', $data)) { throw new Exception( "JSON data is missing property 'stringValue'", ); } - + $args['value'] = $data['stringValue']; break; case 'charValue': - if (!array_key_exists('charValue', $data)){ + if (!array_key_exists('charValue', $data)) { throw new Exception( "JSON data is missing property 'charValue'", ); } - + $args['value'] = $data['charValue']; break; case 'mapValue': $args['value'] = DebugMapValue::jsonDeserialize($data); break; case 'listValue': - if (!array_key_exists('listValue', $data)){ + if (!array_key_exists('listValue', $data)) { throw new Exception( "JSON data is missing property 'listValue'", ); } - + $args['value'] = $data['listValue']; break; case 'binaryTreeNodeValue': @@ -656,7 +697,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/trace/src/Commons/Types/DoublyLinkedListNodeAndListValue.php b/seed/php-sdk/trace/src/Commons/Types/DoublyLinkedListNodeAndListValue.php index bc0a5e39f4ad..7403e7a69eb3 100644 --- a/seed/php-sdk/trace/src/Commons/Types/DoublyLinkedListNodeAndListValue.php +++ b/seed/php-sdk/trace/src/Commons/Types/DoublyLinkedListNodeAndListValue.php @@ -27,15 +27,16 @@ class DoublyLinkedListNodeAndListValue extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->nodeId = $values['nodeId'];$this->fullList = $values['fullList']; + ) { + $this->nodeId = $values['nodeId']; + $this->fullList = $values['fullList']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Commons/Types/DoublyLinkedListNodeValue.php b/seed/php-sdk/trace/src/Commons/Types/DoublyLinkedListNodeValue.php index 7190b8db2176..e7dff9c8d898 100644 --- a/seed/php-sdk/trace/src/Commons/Types/DoublyLinkedListNodeValue.php +++ b/seed/php-sdk/trace/src/Commons/Types/DoublyLinkedListNodeValue.php @@ -41,15 +41,18 @@ class DoublyLinkedListNodeValue extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->nodeId = $values['nodeId'];$this->val = $values['val'];$this->next = $values['next'] ?? null;$this->prev = $values['prev'] ?? null; + ) { + $this->nodeId = $values['nodeId']; + $this->val = $values['val']; + $this->next = $values['next'] ?? null; + $this->prev = $values['prev'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Commons/Types/DoublyLinkedListValue.php b/seed/php-sdk/trace/src/Commons/Types/DoublyLinkedListValue.php index ef0ed18d1f53..dc2b727ad252 100644 --- a/seed/php-sdk/trace/src/Commons/Types/DoublyLinkedListValue.php +++ b/seed/php-sdk/trace/src/Commons/Types/DoublyLinkedListValue.php @@ -28,15 +28,16 @@ class DoublyLinkedListValue extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->head = $values['head'] ?? null;$this->nodes = $values['nodes']; + ) { + $this->head = $values['head'] ?? null; + $this->nodes = $values['nodes']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Commons/Types/FileInfo.php b/seed/php-sdk/trace/src/Commons/Types/FileInfo.php index eef78392aad1..f697e6379ad2 100644 --- a/seed/php-sdk/trace/src/Commons/Types/FileInfo.php +++ b/seed/php-sdk/trace/src/Commons/Types/FileInfo.php @@ -27,15 +27,16 @@ class FileInfo extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->filename = $values['filename'];$this->contents = $values['contents']; + ) { + $this->filename = $values['filename']; + $this->contents = $values['contents']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Commons/Types/GenericValue.php b/seed/php-sdk/trace/src/Commons/Types/GenericValue.php index 727e39f7b3ce..6843b18357ea 100644 --- a/seed/php-sdk/trace/src/Commons/Types/GenericValue.php +++ b/seed/php-sdk/trace/src/Commons/Types/GenericValue.php @@ -27,15 +27,16 @@ class GenericValue extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->stringifiedType = $values['stringifiedType'] ?? null;$this->stringifiedValue = $values['stringifiedValue']; + ) { + $this->stringifiedType = $values['stringifiedType'] ?? null; + $this->stringifiedValue = $values['stringifiedValue']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Commons/Types/KeyValuePair.php b/seed/php-sdk/trace/src/Commons/Types/KeyValuePair.php index 465c7fa5f538..71cce5081f7d 100644 --- a/seed/php-sdk/trace/src/Commons/Types/KeyValuePair.php +++ b/seed/php-sdk/trace/src/Commons/Types/KeyValuePair.php @@ -27,15 +27,16 @@ class KeyValuePair extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->key = $values['key'];$this->value = $values['value']; + ) { + $this->key = $values['key']; + $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Commons/Types/Language.php b/seed/php-sdk/trace/src/Commons/Types/Language.php index e25c3e8d3bbf..d2be8ef9d4ec 100644 --- a/seed/php-sdk/trace/src/Commons/Types/Language.php +++ b/seed/php-sdk/trace/src/Commons/Types/Language.php @@ -2,8 +2,8 @@ namespace Seed\Commons\Types; -enum Language - : string { +enum Language: string +{ case Java = "JAVA"; case Javascript = "JAVASCRIPT"; case Python = "PYTHON"; diff --git a/seed/php-sdk/trace/src/Commons/Types/ListType.php b/seed/php-sdk/trace/src/Commons/Types/ListType.php index 744e35f82d64..d49970961e83 100644 --- a/seed/php-sdk/trace/src/Commons/Types/ListType.php +++ b/seed/php-sdk/trace/src/Commons/Types/ListType.php @@ -27,15 +27,16 @@ class ListType extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->valueType = $values['valueType'];$this->isFixedLength = $values['isFixedLength'] ?? null; + ) { + $this->valueType = $values['valueType']; + $this->isFixedLength = $values['isFixedLength'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Commons/Types/MapType.php b/seed/php-sdk/trace/src/Commons/Types/MapType.php index c96b942243c5..91c1d1e22729 100644 --- a/seed/php-sdk/trace/src/Commons/Types/MapType.php +++ b/seed/php-sdk/trace/src/Commons/Types/MapType.php @@ -27,15 +27,16 @@ class MapType extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->keyType = $values['keyType'];$this->valueType = $values['valueType']; + ) { + $this->keyType = $values['keyType']; + $this->valueType = $values['valueType']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Commons/Types/MapValue.php b/seed/php-sdk/trace/src/Commons/Types/MapValue.php index 6f3121f9881f..b537cea12a73 100644 --- a/seed/php-sdk/trace/src/Commons/Types/MapValue.php +++ b/seed/php-sdk/trace/src/Commons/Types/MapValue.php @@ -21,15 +21,15 @@ class MapValue extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->keyValuePairs = $values['keyValuePairs']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Commons/Types/SinglyLinkedListNodeAndListValue.php b/seed/php-sdk/trace/src/Commons/Types/SinglyLinkedListNodeAndListValue.php index 1c4b9e2b0ddd..8ae4e9507955 100644 --- a/seed/php-sdk/trace/src/Commons/Types/SinglyLinkedListNodeAndListValue.php +++ b/seed/php-sdk/trace/src/Commons/Types/SinglyLinkedListNodeAndListValue.php @@ -27,15 +27,16 @@ class SinglyLinkedListNodeAndListValue extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->nodeId = $values['nodeId'];$this->fullList = $values['fullList']; + ) { + $this->nodeId = $values['nodeId']; + $this->fullList = $values['fullList']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Commons/Types/SinglyLinkedListNodeValue.php b/seed/php-sdk/trace/src/Commons/Types/SinglyLinkedListNodeValue.php index 700c54f2dcc4..1415a9a80c42 100644 --- a/seed/php-sdk/trace/src/Commons/Types/SinglyLinkedListNodeValue.php +++ b/seed/php-sdk/trace/src/Commons/Types/SinglyLinkedListNodeValue.php @@ -34,15 +34,17 @@ class SinglyLinkedListNodeValue extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->nodeId = $values['nodeId'];$this->val = $values['val'];$this->next = $values['next'] ?? null; + ) { + $this->nodeId = $values['nodeId']; + $this->val = $values['val']; + $this->next = $values['next'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Commons/Types/SinglyLinkedListValue.php b/seed/php-sdk/trace/src/Commons/Types/SinglyLinkedListValue.php index 7e1d17c56969..45d75850f1bc 100644 --- a/seed/php-sdk/trace/src/Commons/Types/SinglyLinkedListValue.php +++ b/seed/php-sdk/trace/src/Commons/Types/SinglyLinkedListValue.php @@ -28,15 +28,16 @@ class SinglyLinkedListValue extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->head = $values['head'] ?? null;$this->nodes = $values['nodes']; + ) { + $this->head = $values['head'] ?? null; + $this->nodes = $values['nodes']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Commons/Types/TestCase.php b/seed/php-sdk/trace/src/Commons/Types/TestCase.php index 75b1fe33ca5c..656f67f36469 100644 --- a/seed/php-sdk/trace/src/Commons/Types/TestCase.php +++ b/seed/php-sdk/trace/src/Commons/Types/TestCase.php @@ -28,15 +28,16 @@ class TestCase extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->params = $values['params']; + ) { + $this->id = $values['id']; + $this->params = $values['params']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Commons/Types/TestCaseWithExpectedResult.php b/seed/php-sdk/trace/src/Commons/Types/TestCaseWithExpectedResult.php index 621611deba5b..9180a158b65a 100644 --- a/seed/php-sdk/trace/src/Commons/Types/TestCaseWithExpectedResult.php +++ b/seed/php-sdk/trace/src/Commons/Types/TestCaseWithExpectedResult.php @@ -27,15 +27,16 @@ class TestCaseWithExpectedResult extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->testCase = $values['testCase'];$this->expectedResult = $values['expectedResult']; + ) { + $this->testCase = $values['testCase']; + $this->expectedResult = $values['expectedResult']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Commons/Types/VariableType.php b/seed/php-sdk/trace/src/Commons/Types/VariableType.php index 39c002156322..e53086b39c66 100644 --- a/seed/php-sdk/trace/src/Commons/Types/VariableType.php +++ b/seed/php-sdk/trace/src/Commons/Types/VariableType.php @@ -60,15 +60,16 @@ class VariableType extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @return VariableType */ - public static function integerType(): VariableType { + public static function integerType(): VariableType + { return new VariableType([ 'type' => 'integerType', 'value' => null, @@ -78,7 +79,8 @@ public static function integerType(): VariableType { /** * @return VariableType */ - public static function doubleType(): VariableType { + public static function doubleType(): VariableType + { return new VariableType([ 'type' => 'doubleType', 'value' => null, @@ -88,7 +90,8 @@ public static function doubleType(): VariableType { /** * @return VariableType */ - public static function booleanType(): VariableType { + public static function booleanType(): VariableType + { return new VariableType([ 'type' => 'booleanType', 'value' => null, @@ -98,7 +101,8 @@ public static function booleanType(): VariableType { /** * @return VariableType */ - public static function stringType(): VariableType { + public static function stringType(): VariableType + { return new VariableType([ 'type' => 'stringType', 'value' => null, @@ -108,7 +112,8 @@ public static function stringType(): VariableType { /** * @return VariableType */ - public static function charType(): VariableType { + public static function charType(): VariableType + { return new VariableType([ 'type' => 'charType', 'value' => null, @@ -119,7 +124,8 @@ public static function charType(): VariableType { * @param ListType $listType * @return VariableType */ - public static function listType(ListType $listType): VariableType { + public static function listType(ListType $listType): VariableType + { return new VariableType([ 'type' => 'listType', 'value' => $listType, @@ -130,7 +136,8 @@ public static function listType(ListType $listType): VariableType { * @param MapType $mapType * @return VariableType */ - public static function mapType(MapType $mapType): VariableType { + public static function mapType(MapType $mapType): VariableType + { return new VariableType([ 'type' => 'mapType', 'value' => $mapType, @@ -140,7 +147,8 @@ public static function mapType(MapType $mapType): VariableType { /** * @return VariableType */ - public static function binaryTreeType(): VariableType { + public static function binaryTreeType(): VariableType + { return new VariableType([ 'type' => 'binaryTreeType', 'value' => null, @@ -150,7 +158,8 @@ public static function binaryTreeType(): VariableType { /** * @return VariableType */ - public static function singlyLinkedListType(): VariableType { + public static function singlyLinkedListType(): VariableType + { return new VariableType([ 'type' => 'singlyLinkedListType', 'value' => null, @@ -160,7 +169,8 @@ public static function singlyLinkedListType(): VariableType { /** * @return VariableType */ - public static function doublyLinkedListType(): VariableType { + public static function doublyLinkedListType(): VariableType + { return new VariableType([ 'type' => 'doublyLinkedListType', 'value' => null, @@ -170,117 +180,131 @@ public static function doublyLinkedListType(): VariableType { /** * @return bool */ - public function isIntegerType(): bool { - return is_null($this->value)&& $this->type === 'integerType'; + public function isIntegerType(): bool + { + return is_null($this->value) && $this->type === 'integerType'; } /** * @return bool */ - public function isDoubleType(): bool { - return is_null($this->value)&& $this->type === 'doubleType'; + public function isDoubleType(): bool + { + return is_null($this->value) && $this->type === 'doubleType'; } /** * @return bool */ - public function isBooleanType(): bool { - return is_null($this->value)&& $this->type === 'booleanType'; + public function isBooleanType(): bool + { + return is_null($this->value) && $this->type === 'booleanType'; } /** * @return bool */ - public function isStringType(): bool { - return is_null($this->value)&& $this->type === 'stringType'; + public function isStringType(): bool + { + return is_null($this->value) && $this->type === 'stringType'; } /** * @return bool */ - public function isCharType(): bool { - return is_null($this->value)&& $this->type === 'charType'; + public function isCharType(): bool + { + return is_null($this->value) && $this->type === 'charType'; } /** * @return bool */ - public function isListType(): bool { - return $this->value instanceof ListType&& $this->type === 'listType'; + public function isListType(): bool + { + return $this->value instanceof ListType && $this->type === 'listType'; } /** * @return ListType */ - public function asListType(): ListType { - if (!($this->value instanceof ListType&& $this->type === 'listType')){ + public function asListType(): ListType + { + if (!($this->value instanceof ListType && $this->type === 'listType')) { throw new Exception( "Expected listType; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isMapType(): bool { - return $this->value instanceof MapType&& $this->type === 'mapType'; + public function isMapType(): bool + { + return $this->value instanceof MapType && $this->type === 'mapType'; } /** * @return MapType */ - public function asMapType(): MapType { - if (!($this->value instanceof MapType&& $this->type === 'mapType')){ + public function asMapType(): MapType + { + if (!($this->value instanceof MapType && $this->type === 'mapType')) { throw new Exception( "Expected mapType; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isBinaryTreeType(): bool { - return is_null($this->value)&& $this->type === 'binaryTreeType'; + public function isBinaryTreeType(): bool + { + return is_null($this->value) && $this->type === 'binaryTreeType'; } /** * @return bool */ - public function isSinglyLinkedListType(): bool { - return is_null($this->value)&& $this->type === 'singlyLinkedListType'; + public function isSinglyLinkedListType(): bool + { + return is_null($this->value) && $this->type === 'singlyLinkedListType'; } /** * @return bool */ - public function isDoublyLinkedListType(): bool { - return is_null($this->value)&& $this->type === 'doublyLinkedListType'; + public function isDoublyLinkedListType(): bool + { + return is_null($this->value) && $this->type === 'doublyLinkedListType'; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'integerType': $result['integerType'] = []; break; @@ -315,26 +339,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -343,22 +368,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'integerType': $args['value'] = null; break; @@ -394,7 +420,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/trace/src/Commons/Types/VariableValue.php b/seed/php-sdk/trace/src/Commons/Types/VariableValue.php index bfa9e25d9dcc..ecdcde42e9a9 100644 --- a/seed/php-sdk/trace/src/Commons/Types/VariableValue.php +++ b/seed/php-sdk/trace/src/Commons/Types/VariableValue.php @@ -76,16 +76,17 @@ class VariableValue extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param int $integerValue * @return VariableValue */ - public static function integerValue(int $integerValue): VariableValue { + public static function integerValue(int $integerValue): VariableValue + { return new VariableValue([ 'type' => 'integerValue', 'value' => $integerValue, @@ -96,7 +97,8 @@ public static function integerValue(int $integerValue): VariableValue { * @param bool $booleanValue * @return VariableValue */ - public static function booleanValue(bool $booleanValue): VariableValue { + public static function booleanValue(bool $booleanValue): VariableValue + { return new VariableValue([ 'type' => 'booleanValue', 'value' => $booleanValue, @@ -107,7 +109,8 @@ public static function booleanValue(bool $booleanValue): VariableValue { * @param float $doubleValue * @return VariableValue */ - public static function doubleValue(float $doubleValue): VariableValue { + public static function doubleValue(float $doubleValue): VariableValue + { return new VariableValue([ 'type' => 'doubleValue', 'value' => $doubleValue, @@ -118,7 +121,8 @@ public static function doubleValue(float $doubleValue): VariableValue { * @param string $stringValue * @return VariableValue */ - public static function stringValue(string $stringValue): VariableValue { + public static function stringValue(string $stringValue): VariableValue + { return new VariableValue([ 'type' => 'stringValue', 'value' => $stringValue, @@ -129,7 +133,8 @@ public static function stringValue(string $stringValue): VariableValue { * @param string $charValue * @return VariableValue */ - public static function charValue(string $charValue): VariableValue { + public static function charValue(string $charValue): VariableValue + { return new VariableValue([ 'type' => 'charValue', 'value' => $charValue, @@ -140,7 +145,8 @@ public static function charValue(string $charValue): VariableValue { * @param MapValue $mapValue * @return VariableValue */ - public static function mapValue(MapValue $mapValue): VariableValue { + public static function mapValue(MapValue $mapValue): VariableValue + { return new VariableValue([ 'type' => 'mapValue', 'value' => $mapValue, @@ -151,7 +157,8 @@ public static function mapValue(MapValue $mapValue): VariableValue { * @param array $listValue * @return VariableValue */ - public static function listValue(array $listValue): VariableValue { + public static function listValue(array $listValue): VariableValue + { return new VariableValue([ 'type' => 'listValue', 'value' => $listValue, @@ -162,7 +169,8 @@ public static function listValue(array $listValue): VariableValue { * @param BinaryTreeValue $binaryTreeValue * @return VariableValue */ - public static function binaryTreeValue(BinaryTreeValue $binaryTreeValue): VariableValue { + public static function binaryTreeValue(BinaryTreeValue $binaryTreeValue): VariableValue + { return new VariableValue([ 'type' => 'binaryTreeValue', 'value' => $binaryTreeValue, @@ -173,7 +181,8 @@ public static function binaryTreeValue(BinaryTreeValue $binaryTreeValue): Variab * @param SinglyLinkedListValue $singlyLinkedListValue * @return VariableValue */ - public static function singlyLinkedListValue(SinglyLinkedListValue $singlyLinkedListValue): VariableValue { + public static function singlyLinkedListValue(SinglyLinkedListValue $singlyLinkedListValue): VariableValue + { return new VariableValue([ 'type' => 'singlyLinkedListValue', 'value' => $singlyLinkedListValue, @@ -184,7 +193,8 @@ public static function singlyLinkedListValue(SinglyLinkedListValue $singlyLinked * @param DoublyLinkedListValue $doublyLinkedListValue * @return VariableValue */ - public static function doublyLinkedListValue(DoublyLinkedListValue $doublyLinkedListValue): VariableValue { + public static function doublyLinkedListValue(DoublyLinkedListValue $doublyLinkedListValue): VariableValue + { return new VariableValue([ 'type' => 'doublyLinkedListValue', 'value' => $doublyLinkedListValue, @@ -194,7 +204,8 @@ public static function doublyLinkedListValue(DoublyLinkedListValue $doublyLinked /** * @return VariableValue */ - public static function nullValue(): VariableValue { + public static function nullValue(): VariableValue + { return new VariableValue([ 'type' => 'nullValue', 'value' => null, @@ -204,228 +215,251 @@ public static function nullValue(): VariableValue { /** * @return bool */ - public function isIntegerValue(): bool { - return is_int($this->value)&& $this->type === 'integerValue'; + public function isIntegerValue(): bool + { + return is_int($this->value) && $this->type === 'integerValue'; } /** * @return int */ - public function asIntegerValue(): int { - if (!(is_int($this->value)&& $this->type === 'integerValue')){ + public function asIntegerValue(): int + { + if (!(is_int($this->value) && $this->type === 'integerValue')) { throw new Exception( "Expected integerValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isBooleanValue(): bool { - return is_bool($this->value)&& $this->type === 'booleanValue'; + public function isBooleanValue(): bool + { + return is_bool($this->value) && $this->type === 'booleanValue'; } /** * @return bool */ - public function asBooleanValue(): bool { - if (!(is_bool($this->value)&& $this->type === 'booleanValue')){ + public function asBooleanValue(): bool + { + if (!(is_bool($this->value) && $this->type === 'booleanValue')) { throw new Exception( "Expected booleanValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isDoubleValue(): bool { - return is_float($this->value)&& $this->type === 'doubleValue'; + public function isDoubleValue(): bool + { + return is_float($this->value) && $this->type === 'doubleValue'; } /** * @return float */ - public function asDoubleValue(): float { - if (!(is_float($this->value)&& $this->type === 'doubleValue')){ + public function asDoubleValue(): float + { + if (!(is_float($this->value) && $this->type === 'doubleValue')) { throw new Exception( "Expected doubleValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isStringValue(): bool { - return is_string($this->value)&& $this->type === 'stringValue'; + public function isStringValue(): bool + { + return is_string($this->value) && $this->type === 'stringValue'; } /** * @return string */ - public function asStringValue(): string { - if (!(is_string($this->value)&& $this->type === 'stringValue')){ + public function asStringValue(): string + { + if (!(is_string($this->value) && $this->type === 'stringValue')) { throw new Exception( "Expected stringValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isCharValue(): bool { - return is_string($this->value)&& $this->type === 'charValue'; + public function isCharValue(): bool + { + return is_string($this->value) && $this->type === 'charValue'; } /** * @return string */ - public function asCharValue(): string { - if (!(is_string($this->value)&& $this->type === 'charValue')){ + public function asCharValue(): string + { + if (!(is_string($this->value) && $this->type === 'charValue')) { throw new Exception( "Expected charValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isMapValue(): bool { - return $this->value instanceof MapValue&& $this->type === 'mapValue'; + public function isMapValue(): bool + { + return $this->value instanceof MapValue && $this->type === 'mapValue'; } /** * @return MapValue */ - public function asMapValue(): MapValue { - if (!($this->value instanceof MapValue&& $this->type === 'mapValue')){ + public function asMapValue(): MapValue + { + if (!($this->value instanceof MapValue && $this->type === 'mapValue')) { throw new Exception( "Expected mapValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isListValue(): bool { - return is_array($this->value)&& $this->type === 'listValue'; + public function isListValue(): bool + { + return is_array($this->value) && $this->type === 'listValue'; } /** * @return array */ - public function asListValue(): array { - if (!(is_array($this->value)&& $this->type === 'listValue')){ + public function asListValue(): array + { + if (!(is_array($this->value) && $this->type === 'listValue')) { throw new Exception( "Expected listValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isBinaryTreeValue(): bool { - return $this->value instanceof BinaryTreeValue&& $this->type === 'binaryTreeValue'; + public function isBinaryTreeValue(): bool + { + return $this->value instanceof BinaryTreeValue && $this->type === 'binaryTreeValue'; } /** * @return BinaryTreeValue */ - public function asBinaryTreeValue(): BinaryTreeValue { - if (!($this->value instanceof BinaryTreeValue&& $this->type === 'binaryTreeValue')){ + public function asBinaryTreeValue(): BinaryTreeValue + { + if (!($this->value instanceof BinaryTreeValue && $this->type === 'binaryTreeValue')) { throw new Exception( "Expected binaryTreeValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isSinglyLinkedListValue(): bool { - return $this->value instanceof SinglyLinkedListValue&& $this->type === 'singlyLinkedListValue'; + public function isSinglyLinkedListValue(): bool + { + return $this->value instanceof SinglyLinkedListValue && $this->type === 'singlyLinkedListValue'; } /** * @return SinglyLinkedListValue */ - public function asSinglyLinkedListValue(): SinglyLinkedListValue { - if (!($this->value instanceof SinglyLinkedListValue&& $this->type === 'singlyLinkedListValue')){ + public function asSinglyLinkedListValue(): SinglyLinkedListValue + { + if (!($this->value instanceof SinglyLinkedListValue && $this->type === 'singlyLinkedListValue')) { throw new Exception( "Expected singlyLinkedListValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isDoublyLinkedListValue(): bool { - return $this->value instanceof DoublyLinkedListValue&& $this->type === 'doublyLinkedListValue'; + public function isDoublyLinkedListValue(): bool + { + return $this->value instanceof DoublyLinkedListValue && $this->type === 'doublyLinkedListValue'; } /** * @return DoublyLinkedListValue */ - public function asDoublyLinkedListValue(): DoublyLinkedListValue { - if (!($this->value instanceof DoublyLinkedListValue&& $this->type === 'doublyLinkedListValue')){ + public function asDoublyLinkedListValue(): DoublyLinkedListValue + { + if (!($this->value instanceof DoublyLinkedListValue && $this->type === 'doublyLinkedListValue')) { throw new Exception( "Expected doublyLinkedListValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isNullValue(): bool { - return is_null($this->value)&& $this->type === 'nullValue'; + public function isNullValue(): bool + { + return is_null($this->value) && $this->type === 'nullValue'; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'integerValue': $value = $this->value; $result['integerValue'] = $value; @@ -471,26 +505,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -499,77 +534,78 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'integerValue': - if (!array_key_exists('integerValue', $data)){ + if (!array_key_exists('integerValue', $data)) { throw new Exception( "JSON data is missing property 'integerValue'", ); } - + $args['value'] = $data['integerValue']; break; case 'booleanValue': - if (!array_key_exists('booleanValue', $data)){ + if (!array_key_exists('booleanValue', $data)) { throw new Exception( "JSON data is missing property 'booleanValue'", ); } - + $args['value'] = $data['booleanValue']; break; case 'doubleValue': - if (!array_key_exists('doubleValue', $data)){ + if (!array_key_exists('doubleValue', $data)) { throw new Exception( "JSON data is missing property 'doubleValue'", ); } - + $args['value'] = $data['doubleValue']; break; case 'stringValue': - if (!array_key_exists('stringValue', $data)){ + if (!array_key_exists('stringValue', $data)) { throw new Exception( "JSON data is missing property 'stringValue'", ); } - + $args['value'] = $data['stringValue']; break; case 'charValue': - if (!array_key_exists('charValue', $data)){ + if (!array_key_exists('charValue', $data)) { throw new Exception( "JSON data is missing property 'charValue'", ); } - + $args['value'] = $data['charValue']; break; case 'mapValue': $args['value'] = MapValue::jsonDeserialize($data); break; case 'listValue': - if (!array_key_exists('listValue', $data)){ + if (!array_key_exists('listValue', $data)) { throw new Exception( "JSON data is missing property 'listValue'", ); } - + $args['value'] = $data['listValue']; break; case 'binaryTreeValue': @@ -589,7 +625,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/trace/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/trace/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/trace/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/trace/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/trace/src/Core/Client/RawClient.php b/seed/php-sdk/trace/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/trace/src/Core/Client/RawClient.php +++ b/seed/php-sdk/trace/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/trace/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/trace/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/trace/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/trace/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/trace/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/trace/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/trace/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/trace/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/trace/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/trace/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/trace/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/trace/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/trace/src/Core/Types/ArrayType.php b/seed/php-sdk/trace/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/trace/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/trace/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/trace/src/Core/Types/Constant.php b/seed/php-sdk/trace/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/trace/src/Core/Types/Constant.php +++ b/seed/php-sdk/trace/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/trace/src/Core/Types/Union.php b/seed/php-sdk/trace/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/trace/src/Core/Types/Union.php +++ b/seed/php-sdk/trace/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/trace/src/Environments.php b/seed/php-sdk/trace/src/Environments.php index 28ce881166ae..b75ab54a6de4 100644 --- a/seed/php-sdk/trace/src/Environments.php +++ b/seed/php-sdk/trace/src/Environments.php @@ -2,7 +2,7 @@ namespace Seed; -enum Environments - : string { +enum Environments: string +{ case Prod = "https://api.trace.come"; } diff --git a/seed/php-sdk/trace/src/Exceptions/SeedApiException.php b/seed/php-sdk/trace/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/trace/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/trace/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/trace/src/Exceptions/SeedException.php b/seed/php-sdk/trace/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/trace/src/Exceptions/SeedException.php +++ b/seed/php-sdk/trace/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/trace/src/Homepage/HomepageClient.php b/seed/php-sdk/trace/src/Homepage/HomepageClient.php index 9844701bdd76..f365271479ab 100644 --- a/seed/php-sdk/trace/src/Homepage/HomepageClient.php +++ b/seed/php-sdk/trace/src/Homepage/HomepageClient.php @@ -15,7 +15,7 @@ use Psr\Http\Client\ClientExceptionInterface; use Seed\Core\Json\JsonSerializer; -class HomepageClient +class HomepageClient { /** * @var array{ @@ -43,11 +43,10 @@ class HomepageClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -65,7 +64,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getHomepageProblems(?array $options = null): array { + public function getHomepageProblems(?array $options = null): array + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -77,15 +77,15 @@ public function getHomepageProblems(?array $options = null): array { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, ['string']); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -116,7 +116,8 @@ public function getHomepageProblems(?array $options = null): array { * @throws SeedException * @throws SeedApiException */ - public function setHomepageProblems(array $request, ?array $options = null): void { + public function setHomepageProblems(array $request, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -129,12 +130,12 @@ public function setHomepageProblems(array $request, ?array $options = null): voi $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/trace/src/LangServer/Types/LangServerRequest.php b/seed/php-sdk/trace/src/LangServer/Types/LangServerRequest.php index 0511e47f2dc4..992f5c590881 100644 --- a/seed/php-sdk/trace/src/LangServer/Types/LangServerRequest.php +++ b/seed/php-sdk/trace/src/LangServer/Types/LangServerRequest.php @@ -20,15 +20,15 @@ class LangServerRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->request = $values['request']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/LangServer/Types/LangServerResponse.php b/seed/php-sdk/trace/src/LangServer/Types/LangServerResponse.php index 3ddfbe76dab7..3f931d3551ee 100644 --- a/seed/php-sdk/trace/src/LangServer/Types/LangServerResponse.php +++ b/seed/php-sdk/trace/src/LangServer/Types/LangServerResponse.php @@ -20,15 +20,15 @@ class LangServerResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->response = $values['response']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Migration/MigrationClient.php b/seed/php-sdk/trace/src/Migration/MigrationClient.php index d1491b37f82b..2efc36c24d4b 100644 --- a/seed/php-sdk/trace/src/Migration/MigrationClient.php +++ b/seed/php-sdk/trace/src/Migration/MigrationClient.php @@ -16,7 +16,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class MigrationClient +class MigrationClient { /** * @var array{ @@ -44,11 +44,10 @@ class MigrationClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -67,7 +66,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getAttemptedMigrations(GetAttemptedMigrationsRequest $request, ?array $options = null): array { + public function getAttemptedMigrations(GetAttemptedMigrationsRequest $request, ?array $options = null): array + { $options = array_merge($this->options, $options ?? []); $headers = []; $headers['admin-key-header'] = $request->adminKeyHeader; @@ -82,15 +82,15 @@ public function getAttemptedMigrations(GetAttemptedMigrationsRequest $request, ? $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, [Migration::class]); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/trace/src/Migration/Requests/GetAttemptedMigrationsRequest.php b/seed/php-sdk/trace/src/Migration/Requests/GetAttemptedMigrationsRequest.php index 2149270135ab..8e418d184f21 100644 --- a/seed/php-sdk/trace/src/Migration/Requests/GetAttemptedMigrationsRequest.php +++ b/seed/php-sdk/trace/src/Migration/Requests/GetAttemptedMigrationsRequest.php @@ -18,8 +18,7 @@ class GetAttemptedMigrationsRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->adminKeyHeader = $values['adminKeyHeader']; } } diff --git a/seed/php-sdk/trace/src/Migration/Types/Migration.php b/seed/php-sdk/trace/src/Migration/Types/Migration.php index 35f28294eab0..7bba233d1798 100644 --- a/seed/php-sdk/trace/src/Migration/Types/Migration.php +++ b/seed/php-sdk/trace/src/Migration/Types/Migration.php @@ -27,15 +27,16 @@ class Migration extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->status = $values['status']; + ) { + $this->name = $values['name']; + $this->status = $values['status']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Migration/Types/MigrationStatus.php b/seed/php-sdk/trace/src/Migration/Types/MigrationStatus.php index 394757eccb34..a0a4118a6e35 100644 --- a/seed/php-sdk/trace/src/Migration/Types/MigrationStatus.php +++ b/seed/php-sdk/trace/src/Migration/Types/MigrationStatus.php @@ -2,8 +2,8 @@ namespace Seed\Migration\Types; -enum MigrationStatus - : string { +enum MigrationStatus: string +{ case Running = "RUNNING"; case Failed = "FAILED"; case Finished = "FINISHED"; diff --git a/seed/php-sdk/trace/src/Playlist/PlaylistClient.php b/seed/php-sdk/trace/src/Playlist/PlaylistClient.php index 914021fdc950..cd51e68fe56f 100644 --- a/seed/php-sdk/trace/src/Playlist/PlaylistClient.php +++ b/seed/php-sdk/trace/src/Playlist/PlaylistClient.php @@ -19,7 +19,7 @@ use Seed\Core\Json\JsonDecoder; use Seed\Playlist\Types\UpdatePlaylistRequest; -class PlaylistClient +class PlaylistClient { /** * @var array{ @@ -47,11 +47,10 @@ class PlaylistClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -73,11 +72,12 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function createPlaylist(int $serviceParam, CreatePlaylistRequest $request, ?array $options = null): Playlist { + public function createPlaylist(int $serviceParam, CreatePlaylistRequest $request, ?array $options = null): Playlist + { $options = array_merge($this->options, $options ?? []); $query = []; $query['datetime'] = JsonSerializer::serializeDateTime($request->datetime); - if ($request->optionalDatetime != null){ + if ($request->optionalDatetime != null) { $query['optionalDatetime'] = JsonSerializer::serializeDateTime($request->optionalDatetime); } try { @@ -92,15 +92,15 @@ public function createPlaylist(int $serviceParam, CreatePlaylistRequest $request $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return Playlist::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -135,16 +135,17 @@ public function createPlaylist(int $serviceParam, CreatePlaylistRequest $request * @throws SeedException * @throws SeedApiException */ - public function getPlaylists(int $serviceParam, GetPlaylistsRequest $request, ?array $options = null): array { + public function getPlaylists(int $serviceParam, GetPlaylistsRequest $request, ?array $options = null): array + { $options = array_merge($this->options, $options ?? []); $query = []; $query['otherField'] = $request->otherField; $query['multiLineDocs'] = $request->multiLineDocs; $query['multipleField'] = $request->multipleField; - if ($request->limit != null){ + if ($request->limit != null) { $query['limit'] = $request->limit; } - if ($request->optionalMultipleField != null){ + if ($request->optionalMultipleField != null) { $query['optionalMultipleField'] = $request->optionalMultipleField; } try { @@ -158,15 +159,15 @@ public function getPlaylists(int $serviceParam, GetPlaylistsRequest $request, ?a $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, [Playlist::class]); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -201,7 +202,8 @@ public function getPlaylists(int $serviceParam, GetPlaylistsRequest $request, ?a * @throws SeedException * @throws SeedApiException */ - public function getPlaylist(int $serviceParam, string $playlistId, ?array $options = null): Playlist { + public function getPlaylist(int $serviceParam, string $playlistId, ?array $options = null): Playlist + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -213,15 +215,15 @@ public function getPlaylist(int $serviceParam, string $playlistId, ?array $optio $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return Playlist::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -257,7 +259,8 @@ public function getPlaylist(int $serviceParam, string $playlistId, ?array $optio * @throws SeedException * @throws SeedApiException */ - public function updatePlaylist(int $serviceParam, string $playlistId, ?UpdatePlaylistRequest $request = null, ?array $options = null): ?Playlist { + public function updatePlaylist(int $serviceParam, string $playlistId, ?UpdatePlaylistRequest $request = null, ?array $options = null): ?Playlist + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -270,18 +273,18 @@ public function updatePlaylist(int $serviceParam, string $playlistId, ?UpdatePla $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); - if (empty($json)){ + if (empty($json)) { return null; } return Playlist::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -315,7 +318,8 @@ public function updatePlaylist(int $serviceParam, string $playlistId, ?UpdatePla * @throws SeedException * @throws SeedApiException */ - public function deletePlaylist(int $serviceParam, string $playlistId, ?array $options = null): void { + public function deletePlaylist(int $serviceParam, string $playlistId, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -327,12 +331,12 @@ public function deletePlaylist(int $serviceParam, string $playlistId, ?array $op $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/trace/src/Playlist/Requests/CreatePlaylistRequest.php b/seed/php-sdk/trace/src/Playlist/Requests/CreatePlaylistRequest.php index e3e3b3fb078c..dcb7ac45d136 100644 --- a/seed/php-sdk/trace/src/Playlist/Requests/CreatePlaylistRequest.php +++ b/seed/php-sdk/trace/src/Playlist/Requests/CreatePlaylistRequest.php @@ -32,8 +32,9 @@ class CreatePlaylistRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->datetime = $values['datetime'];$this->optionalDatetime = $values['optionalDatetime'] ?? null;$this->body = $values['body']; + ) { + $this->datetime = $values['datetime']; + $this->optionalDatetime = $values['optionalDatetime'] ?? null; + $this->body = $values['body']; } } diff --git a/seed/php-sdk/trace/src/Playlist/Requests/GetPlaylistsRequest.php b/seed/php-sdk/trace/src/Playlist/Requests/GetPlaylistsRequest.php index eef0ba6575d2..25574c0288cb 100644 --- a/seed/php-sdk/trace/src/Playlist/Requests/GetPlaylistsRequest.php +++ b/seed/php-sdk/trace/src/Playlist/Requests/GetPlaylistsRequest.php @@ -45,8 +45,11 @@ class GetPlaylistsRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->limit = $values['limit'] ?? null;$this->otherField = $values['otherField'];$this->multiLineDocs = $values['multiLineDocs'];$this->optionalMultipleField = $values['optionalMultipleField'] ?? null;$this->multipleField = $values['multipleField']; + ) { + $this->limit = $values['limit'] ?? null; + $this->otherField = $values['otherField']; + $this->multiLineDocs = $values['multiLineDocs']; + $this->optionalMultipleField = $values['optionalMultipleField'] ?? null; + $this->multipleField = $values['multipleField']; } } diff --git a/seed/php-sdk/trace/src/Playlist/Traits/PlaylistCreateRequest.php b/seed/php-sdk/trace/src/Playlist/Traits/PlaylistCreateRequest.php index 92344af598e6..782f7f4a3755 100644 --- a/seed/php-sdk/trace/src/Playlist/Traits/PlaylistCreateRequest.php +++ b/seed/php-sdk/trace/src/Playlist/Traits/PlaylistCreateRequest.php @@ -9,7 +9,7 @@ * @property string $name * @property array $problems */ -trait PlaylistCreateRequest +trait PlaylistCreateRequest { /** * @var string $name diff --git a/seed/php-sdk/trace/src/Playlist/Types/Playlist.php b/seed/php-sdk/trace/src/Playlist/Types/Playlist.php index 3d5936b5a363..b5ea8ffa197d 100644 --- a/seed/php-sdk/trace/src/Playlist/Types/Playlist.php +++ b/seed/php-sdk/trace/src/Playlist/Types/Playlist.php @@ -32,15 +32,18 @@ class Playlist extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->problems = $values['problems'];$this->playlistId = $values['playlistId'];$this->ownerId = $values['ownerId']; + ) { + $this->name = $values['name']; + $this->problems = $values['problems']; + $this->playlistId = $values['playlistId']; + $this->ownerId = $values['ownerId']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Playlist/Types/PlaylistCreateRequest.php b/seed/php-sdk/trace/src/Playlist/Types/PlaylistCreateRequest.php index dadacd569e6b..dee799facb89 100644 --- a/seed/php-sdk/trace/src/Playlist/Types/PlaylistCreateRequest.php +++ b/seed/php-sdk/trace/src/Playlist/Types/PlaylistCreateRequest.php @@ -28,15 +28,16 @@ class PlaylistCreateRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->problems = $values['problems']; + ) { + $this->name = $values['name']; + $this->problems = $values['problems']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Playlist/Types/PlaylistIdNotFoundErrorBody.php b/seed/php-sdk/trace/src/Playlist/Types/PlaylistIdNotFoundErrorBody.php index a373165464b2..c86e69ea47f0 100644 --- a/seed/php-sdk/trace/src/Playlist/Types/PlaylistIdNotFoundErrorBody.php +++ b/seed/php-sdk/trace/src/Playlist/Types/PlaylistIdNotFoundErrorBody.php @@ -38,16 +38,17 @@ class PlaylistIdNotFoundErrorBody extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param string $playlistId * @return PlaylistIdNotFoundErrorBody */ - public static function playlistId(string $playlistId): PlaylistIdNotFoundErrorBody { + public static function playlistId(string $playlistId): PlaylistIdNotFoundErrorBody + { return new PlaylistIdNotFoundErrorBody([ 'type' => 'playlistId', 'value' => $playlistId, @@ -57,67 +58,72 @@ public static function playlistId(string $playlistId): PlaylistIdNotFoundErrorBo /** * @return bool */ - public function isPlaylistId(): bool { - return is_string($this->value)&& $this->type === 'playlistId'; + public function isPlaylistId(): bool + { + return is_string($this->value) && $this->type === 'playlistId'; } /** * @return string */ - public function asPlaylistId(): string { - if (!(is_string($this->value)&& $this->type === 'playlistId')){ + public function asPlaylistId(): string + { + if (!(is_string($this->value) && $this->type === 'playlistId')) { throw new Exception( "Expected playlistId; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'playlistId': $value = $this->value; $result['playlistId'] = $value; break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -126,29 +132,30 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'playlistId': - if (!array_key_exists('playlistId', $data)){ + if (!array_key_exists('playlistId', $data)) { throw new Exception( "JSON data is missing property 'playlistId'", ); } - + $args['value'] = $data['playlistId']; break; case '_unknown': @@ -156,7 +163,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/trace/src/Playlist/Types/ReservedKeywordEnum.php b/seed/php-sdk/trace/src/Playlist/Types/ReservedKeywordEnum.php index 8173b4eb4a3c..f12d5527854e 100644 --- a/seed/php-sdk/trace/src/Playlist/Types/ReservedKeywordEnum.php +++ b/seed/php-sdk/trace/src/Playlist/Types/ReservedKeywordEnum.php @@ -2,8 +2,8 @@ namespace Seed\Playlist\Types; -enum ReservedKeywordEnum - : string { +enum ReservedKeywordEnum: string +{ case Is = "is"; case As_ = "as"; } diff --git a/seed/php-sdk/trace/src/Playlist/Types/UpdatePlaylistRequest.php b/seed/php-sdk/trace/src/Playlist/Types/UpdatePlaylistRequest.php index e16594e193ab..c9132d0da1b7 100644 --- a/seed/php-sdk/trace/src/Playlist/Types/UpdatePlaylistRequest.php +++ b/seed/php-sdk/trace/src/Playlist/Types/UpdatePlaylistRequest.php @@ -28,15 +28,16 @@ class UpdatePlaylistRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->problems = $values['problems']; + ) { + $this->name = $values['name']; + $this->problems = $values['problems']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Problem/ProblemClient.php b/seed/php-sdk/trace/src/Problem/ProblemClient.php index cde48af17db1..ed6ac49fd5b2 100644 --- a/seed/php-sdk/trace/src/Problem/ProblemClient.php +++ b/seed/php-sdk/trace/src/Problem/ProblemClient.php @@ -18,7 +18,7 @@ use Seed\Problem\Requests\GetDefaultStarterFilesRequest; use Seed\Problem\Types\GetDefaultStarterFilesResponse; -class ProblemClient +class ProblemClient { /** * @var array{ @@ -46,11 +46,10 @@ class ProblemClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -71,7 +70,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function createProblem(CreateProblemRequest $request, ?array $options = null): CreateProblemResponse { + public function createProblem(CreateProblemRequest $request, ?array $options = null): CreateProblemResponse + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -84,15 +84,15 @@ public function createProblem(CreateProblemRequest $request, ?array $options = n $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return CreateProblemResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -127,7 +127,8 @@ public function createProblem(CreateProblemRequest $request, ?array $options = n * @throws SeedException * @throws SeedApiException */ - public function updateProblem(string $problemId, CreateProblemRequest $request, ?array $options = null): UpdateProblemResponse { + public function updateProblem(string $problemId, CreateProblemRequest $request, ?array $options = null): UpdateProblemResponse + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -140,15 +141,15 @@ public function updateProblem(string $problemId, CreateProblemRequest $request, $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return UpdateProblemResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -181,7 +182,8 @@ public function updateProblem(string $problemId, CreateProblemRequest $request, * @throws SeedException * @throws SeedApiException */ - public function deleteProblem(string $problemId, ?array $options = null): void { + public function deleteProblem(string $problemId, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -193,12 +195,12 @@ public function deleteProblem(string $problemId, ?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -232,7 +234,8 @@ public function deleteProblem(string $problemId, ?array $options = null): void { * @throws SeedException * @throws SeedApiException */ - public function getDefaultStarterFiles(GetDefaultStarterFilesRequest $request, ?array $options = null): GetDefaultStarterFilesResponse { + public function getDefaultStarterFiles(GetDefaultStarterFilesRequest $request, ?array $options = null): GetDefaultStarterFilesResponse + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -245,15 +248,15 @@ public function getDefaultStarterFiles(GetDefaultStarterFilesRequest $request, ? $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return GetDefaultStarterFilesResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/trace/src/Problem/Requests/GetDefaultStarterFilesRequest.php b/seed/php-sdk/trace/src/Problem/Requests/GetDefaultStarterFilesRequest.php index 0d3e7af460e7..59d976a147f2 100644 --- a/seed/php-sdk/trace/src/Problem/Requests/GetDefaultStarterFilesRequest.php +++ b/seed/php-sdk/trace/src/Problem/Requests/GetDefaultStarterFilesRequest.php @@ -44,8 +44,9 @@ class GetDefaultStarterFilesRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->inputParams = $values['inputParams'];$this->outputType = $values['outputType'];$this->methodName = $values['methodName']; + ) { + $this->inputParams = $values['inputParams']; + $this->outputType = $values['outputType']; + $this->methodName = $values['methodName']; } } diff --git a/seed/php-sdk/trace/src/Problem/Types/CreateProblemError.php b/seed/php-sdk/trace/src/Problem/Types/CreateProblemError.php index 86d89470ab2c..37af72692a11 100644 --- a/seed/php-sdk/trace/src/Problem/Types/CreateProblemError.php +++ b/seed/php-sdk/trace/src/Problem/Types/CreateProblemError.php @@ -38,16 +38,17 @@ class CreateProblemError extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->errorType = $values['errorType'];$this->value = $values['value']; + ) { + $this->errorType = $values['errorType']; + $this->value = $values['value']; } /** * @param GenericCreateProblemError $generic * @return CreateProblemError */ - public static function generic(GenericCreateProblemError $generic): CreateProblemError { + public static function generic(GenericCreateProblemError $generic): CreateProblemError + { return new CreateProblemError([ 'errorType' => 'generic', 'value' => $generic, @@ -57,67 +58,72 @@ public static function generic(GenericCreateProblemError $generic): CreateProble /** * @return bool */ - public function isGeneric(): bool { - return $this->value instanceof GenericCreateProblemError&& $this->errorType === 'generic'; + public function isGeneric(): bool + { + return $this->value instanceof GenericCreateProblemError && $this->errorType === 'generic'; } /** * @return GenericCreateProblemError */ - public function asGeneric(): GenericCreateProblemError { - if (!($this->value instanceof GenericCreateProblemError&& $this->errorType === 'generic')){ + public function asGeneric(): GenericCreateProblemError + { + if (!($this->value instanceof GenericCreateProblemError && $this->errorType === 'generic')) { throw new Exception( "Expected generic; got " . $this->errorType . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['_type'] = $this->errorType; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->errorType){ + + switch ($this->errorType) { case 'generic': $value = $this->asGeneric()->jsonSerialize(); $result = array_merge($value, $result); break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -126,22 +132,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('_type', $data)){ + if (!array_key_exists('_type', $data)) { throw new Exception( "JSON data is missing property '_type'", ); } $errorType = $data['_type']; - if (!(is_string($errorType))){ + if (!(is_string($errorType))) { throw new Exception( "Expected property 'errorType' in JSON data to be string, instead received " . get_debug_type($data['_type']), ); } - + $args['errorType'] = $errorType; - switch ($errorType){ + switch ($errorType) { case 'generic': $args['value'] = GenericCreateProblemError::jsonDeserialize($data); break; @@ -150,7 +157,7 @@ public static function jsonDeserialize(array $data): static { $args['errorType'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/trace/src/Problem/Types/CreateProblemRequest.php b/seed/php-sdk/trace/src/Problem/Types/CreateProblemRequest.php index 29aaba8298b5..7d8d13dba051 100644 --- a/seed/php-sdk/trace/src/Problem/Types/CreateProblemRequest.php +++ b/seed/php-sdk/trace/src/Problem/Types/CreateProblemRequest.php @@ -66,15 +66,21 @@ class CreateProblemRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->problemName = $values['problemName'];$this->problemDescription = $values['problemDescription'];$this->files = $values['files'];$this->inputParams = $values['inputParams'];$this->outputType = $values['outputType'];$this->testcases = $values['testcases'];$this->methodName = $values['methodName']; + ) { + $this->problemName = $values['problemName']; + $this->problemDescription = $values['problemDescription']; + $this->files = $values['files']; + $this->inputParams = $values['inputParams']; + $this->outputType = $values['outputType']; + $this->testcases = $values['testcases']; + $this->methodName = $values['methodName']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Problem/Types/CreateProblemResponse.php b/seed/php-sdk/trace/src/Problem/Types/CreateProblemResponse.php index 86e7a32c3aa2..d6d521282561 100644 --- a/seed/php-sdk/trace/src/Problem/Types/CreateProblemResponse.php +++ b/seed/php-sdk/trace/src/Problem/Types/CreateProblemResponse.php @@ -42,16 +42,17 @@ class CreateProblemResponse extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param string $success * @return CreateProblemResponse */ - public static function success(string $success): CreateProblemResponse { + public static function success(string $success): CreateProblemResponse + { return new CreateProblemResponse([ 'type' => 'success', 'value' => $success, @@ -62,7 +63,8 @@ public static function success(string $success): CreateProblemResponse { * @param CreateProblemError $error * @return CreateProblemResponse */ - public static function error(CreateProblemError $error): CreateProblemResponse { + public static function error(CreateProblemError $error): CreateProblemResponse + { return new CreateProblemResponse([ 'type' => 'error', 'value' => $error, @@ -72,61 +74,67 @@ public static function error(CreateProblemError $error): CreateProblemResponse { /** * @return bool */ - public function isSuccess(): bool { - return is_string($this->value)&& $this->type === 'success'; + public function isSuccess(): bool + { + return is_string($this->value) && $this->type === 'success'; } /** * @return string */ - public function asSuccess(): string { - if (!(is_string($this->value)&& $this->type === 'success')){ + public function asSuccess(): string + { + if (!(is_string($this->value) && $this->type === 'success')) { throw new Exception( "Expected success; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isError(): bool { - return $this->value instanceof CreateProblemError&& $this->type === 'error'; + public function isError(): bool + { + return $this->value instanceof CreateProblemError && $this->type === 'error'; } /** * @return CreateProblemError */ - public function asError(): CreateProblemError { - if (!($this->value instanceof CreateProblemError&& $this->type === 'error')){ + public function asError(): CreateProblemError + { + if (!($this->value instanceof CreateProblemError && $this->type === 'error')) { throw new Exception( "Expected error; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'success': $value = $this->value; $result['success'] = $value; @@ -137,26 +145,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -165,39 +174,40 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'success': - if (!array_key_exists('success', $data)){ + if (!array_key_exists('success', $data)) { throw new Exception( "JSON data is missing property 'success'", ); } - + $args['value'] = $data['success']; break; case 'error': - if (!array_key_exists('error', $data)){ + if (!array_key_exists('error', $data)) { throw new Exception( "JSON data is missing property 'error'", ); } - - if (!(is_array($data['error']))){ + + if (!(is_array($data['error']))) { throw new Exception( "Expected property 'error' in JSON data to be array, instead received " . get_debug_type($data['error']), ); @@ -209,7 +219,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/trace/src/Problem/Types/GenericCreateProblemError.php b/seed/php-sdk/trace/src/Problem/Types/GenericCreateProblemError.php index 2cd6eb4c0cb9..0c3bde35e6a2 100644 --- a/seed/php-sdk/trace/src/Problem/Types/GenericCreateProblemError.php +++ b/seed/php-sdk/trace/src/Problem/Types/GenericCreateProblemError.php @@ -34,15 +34,17 @@ class GenericCreateProblemError extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->message = $values['message'];$this->type = $values['type'];$this->stacktrace = $values['stacktrace']; + ) { + $this->message = $values['message']; + $this->type = $values['type']; + $this->stacktrace = $values['stacktrace']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Problem/Types/GetDefaultStarterFilesResponse.php b/seed/php-sdk/trace/src/Problem/Types/GetDefaultStarterFilesResponse.php index 0573142f2cd1..7102fee5b59e 100644 --- a/seed/php-sdk/trace/src/Problem/Types/GetDefaultStarterFilesResponse.php +++ b/seed/php-sdk/trace/src/Problem/Types/GetDefaultStarterFilesResponse.php @@ -22,15 +22,15 @@ class GetDefaultStarterFilesResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->files = $values['files']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Problem/Types/ProblemDescription.php b/seed/php-sdk/trace/src/Problem/Types/ProblemDescription.php index b4e2c79eb3ea..4c8997563d46 100644 --- a/seed/php-sdk/trace/src/Problem/Types/ProblemDescription.php +++ b/seed/php-sdk/trace/src/Problem/Types/ProblemDescription.php @@ -21,15 +21,15 @@ class ProblemDescription extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->boards = $values['boards']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Problem/Types/ProblemDescriptionBoard.php b/seed/php-sdk/trace/src/Problem/Types/ProblemDescriptionBoard.php index 0368c9b49739..d9e920c0e2b0 100644 --- a/seed/php-sdk/trace/src/Problem/Types/ProblemDescriptionBoard.php +++ b/seed/php-sdk/trace/src/Problem/Types/ProblemDescriptionBoard.php @@ -45,16 +45,17 @@ class ProblemDescriptionBoard extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param string $html * @return ProblemDescriptionBoard */ - public static function html(string $html): ProblemDescriptionBoard { + public static function html(string $html): ProblemDescriptionBoard + { return new ProblemDescriptionBoard([ 'type' => 'html', 'value' => $html, @@ -65,7 +66,8 @@ public static function html(string $html): ProblemDescriptionBoard { * @param VariableValue $variable * @return ProblemDescriptionBoard */ - public static function variable(VariableValue $variable): ProblemDescriptionBoard { + public static function variable(VariableValue $variable): ProblemDescriptionBoard + { return new ProblemDescriptionBoard([ 'type' => 'variable', 'value' => $variable, @@ -76,7 +78,8 @@ public static function variable(VariableValue $variable): ProblemDescriptionBoar * @param string $testCaseId * @return ProblemDescriptionBoard */ - public static function testCaseId(string $testCaseId): ProblemDescriptionBoard { + public static function testCaseId(string $testCaseId): ProblemDescriptionBoard + { return new ProblemDescriptionBoard([ 'type' => 'testCaseId', 'value' => $testCaseId, @@ -86,81 +89,89 @@ public static function testCaseId(string $testCaseId): ProblemDescriptionBoard { /** * @return bool */ - public function isHtml(): bool { - return is_string($this->value)&& $this->type === 'html'; + public function isHtml(): bool + { + return is_string($this->value) && $this->type === 'html'; } /** * @return string */ - public function asHtml(): string { - if (!(is_string($this->value)&& $this->type === 'html')){ + public function asHtml(): string + { + if (!(is_string($this->value) && $this->type === 'html')) { throw new Exception( "Expected html; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isVariable(): bool { - return $this->value instanceof VariableValue&& $this->type === 'variable'; + public function isVariable(): bool + { + return $this->value instanceof VariableValue && $this->type === 'variable'; } /** * @return VariableValue */ - public function asVariable(): VariableValue { - if (!($this->value instanceof VariableValue&& $this->type === 'variable')){ + public function asVariable(): VariableValue + { + if (!($this->value instanceof VariableValue && $this->type === 'variable')) { throw new Exception( "Expected variable; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isTestCaseId(): bool { - return is_string($this->value)&& $this->type === 'testCaseId'; + public function isTestCaseId(): bool + { + return is_string($this->value) && $this->type === 'testCaseId'; } /** * @return string */ - public function asTestCaseId(): string { - if (!(is_string($this->value)&& $this->type === 'testCaseId')){ + public function asTestCaseId(): string + { + if (!(is_string($this->value) && $this->type === 'testCaseId')) { throw new Exception( "Expected testCaseId; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'html': $value = $this->value; $result['html'] = $value; @@ -175,26 +186,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -203,39 +215,40 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'html': - if (!array_key_exists('html', $data)){ + if (!array_key_exists('html', $data)) { throw new Exception( "JSON data is missing property 'html'", ); } - + $args['value'] = $data['html']; break; case 'variable': - if (!array_key_exists('variable', $data)){ + if (!array_key_exists('variable', $data)) { throw new Exception( "JSON data is missing property 'variable'", ); } - - if (!(is_array($data['variable']))){ + + if (!(is_array($data['variable']))) { throw new Exception( "Expected property 'variable' in JSON data to be array, instead received " . get_debug_type($data['variable']), ); @@ -243,12 +256,12 @@ public static function jsonDeserialize(array $data): static { $args['value'] = VariableValue::jsonDeserialize($data['variable']); break; case 'testCaseId': - if (!array_key_exists('testCaseId', $data)){ + if (!array_key_exists('testCaseId', $data)) { throw new Exception( "JSON data is missing property 'testCaseId'", ); } - + $args['value'] = $data['testCaseId']; break; case '_unknown': @@ -256,7 +269,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/trace/src/Problem/Types/ProblemFiles.php b/seed/php-sdk/trace/src/Problem/Types/ProblemFiles.php index e7d13ed27457..6fd2f14a197b 100644 --- a/seed/php-sdk/trace/src/Problem/Types/ProblemFiles.php +++ b/seed/php-sdk/trace/src/Problem/Types/ProblemFiles.php @@ -29,15 +29,16 @@ class ProblemFiles extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->solutionFile = $values['solutionFile'];$this->readOnlyFiles = $values['readOnlyFiles']; + ) { + $this->solutionFile = $values['solutionFile']; + $this->readOnlyFiles = $values['readOnlyFiles']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Problem/Types/ProblemInfo.php b/seed/php-sdk/trace/src/Problem/Types/ProblemInfo.php index 72de192d40d6..38c0748dedbf 100644 --- a/seed/php-sdk/trace/src/Problem/Types/ProblemInfo.php +++ b/seed/php-sdk/trace/src/Problem/Types/ProblemInfo.php @@ -87,15 +87,24 @@ class ProblemInfo extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->problemId = $values['problemId'];$this->problemDescription = $values['problemDescription'];$this->problemName = $values['problemName'];$this->problemVersion = $values['problemVersion'];$this->files = $values['files'];$this->inputParams = $values['inputParams'];$this->outputType = $values['outputType'];$this->testcases = $values['testcases'];$this->methodName = $values['methodName'];$this->supportsCustomTestCases = $values['supportsCustomTestCases']; + ) { + $this->problemId = $values['problemId']; + $this->problemDescription = $values['problemDescription']; + $this->problemName = $values['problemName']; + $this->problemVersion = $values['problemVersion']; + $this->files = $values['files']; + $this->inputParams = $values['inputParams']; + $this->outputType = $values['outputType']; + $this->testcases = $values['testcases']; + $this->methodName = $values['methodName']; + $this->supportsCustomTestCases = $values['supportsCustomTestCases']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Problem/Types/UpdateProblemResponse.php b/seed/php-sdk/trace/src/Problem/Types/UpdateProblemResponse.php index 06f317077f1a..06596639f4e7 100644 --- a/seed/php-sdk/trace/src/Problem/Types/UpdateProblemResponse.php +++ b/seed/php-sdk/trace/src/Problem/Types/UpdateProblemResponse.php @@ -20,15 +20,15 @@ class UpdateProblemResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->problemVersion = $values['problemVersion']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Problem/Types/VariableTypeAndName.php b/seed/php-sdk/trace/src/Problem/Types/VariableTypeAndName.php index 7985f13b4f69..04899e856a29 100644 --- a/seed/php-sdk/trace/src/Problem/Types/VariableTypeAndName.php +++ b/seed/php-sdk/trace/src/Problem/Types/VariableTypeAndName.php @@ -28,15 +28,16 @@ class VariableTypeAndName extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->variableType = $values['variableType'];$this->name = $values['name']; + ) { + $this->variableType = $values['variableType']; + $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/SeedClient.php b/seed/php-sdk/trace/src/SeedClient.php index 5b99082f0c54..a20f570545c7 100644 --- a/seed/php-sdk/trace/src/SeedClient.php +++ b/seed/php-sdk/trace/src/SeedClient.php @@ -13,7 +13,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var V2Client $v2 @@ -86,32 +86,31 @@ public function __construct( ?string $token = null, ?string $xRandomHeader = null, ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - if ($token != null){ + if ($token != null) { $defaultHeaders['Authorization'] = "Bearer $token"; } - if ($xRandomHeader != null){ + if ($xRandomHeader != null) { $defaultHeaders['X-Random-Header'] = $xRandomHeader; } - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->v2 = new V2Client($this->client, $this->options); $this->admin = new AdminClient($this->client, $this->options); $this->homepage = new HomepageClient($this->client, $this->options); diff --git a/seed/php-sdk/trace/src/Submission/SubmissionClient.php b/seed/php-sdk/trace/src/Submission/SubmissionClient.php index 7b794aff1264..d9baf6f8304e 100644 --- a/seed/php-sdk/trace/src/Submission/SubmissionClient.php +++ b/seed/php-sdk/trace/src/Submission/SubmissionClient.php @@ -16,7 +16,7 @@ use Psr\Http\Client\ClientExceptionInterface; use Seed\Submission\Types\GetExecutionSessionStateResponse; -class SubmissionClient +class SubmissionClient { /** * @var array{ @@ -44,11 +44,10 @@ class SubmissionClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -69,7 +68,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function createExecutionSession(string $language, ?array $options = null): ExecutionSessionResponse { + public function createExecutionSession(string $language, ?array $options = null): ExecutionSessionResponse + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -81,15 +81,15 @@ public function createExecutionSession(string $language, ?array $options = null) $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ExecutionSessionResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -123,7 +123,8 @@ public function createExecutionSession(string $language, ?array $options = null) * @throws SeedException * @throws SeedApiException */ - public function getExecutionSession(string $sessionId, ?array $options = null): ?ExecutionSessionResponse { + public function getExecutionSession(string $sessionId, ?array $options = null): ?ExecutionSessionResponse + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -135,18 +136,18 @@ public function getExecutionSession(string $sessionId, ?array $options = null): $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); - if (empty($json)){ + if (empty($json)) { return null; } return ExecutionSessionResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -179,7 +180,8 @@ public function getExecutionSession(string $sessionId, ?array $options = null): * @throws SeedException * @throws SeedApiException */ - public function stopExecutionSession(string $sessionId, ?array $options = null): void { + public function stopExecutionSession(string $sessionId, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -191,12 +193,12 @@ public function stopExecutionSession(string $sessionId, ?array $options = null): $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -227,7 +229,8 @@ public function stopExecutionSession(string $sessionId, ?array $options = null): * @throws SeedException * @throws SeedApiException */ - public function getExecutionSessionsState(?array $options = null): GetExecutionSessionStateResponse { + public function getExecutionSessionsState(?array $options = null): GetExecutionSessionStateResponse + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -239,15 +242,15 @@ public function getExecutionSessionsState(?array $options = null): GetExecutionS $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return GetExecutionSessionStateResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/trace/src/Submission/Types/ActualResult.php b/seed/php-sdk/trace/src/Submission/Types/ActualResult.php index 2815ff3f1564..146ddec1b4e3 100644 --- a/seed/php-sdk/trace/src/Submission/Types/ActualResult.php +++ b/seed/php-sdk/trace/src/Submission/Types/ActualResult.php @@ -47,16 +47,17 @@ class ActualResult extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param VariableValue $value * @return ActualResult */ - public static function value(VariableValue $value): ActualResult { + public static function value(VariableValue $value): ActualResult + { return new ActualResult([ 'type' => 'value', 'value' => $value, @@ -67,7 +68,8 @@ public static function value(VariableValue $value): ActualResult { * @param ExceptionInfo $exception * @return ActualResult */ - public static function exception(ExceptionInfo $exception): ActualResult { + public static function exception(ExceptionInfo $exception): ActualResult + { return new ActualResult([ 'type' => 'exception', 'value' => $exception, @@ -78,7 +80,8 @@ public static function exception(ExceptionInfo $exception): ActualResult { * @param ExceptionV2 $exceptionV2 * @return ActualResult */ - public static function exceptionV2(ExceptionV2 $exceptionV2): ActualResult { + public static function exceptionV2(ExceptionV2 $exceptionV2): ActualResult + { return new ActualResult([ 'type' => 'exceptionV2', 'value' => $exceptionV2, @@ -88,81 +91,89 @@ public static function exceptionV2(ExceptionV2 $exceptionV2): ActualResult { /** * @return bool */ - public function isValue(): bool { - return $this->value instanceof VariableValue&& $this->type === 'value'; + public function isValue(): bool + { + return $this->value instanceof VariableValue && $this->type === 'value'; } /** * @return VariableValue */ - public function asValue(): VariableValue { - if (!($this->value instanceof VariableValue&& $this->type === 'value')){ + public function asValue(): VariableValue + { + if (!($this->value instanceof VariableValue && $this->type === 'value')) { throw new Exception( "Expected value; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isException(): bool { - return $this->value instanceof ExceptionInfo&& $this->type === 'exception'; + public function isException(): bool + { + return $this->value instanceof ExceptionInfo && $this->type === 'exception'; } /** * @return ExceptionInfo */ - public function asException(): ExceptionInfo { - if (!($this->value instanceof ExceptionInfo&& $this->type === 'exception')){ + public function asException(): ExceptionInfo + { + if (!($this->value instanceof ExceptionInfo && $this->type === 'exception')) { throw new Exception( "Expected exception; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isExceptionV2(): bool { - return $this->value instanceof ExceptionV2&& $this->type === 'exceptionV2'; + public function isExceptionV2(): bool + { + return $this->value instanceof ExceptionV2 && $this->type === 'exceptionV2'; } /** * @return ExceptionV2 */ - public function asExceptionV2(): ExceptionV2 { - if (!($this->value instanceof ExceptionV2&& $this->type === 'exceptionV2')){ + public function asExceptionV2(): ExceptionV2 + { + if (!($this->value instanceof ExceptionV2 && $this->type === 'exceptionV2')) { throw new Exception( "Expected exceptionV2; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'value': $value = $this->asValue()->jsonSerialize(); $result['value'] = $value; @@ -177,26 +188,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -205,30 +217,31 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'value': - if (!array_key_exists('value', $data)){ + if (!array_key_exists('value', $data)) { throw new Exception( "JSON data is missing property 'value'", ); } - - if (!(is_array($data['value']))){ + + if (!(is_array($data['value']))) { throw new Exception( "Expected property 'value' in JSON data to be array, instead received " . get_debug_type($data['value']), ); @@ -239,13 +252,13 @@ public static function jsonDeserialize(array $data): static { $args['value'] = ExceptionInfo::jsonDeserialize($data); break; case 'exceptionV2': - if (!array_key_exists('exceptionV2', $data)){ + if (!array_key_exists('exceptionV2', $data)) { throw new Exception( "JSON data is missing property 'exceptionV2'", ); } - - if (!(is_array($data['exceptionV2']))){ + + if (!(is_array($data['exceptionV2']))) { throw new Exception( "Expected property 'exceptionV2' in JSON data to be array, instead received " . get_debug_type($data['exceptionV2']), ); @@ -257,7 +270,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/trace/src/Submission/Types/BuildingExecutorResponse.php b/seed/php-sdk/trace/src/Submission/Types/BuildingExecutorResponse.php index b1b372bb085c..79b406db2fb4 100644 --- a/seed/php-sdk/trace/src/Submission/Types/BuildingExecutorResponse.php +++ b/seed/php-sdk/trace/src/Submission/Types/BuildingExecutorResponse.php @@ -27,15 +27,16 @@ class BuildingExecutorResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->submissionId = $values['submissionId'];$this->status = $values['status']; + ) { + $this->submissionId = $values['submissionId']; + $this->status = $values['status']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/CodeExecutionUpdate.php b/seed/php-sdk/trace/src/Submission/Types/CodeExecutionUpdate.php index c2444b4d8488..6f48aa24cac7 100644 --- a/seed/php-sdk/trace/src/Submission/Types/CodeExecutionUpdate.php +++ b/seed/php-sdk/trace/src/Submission/Types/CodeExecutionUpdate.php @@ -78,16 +78,17 @@ class CodeExecutionUpdate extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param BuildingExecutorResponse $buildingExecutor * @return CodeExecutionUpdate */ - public static function buildingExecutor(BuildingExecutorResponse $buildingExecutor): CodeExecutionUpdate { + public static function buildingExecutor(BuildingExecutorResponse $buildingExecutor): CodeExecutionUpdate + { return new CodeExecutionUpdate([ 'type' => 'buildingExecutor', 'value' => $buildingExecutor, @@ -98,7 +99,8 @@ public static function buildingExecutor(BuildingExecutorResponse $buildingExecut * @param RunningResponse $running * @return CodeExecutionUpdate */ - public static function running(RunningResponse $running): CodeExecutionUpdate { + public static function running(RunningResponse $running): CodeExecutionUpdate + { return new CodeExecutionUpdate([ 'type' => 'running', 'value' => $running, @@ -109,7 +111,8 @@ public static function running(RunningResponse $running): CodeExecutionUpdate { * @param ErroredResponse $errored * @return CodeExecutionUpdate */ - public static function errored(ErroredResponse $errored): CodeExecutionUpdate { + public static function errored(ErroredResponse $errored): CodeExecutionUpdate + { return new CodeExecutionUpdate([ 'type' => 'errored', 'value' => $errored, @@ -120,7 +123,8 @@ public static function errored(ErroredResponse $errored): CodeExecutionUpdate { * @param StoppedResponse $stopped * @return CodeExecutionUpdate */ - public static function stopped(StoppedResponse $stopped): CodeExecutionUpdate { + public static function stopped(StoppedResponse $stopped): CodeExecutionUpdate + { return new CodeExecutionUpdate([ 'type' => 'stopped', 'value' => $stopped, @@ -131,7 +135,8 @@ public static function stopped(StoppedResponse $stopped): CodeExecutionUpdate { * @param GradedResponse $graded * @return CodeExecutionUpdate */ - public static function graded(GradedResponse $graded): CodeExecutionUpdate { + public static function graded(GradedResponse $graded): CodeExecutionUpdate + { return new CodeExecutionUpdate([ 'type' => 'graded', 'value' => $graded, @@ -142,7 +147,8 @@ public static function graded(GradedResponse $graded): CodeExecutionUpdate { * @param GradedResponseV2 $gradedV2 * @return CodeExecutionUpdate */ - public static function gradedV2(GradedResponseV2 $gradedV2): CodeExecutionUpdate { + public static function gradedV2(GradedResponseV2 $gradedV2): CodeExecutionUpdate + { return new CodeExecutionUpdate([ 'type' => 'gradedV2', 'value' => $gradedV2, @@ -153,7 +159,8 @@ public static function gradedV2(GradedResponseV2 $gradedV2): CodeExecutionUpdate * @param WorkspaceRanResponse $workspaceRan * @return CodeExecutionUpdate */ - public static function workspaceRan(WorkspaceRanResponse $workspaceRan): CodeExecutionUpdate { + public static function workspaceRan(WorkspaceRanResponse $workspaceRan): CodeExecutionUpdate + { return new CodeExecutionUpdate([ 'type' => 'workspaceRan', 'value' => $workspaceRan, @@ -164,7 +171,8 @@ public static function workspaceRan(WorkspaceRanResponse $workspaceRan): CodeExe * @param RecordingResponseNotification $recording * @return CodeExecutionUpdate */ - public static function recording(RecordingResponseNotification $recording): CodeExecutionUpdate { + public static function recording(RecordingResponseNotification $recording): CodeExecutionUpdate + { return new CodeExecutionUpdate([ 'type' => 'recording', 'value' => $recording, @@ -175,7 +183,8 @@ public static function recording(RecordingResponseNotification $recording): Code * @param RecordedResponseNotification $recorded * @return CodeExecutionUpdate */ - public static function recorded(RecordedResponseNotification $recorded): CodeExecutionUpdate { + public static function recorded(RecordedResponseNotification $recorded): CodeExecutionUpdate + { return new CodeExecutionUpdate([ 'type' => 'recorded', 'value' => $recorded, @@ -186,7 +195,8 @@ public static function recorded(RecordedResponseNotification $recorded): CodeExe * @param InvalidRequestResponse $invalidRequest * @return CodeExecutionUpdate */ - public static function invalidRequest(InvalidRequestResponse $invalidRequest): CodeExecutionUpdate { + public static function invalidRequest(InvalidRequestResponse $invalidRequest): CodeExecutionUpdate + { return new CodeExecutionUpdate([ 'type' => 'invalidRequest', 'value' => $invalidRequest, @@ -197,7 +207,8 @@ public static function invalidRequest(InvalidRequestResponse $invalidRequest): C * @param FinishedResponse $finished * @return CodeExecutionUpdate */ - public static function finished(FinishedResponse $finished): CodeExecutionUpdate { + public static function finished(FinishedResponse $finished): CodeExecutionUpdate + { return new CodeExecutionUpdate([ 'type' => 'finished', 'value' => $finished, @@ -207,241 +218,265 @@ public static function finished(FinishedResponse $finished): CodeExecutionUpdate /** * @return bool */ - public function isBuildingExecutor(): bool { - return $this->value instanceof BuildingExecutorResponse&& $this->type === 'buildingExecutor'; + public function isBuildingExecutor(): bool + { + return $this->value instanceof BuildingExecutorResponse && $this->type === 'buildingExecutor'; } /** * @return BuildingExecutorResponse */ - public function asBuildingExecutor(): BuildingExecutorResponse { - if (!($this->value instanceof BuildingExecutorResponse&& $this->type === 'buildingExecutor')){ + public function asBuildingExecutor(): BuildingExecutorResponse + { + if (!($this->value instanceof BuildingExecutorResponse && $this->type === 'buildingExecutor')) { throw new Exception( "Expected buildingExecutor; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isRunning(): bool { - return $this->value instanceof RunningResponse&& $this->type === 'running'; + public function isRunning(): bool + { + return $this->value instanceof RunningResponse && $this->type === 'running'; } /** * @return RunningResponse */ - public function asRunning(): RunningResponse { - if (!($this->value instanceof RunningResponse&& $this->type === 'running')){ + public function asRunning(): RunningResponse + { + if (!($this->value instanceof RunningResponse && $this->type === 'running')) { throw new Exception( "Expected running; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isErrored(): bool { - return $this->value instanceof ErroredResponse&& $this->type === 'errored'; + public function isErrored(): bool + { + return $this->value instanceof ErroredResponse && $this->type === 'errored'; } /** * @return ErroredResponse */ - public function asErrored(): ErroredResponse { - if (!($this->value instanceof ErroredResponse&& $this->type === 'errored')){ + public function asErrored(): ErroredResponse + { + if (!($this->value instanceof ErroredResponse && $this->type === 'errored')) { throw new Exception( "Expected errored; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isStopped(): bool { - return $this->value instanceof StoppedResponse&& $this->type === 'stopped'; + public function isStopped(): bool + { + return $this->value instanceof StoppedResponse && $this->type === 'stopped'; } /** * @return StoppedResponse */ - public function asStopped(): StoppedResponse { - if (!($this->value instanceof StoppedResponse&& $this->type === 'stopped')){ + public function asStopped(): StoppedResponse + { + if (!($this->value instanceof StoppedResponse && $this->type === 'stopped')) { throw new Exception( "Expected stopped; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isGraded(): bool { - return $this->value instanceof GradedResponse&& $this->type === 'graded'; + public function isGraded(): bool + { + return $this->value instanceof GradedResponse && $this->type === 'graded'; } /** * @return GradedResponse */ - public function asGraded(): GradedResponse { - if (!($this->value instanceof GradedResponse&& $this->type === 'graded')){ + public function asGraded(): GradedResponse + { + if (!($this->value instanceof GradedResponse && $this->type === 'graded')) { throw new Exception( "Expected graded; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isGradedV2(): bool { - return $this->value instanceof GradedResponseV2&& $this->type === 'gradedV2'; + public function isGradedV2(): bool + { + return $this->value instanceof GradedResponseV2 && $this->type === 'gradedV2'; } /** * @return GradedResponseV2 */ - public function asGradedV2(): GradedResponseV2 { - if (!($this->value instanceof GradedResponseV2&& $this->type === 'gradedV2')){ + public function asGradedV2(): GradedResponseV2 + { + if (!($this->value instanceof GradedResponseV2 && $this->type === 'gradedV2')) { throw new Exception( "Expected gradedV2; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isWorkspaceRan(): bool { - return $this->value instanceof WorkspaceRanResponse&& $this->type === 'workspaceRan'; + public function isWorkspaceRan(): bool + { + return $this->value instanceof WorkspaceRanResponse && $this->type === 'workspaceRan'; } /** * @return WorkspaceRanResponse */ - public function asWorkspaceRan(): WorkspaceRanResponse { - if (!($this->value instanceof WorkspaceRanResponse&& $this->type === 'workspaceRan')){ + public function asWorkspaceRan(): WorkspaceRanResponse + { + if (!($this->value instanceof WorkspaceRanResponse && $this->type === 'workspaceRan')) { throw new Exception( "Expected workspaceRan; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isRecording(): bool { - return $this->value instanceof RecordingResponseNotification&& $this->type === 'recording'; + public function isRecording(): bool + { + return $this->value instanceof RecordingResponseNotification && $this->type === 'recording'; } /** * @return RecordingResponseNotification */ - public function asRecording(): RecordingResponseNotification { - if (!($this->value instanceof RecordingResponseNotification&& $this->type === 'recording')){ + public function asRecording(): RecordingResponseNotification + { + if (!($this->value instanceof RecordingResponseNotification && $this->type === 'recording')) { throw new Exception( "Expected recording; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isRecorded(): bool { - return $this->value instanceof RecordedResponseNotification&& $this->type === 'recorded'; + public function isRecorded(): bool + { + return $this->value instanceof RecordedResponseNotification && $this->type === 'recorded'; } /** * @return RecordedResponseNotification */ - public function asRecorded(): RecordedResponseNotification { - if (!($this->value instanceof RecordedResponseNotification&& $this->type === 'recorded')){ + public function asRecorded(): RecordedResponseNotification + { + if (!($this->value instanceof RecordedResponseNotification && $this->type === 'recorded')) { throw new Exception( "Expected recorded; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isInvalidRequest(): bool { - return $this->value instanceof InvalidRequestResponse&& $this->type === 'invalidRequest'; + public function isInvalidRequest(): bool + { + return $this->value instanceof InvalidRequestResponse && $this->type === 'invalidRequest'; } /** * @return InvalidRequestResponse */ - public function asInvalidRequest(): InvalidRequestResponse { - if (!($this->value instanceof InvalidRequestResponse&& $this->type === 'invalidRequest')){ + public function asInvalidRequest(): InvalidRequestResponse + { + if (!($this->value instanceof InvalidRequestResponse && $this->type === 'invalidRequest')) { throw new Exception( "Expected invalidRequest; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isFinished(): bool { - return $this->value instanceof FinishedResponse&& $this->type === 'finished'; + public function isFinished(): bool + { + return $this->value instanceof FinishedResponse && $this->type === 'finished'; } /** * @return FinishedResponse */ - public function asFinished(): FinishedResponse { - if (!($this->value instanceof FinishedResponse&& $this->type === 'finished')){ + public function asFinished(): FinishedResponse + { + if (!($this->value instanceof FinishedResponse && $this->type === 'finished')) { throw new Exception( "Expected finished; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'buildingExecutor': $value = $this->asBuildingExecutor()->jsonSerialize(); $result = array_merge($value, $result); @@ -488,26 +523,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -516,22 +552,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'buildingExecutor': $args['value'] = BuildingExecutorResponse::jsonDeserialize($data); break; @@ -570,7 +607,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/trace/src/Submission/Types/CompileError.php b/seed/php-sdk/trace/src/Submission/Types/CompileError.php index 5300d6400908..fc83772ccb94 100644 --- a/seed/php-sdk/trace/src/Submission/Types/CompileError.php +++ b/seed/php-sdk/trace/src/Submission/Types/CompileError.php @@ -20,15 +20,15 @@ class CompileError extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->message = $values['message']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/CustomTestCasesUnsupported.php b/seed/php-sdk/trace/src/Submission/Types/CustomTestCasesUnsupported.php index a2aa88e2a31f..41ee75032235 100644 --- a/seed/php-sdk/trace/src/Submission/Types/CustomTestCasesUnsupported.php +++ b/seed/php-sdk/trace/src/Submission/Types/CustomTestCasesUnsupported.php @@ -27,15 +27,16 @@ class CustomTestCasesUnsupported extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->problemId = $values['problemId'];$this->submissionId = $values['submissionId']; + ) { + $this->problemId = $values['problemId']; + $this->submissionId = $values['submissionId']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/ErrorInfo.php b/seed/php-sdk/trace/src/Submission/Types/ErrorInfo.php index deebdb161cce..977559a86a8b 100644 --- a/seed/php-sdk/trace/src/Submission/Types/ErrorInfo.php +++ b/seed/php-sdk/trace/src/Submission/Types/ErrorInfo.php @@ -46,16 +46,17 @@ class ErrorInfo extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param CompileError $compileError * @return ErrorInfo */ - public static function compileError(CompileError $compileError): ErrorInfo { + public static function compileError(CompileError $compileError): ErrorInfo + { return new ErrorInfo([ 'type' => 'compileError', 'value' => $compileError, @@ -66,7 +67,8 @@ public static function compileError(CompileError $compileError): ErrorInfo { * @param RuntimeError $runtimeError * @return ErrorInfo */ - public static function runtimeError(RuntimeError $runtimeError): ErrorInfo { + public static function runtimeError(RuntimeError $runtimeError): ErrorInfo + { return new ErrorInfo([ 'type' => 'runtimeError', 'value' => $runtimeError, @@ -77,7 +79,8 @@ public static function runtimeError(RuntimeError $runtimeError): ErrorInfo { * @param InternalError $internalError * @return ErrorInfo */ - public static function internalError(InternalError $internalError): ErrorInfo { + public static function internalError(InternalError $internalError): ErrorInfo + { return new ErrorInfo([ 'type' => 'internalError', 'value' => $internalError, @@ -87,81 +90,89 @@ public static function internalError(InternalError $internalError): ErrorInfo { /** * @return bool */ - public function isCompileError(): bool { - return $this->value instanceof CompileError&& $this->type === 'compileError'; + public function isCompileError(): bool + { + return $this->value instanceof CompileError && $this->type === 'compileError'; } /** * @return CompileError */ - public function asCompileError(): CompileError { - if (!($this->value instanceof CompileError&& $this->type === 'compileError')){ + public function asCompileError(): CompileError + { + if (!($this->value instanceof CompileError && $this->type === 'compileError')) { throw new Exception( "Expected compileError; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isRuntimeError(): bool { - return $this->value instanceof RuntimeError&& $this->type === 'runtimeError'; + public function isRuntimeError(): bool + { + return $this->value instanceof RuntimeError && $this->type === 'runtimeError'; } /** * @return RuntimeError */ - public function asRuntimeError(): RuntimeError { - if (!($this->value instanceof RuntimeError&& $this->type === 'runtimeError')){ + public function asRuntimeError(): RuntimeError + { + if (!($this->value instanceof RuntimeError && $this->type === 'runtimeError')) { throw new Exception( "Expected runtimeError; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isInternalError(): bool { - return $this->value instanceof InternalError&& $this->type === 'internalError'; + public function isInternalError(): bool + { + return $this->value instanceof InternalError && $this->type === 'internalError'; } /** * @return InternalError */ - public function asInternalError(): InternalError { - if (!($this->value instanceof InternalError&& $this->type === 'internalError')){ + public function asInternalError(): InternalError + { + if (!($this->value instanceof InternalError && $this->type === 'internalError')) { throw new Exception( "Expected internalError; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'compileError': $value = $this->asCompileError()->jsonSerialize(); $result = array_merge($value, $result); @@ -176,26 +187,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -204,22 +216,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'compileError': $args['value'] = CompileError::jsonDeserialize($data); break; @@ -234,7 +247,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/trace/src/Submission/Types/ErroredResponse.php b/seed/php-sdk/trace/src/Submission/Types/ErroredResponse.php index 8611b40db839..794033b347c4 100644 --- a/seed/php-sdk/trace/src/Submission/Types/ErroredResponse.php +++ b/seed/php-sdk/trace/src/Submission/Types/ErroredResponse.php @@ -27,15 +27,16 @@ class ErroredResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->submissionId = $values['submissionId'];$this->errorInfo = $values['errorInfo']; + ) { + $this->submissionId = $values['submissionId']; + $this->errorInfo = $values['errorInfo']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/ExceptionInfo.php b/seed/php-sdk/trace/src/Submission/Types/ExceptionInfo.php index 2d2678c97211..634023edcd8e 100644 --- a/seed/php-sdk/trace/src/Submission/Types/ExceptionInfo.php +++ b/seed/php-sdk/trace/src/Submission/Types/ExceptionInfo.php @@ -34,15 +34,17 @@ class ExceptionInfo extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->exceptionType = $values['exceptionType'];$this->exceptionMessage = $values['exceptionMessage'];$this->exceptionStacktrace = $values['exceptionStacktrace']; + ) { + $this->exceptionType = $values['exceptionType']; + $this->exceptionMessage = $values['exceptionMessage']; + $this->exceptionStacktrace = $values['exceptionStacktrace']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/ExceptionV2.php b/seed/php-sdk/trace/src/Submission/Types/ExceptionV2.php index bcde0378b3c4..abd6258c48e1 100644 --- a/seed/php-sdk/trace/src/Submission/Types/ExceptionV2.php +++ b/seed/php-sdk/trace/src/Submission/Types/ExceptionV2.php @@ -42,16 +42,17 @@ class ExceptionV2 extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param ExceptionInfo $generic * @return ExceptionV2 */ - public static function generic(ExceptionInfo $generic): ExceptionV2 { + public static function generic(ExceptionInfo $generic): ExceptionV2 + { return new ExceptionV2([ 'type' => 'generic', 'value' => $generic, @@ -61,7 +62,8 @@ public static function generic(ExceptionInfo $generic): ExceptionV2 { /** * @return ExceptionV2 */ - public static function timeout(): ExceptionV2 { + public static function timeout(): ExceptionV2 + { return new ExceptionV2([ 'type' => 'timeout', 'value' => null, @@ -71,48 +73,53 @@ public static function timeout(): ExceptionV2 { /** * @return bool */ - public function isGeneric(): bool { - return $this->value instanceof ExceptionInfo&& $this->type === 'generic'; + public function isGeneric(): bool + { + return $this->value instanceof ExceptionInfo && $this->type === 'generic'; } /** * @return ExceptionInfo */ - public function asGeneric(): ExceptionInfo { - if (!($this->value instanceof ExceptionInfo&& $this->type === 'generic')){ + public function asGeneric(): ExceptionInfo + { + if (!($this->value instanceof ExceptionInfo && $this->type === 'generic')) { throw new Exception( "Expected generic; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isTimeout(): bool { - return is_null($this->value)&& $this->type === 'timeout'; + public function isTimeout(): bool + { + return is_null($this->value) && $this->type === 'timeout'; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'generic': $value = $this->asGeneric()->jsonSerialize(); $result = array_merge($value, $result); @@ -122,26 +129,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -150,22 +158,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'generic': $args['value'] = ExceptionInfo::jsonDeserialize($data); break; @@ -177,7 +186,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/trace/src/Submission/Types/ExecutionSessionResponse.php b/seed/php-sdk/trace/src/Submission/Types/ExecutionSessionResponse.php index 552e0fcff433..a3d35f346aa0 100644 --- a/seed/php-sdk/trace/src/Submission/Types/ExecutionSessionResponse.php +++ b/seed/php-sdk/trace/src/Submission/Types/ExecutionSessionResponse.php @@ -42,15 +42,18 @@ class ExecutionSessionResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->sessionId = $values['sessionId'];$this->executionSessionUrl = $values['executionSessionUrl'] ?? null;$this->language = $values['language'];$this->status = $values['status']; + ) { + $this->sessionId = $values['sessionId']; + $this->executionSessionUrl = $values['executionSessionUrl'] ?? null; + $this->language = $values['language']; + $this->status = $values['status']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/ExecutionSessionState.php b/seed/php-sdk/trace/src/Submission/Types/ExecutionSessionState.php index 36e854c8c812..a5010bc3a786 100644 --- a/seed/php-sdk/trace/src/Submission/Types/ExecutionSessionState.php +++ b/seed/php-sdk/trace/src/Submission/Types/ExecutionSessionState.php @@ -56,15 +56,20 @@ class ExecutionSessionState extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->lastTimeContacted = $values['lastTimeContacted'] ?? null;$this->sessionId = $values['sessionId'];$this->isWarmInstance = $values['isWarmInstance'];$this->awsTaskId = $values['awsTaskId'] ?? null;$this->language = $values['language'];$this->status = $values['status']; + ) { + $this->lastTimeContacted = $values['lastTimeContacted'] ?? null; + $this->sessionId = $values['sessionId']; + $this->isWarmInstance = $values['isWarmInstance']; + $this->awsTaskId = $values['awsTaskId'] ?? null; + $this->language = $values['language']; + $this->status = $values['status']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/ExecutionSessionStatus.php b/seed/php-sdk/trace/src/Submission/Types/ExecutionSessionStatus.php index 7a0e2cc7948c..1627ddbf2f66 100644 --- a/seed/php-sdk/trace/src/Submission/Types/ExecutionSessionStatus.php +++ b/seed/php-sdk/trace/src/Submission/Types/ExecutionSessionStatus.php @@ -2,8 +2,8 @@ namespace Seed\Submission\Types; -enum ExecutionSessionStatus - : string { +enum ExecutionSessionStatus: string +{ case CreatingContainer = "CREATING_CONTAINER"; case ProvisioningContainer = "PROVISIONING_CONTAINER"; case PendingContainer = "PENDING_CONTAINER"; diff --git a/seed/php-sdk/trace/src/Submission/Types/ExistingSubmissionExecuting.php b/seed/php-sdk/trace/src/Submission/Types/ExistingSubmissionExecuting.php index cb103b25fde5..dce918c464a1 100644 --- a/seed/php-sdk/trace/src/Submission/Types/ExistingSubmissionExecuting.php +++ b/seed/php-sdk/trace/src/Submission/Types/ExistingSubmissionExecuting.php @@ -20,15 +20,15 @@ class ExistingSubmissionExecuting extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->submissionId = $values['submissionId']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/ExpressionLocation.php b/seed/php-sdk/trace/src/Submission/Types/ExpressionLocation.php index 73afd1677540..28b72fdb330a 100644 --- a/seed/php-sdk/trace/src/Submission/Types/ExpressionLocation.php +++ b/seed/php-sdk/trace/src/Submission/Types/ExpressionLocation.php @@ -27,15 +27,16 @@ class ExpressionLocation extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->start = $values['start'];$this->offset = $values['offset']; + ) { + $this->start = $values['start']; + $this->offset = $values['offset']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/FinishedResponse.php b/seed/php-sdk/trace/src/Submission/Types/FinishedResponse.php index ac10e6e8b3d9..2f9f8751ac69 100644 --- a/seed/php-sdk/trace/src/Submission/Types/FinishedResponse.php +++ b/seed/php-sdk/trace/src/Submission/Types/FinishedResponse.php @@ -20,15 +20,15 @@ class FinishedResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->submissionId = $values['submissionId']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/GetExecutionSessionStateResponse.php b/seed/php-sdk/trace/src/Submission/Types/GetExecutionSessionStateResponse.php index 5a18eb4c484b..71478155c148 100644 --- a/seed/php-sdk/trace/src/Submission/Types/GetExecutionSessionStateResponse.php +++ b/seed/php-sdk/trace/src/Submission/Types/GetExecutionSessionStateResponse.php @@ -35,15 +35,17 @@ class GetExecutionSessionStateResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->states = $values['states'];$this->numWarmingInstances = $values['numWarmingInstances'] ?? null;$this->warmingSessionIds = $values['warmingSessionIds']; + ) { + $this->states = $values['states']; + $this->numWarmingInstances = $values['numWarmingInstances'] ?? null; + $this->warmingSessionIds = $values['warmingSessionIds']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/GetSubmissionStateResponse.php b/seed/php-sdk/trace/src/Submission/Types/GetSubmissionStateResponse.php index 1a0937d7af29..dc57587d4d95 100644 --- a/seed/php-sdk/trace/src/Submission/Types/GetSubmissionStateResponse.php +++ b/seed/php-sdk/trace/src/Submission/Types/GetSubmissionStateResponse.php @@ -44,15 +44,18 @@ class GetSubmissionStateResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->timeSubmitted = $values['timeSubmitted'] ?? null;$this->submission = $values['submission'];$this->language = $values['language'];$this->submissionTypeState = $values['submissionTypeState']; + ) { + $this->timeSubmitted = $values['timeSubmitted'] ?? null; + $this->submission = $values['submission']; + $this->language = $values['language']; + $this->submissionTypeState = $values['submissionTypeState']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/GetTraceResponsesPageRequest.php b/seed/php-sdk/trace/src/Submission/Types/GetTraceResponsesPageRequest.php index f5f3c07d80cc..2bb4031ccecb 100644 --- a/seed/php-sdk/trace/src/Submission/Types/GetTraceResponsesPageRequest.php +++ b/seed/php-sdk/trace/src/Submission/Types/GetTraceResponsesPageRequest.php @@ -20,15 +20,15 @@ class GetTraceResponsesPageRequest extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->offset = $values['offset'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/GradedResponse.php b/seed/php-sdk/trace/src/Submission/Types/GradedResponse.php index fba5cfac8788..f1634a9b493d 100644 --- a/seed/php-sdk/trace/src/Submission/Types/GradedResponse.php +++ b/seed/php-sdk/trace/src/Submission/Types/GradedResponse.php @@ -28,15 +28,16 @@ class GradedResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->submissionId = $values['submissionId'];$this->testCases = $values['testCases']; + ) { + $this->submissionId = $values['submissionId']; + $this->testCases = $values['testCases']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/GradedResponseV2.php b/seed/php-sdk/trace/src/Submission/Types/GradedResponseV2.php index 42d96ba0c3b6..f4a76a4a2d75 100644 --- a/seed/php-sdk/trace/src/Submission/Types/GradedResponseV2.php +++ b/seed/php-sdk/trace/src/Submission/Types/GradedResponseV2.php @@ -28,15 +28,16 @@ class GradedResponseV2 extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->submissionId = $values['submissionId'];$this->testCases = $values['testCases']; + ) { + $this->submissionId = $values['submissionId']; + $this->testCases = $values['testCases']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/GradedTestCaseUpdate.php b/seed/php-sdk/trace/src/Submission/Types/GradedTestCaseUpdate.php index e4a5cbfc0caa..bd2baeea55b6 100644 --- a/seed/php-sdk/trace/src/Submission/Types/GradedTestCaseUpdate.php +++ b/seed/php-sdk/trace/src/Submission/Types/GradedTestCaseUpdate.php @@ -27,15 +27,16 @@ class GradedTestCaseUpdate extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->testCaseId = $values['testCaseId'];$this->grade = $values['grade']; + ) { + $this->testCaseId = $values['testCaseId']; + $this->grade = $values['grade']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/InitializeProblemRequest.php b/seed/php-sdk/trace/src/Submission/Types/InitializeProblemRequest.php index d38865fed198..470252c1c55e 100644 --- a/seed/php-sdk/trace/src/Submission/Types/InitializeProblemRequest.php +++ b/seed/php-sdk/trace/src/Submission/Types/InitializeProblemRequest.php @@ -27,15 +27,16 @@ class InitializeProblemRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->problemId = $values['problemId'];$this->problemVersion = $values['problemVersion'] ?? null; + ) { + $this->problemId = $values['problemId']; + $this->problemVersion = $values['problemVersion'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/InternalError.php b/seed/php-sdk/trace/src/Submission/Types/InternalError.php index e02b248448fa..c5a8a0558286 100644 --- a/seed/php-sdk/trace/src/Submission/Types/InternalError.php +++ b/seed/php-sdk/trace/src/Submission/Types/InternalError.php @@ -20,15 +20,15 @@ class InternalError extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->exceptionInfo = $values['exceptionInfo']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/InvalidRequestCause.php b/seed/php-sdk/trace/src/Submission/Types/InvalidRequestCause.php index 9bf52de0d43d..507a2853b4fb 100644 --- a/seed/php-sdk/trace/src/Submission/Types/InvalidRequestCause.php +++ b/seed/php-sdk/trace/src/Submission/Types/InvalidRequestCause.php @@ -46,16 +46,17 @@ class InvalidRequestCause extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param SubmissionIdNotFound $submissionIdNotFound * @return InvalidRequestCause */ - public static function submissionIdNotFound(SubmissionIdNotFound $submissionIdNotFound): InvalidRequestCause { + public static function submissionIdNotFound(SubmissionIdNotFound $submissionIdNotFound): InvalidRequestCause + { return new InvalidRequestCause([ 'type' => 'submissionIdNotFound', 'value' => $submissionIdNotFound, @@ -66,7 +67,8 @@ public static function submissionIdNotFound(SubmissionIdNotFound $submissionIdNo * @param CustomTestCasesUnsupported $customTestCasesUnsupported * @return InvalidRequestCause */ - public static function customTestCasesUnsupported(CustomTestCasesUnsupported $customTestCasesUnsupported): InvalidRequestCause { + public static function customTestCasesUnsupported(CustomTestCasesUnsupported $customTestCasesUnsupported): InvalidRequestCause + { return new InvalidRequestCause([ 'type' => 'customTestCasesUnsupported', 'value' => $customTestCasesUnsupported, @@ -77,7 +79,8 @@ public static function customTestCasesUnsupported(CustomTestCasesUnsupported $cu * @param UnexpectedLanguageError $unexpectedLanguage * @return InvalidRequestCause */ - public static function unexpectedLanguage(UnexpectedLanguageError $unexpectedLanguage): InvalidRequestCause { + public static function unexpectedLanguage(UnexpectedLanguageError $unexpectedLanguage): InvalidRequestCause + { return new InvalidRequestCause([ 'type' => 'unexpectedLanguage', 'value' => $unexpectedLanguage, @@ -87,81 +90,89 @@ public static function unexpectedLanguage(UnexpectedLanguageError $unexpectedLan /** * @return bool */ - public function isSubmissionIdNotFound(): bool { - return $this->value instanceof SubmissionIdNotFound&& $this->type === 'submissionIdNotFound'; + public function isSubmissionIdNotFound(): bool + { + return $this->value instanceof SubmissionIdNotFound && $this->type === 'submissionIdNotFound'; } /** * @return SubmissionIdNotFound */ - public function asSubmissionIdNotFound(): SubmissionIdNotFound { - if (!($this->value instanceof SubmissionIdNotFound&& $this->type === 'submissionIdNotFound')){ + public function asSubmissionIdNotFound(): SubmissionIdNotFound + { + if (!($this->value instanceof SubmissionIdNotFound && $this->type === 'submissionIdNotFound')) { throw new Exception( "Expected submissionIdNotFound; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isCustomTestCasesUnsupported(): bool { - return $this->value instanceof CustomTestCasesUnsupported&& $this->type === 'customTestCasesUnsupported'; + public function isCustomTestCasesUnsupported(): bool + { + return $this->value instanceof CustomTestCasesUnsupported && $this->type === 'customTestCasesUnsupported'; } /** * @return CustomTestCasesUnsupported */ - public function asCustomTestCasesUnsupported(): CustomTestCasesUnsupported { - if (!($this->value instanceof CustomTestCasesUnsupported&& $this->type === 'customTestCasesUnsupported')){ + public function asCustomTestCasesUnsupported(): CustomTestCasesUnsupported + { + if (!($this->value instanceof CustomTestCasesUnsupported && $this->type === 'customTestCasesUnsupported')) { throw new Exception( "Expected customTestCasesUnsupported; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isUnexpectedLanguage(): bool { - return $this->value instanceof UnexpectedLanguageError&& $this->type === 'unexpectedLanguage'; + public function isUnexpectedLanguage(): bool + { + return $this->value instanceof UnexpectedLanguageError && $this->type === 'unexpectedLanguage'; } /** * @return UnexpectedLanguageError */ - public function asUnexpectedLanguage(): UnexpectedLanguageError { - if (!($this->value instanceof UnexpectedLanguageError&& $this->type === 'unexpectedLanguage')){ + public function asUnexpectedLanguage(): UnexpectedLanguageError + { + if (!($this->value instanceof UnexpectedLanguageError && $this->type === 'unexpectedLanguage')) { throw new Exception( "Expected unexpectedLanguage; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'submissionIdNotFound': $value = $this->asSubmissionIdNotFound()->jsonSerialize(); $result = array_merge($value, $result); @@ -176,26 +187,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -204,22 +216,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'submissionIdNotFound': $args['value'] = SubmissionIdNotFound::jsonDeserialize($data); break; @@ -234,7 +247,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/trace/src/Submission/Types/InvalidRequestResponse.php b/seed/php-sdk/trace/src/Submission/Types/InvalidRequestResponse.php index f71582d8e9bc..81ab07a52e2c 100644 --- a/seed/php-sdk/trace/src/Submission/Types/InvalidRequestResponse.php +++ b/seed/php-sdk/trace/src/Submission/Types/InvalidRequestResponse.php @@ -27,15 +27,16 @@ class InvalidRequestResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->request = $values['request'];$this->cause = $values['cause']; + ) { + $this->request = $values['request']; + $this->cause = $values['cause']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/LightweightStackframeInformation.php b/seed/php-sdk/trace/src/Submission/Types/LightweightStackframeInformation.php index d12097f11b9e..b80edd98f678 100644 --- a/seed/php-sdk/trace/src/Submission/Types/LightweightStackframeInformation.php +++ b/seed/php-sdk/trace/src/Submission/Types/LightweightStackframeInformation.php @@ -27,15 +27,16 @@ class LightweightStackframeInformation extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->numStackFrames = $values['numStackFrames'];$this->topStackFrameMethodName = $values['topStackFrameMethodName']; + ) { + $this->numStackFrames = $values['numStackFrames']; + $this->topStackFrameMethodName = $values['topStackFrameMethodName']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/RecordedResponseNotification.php b/seed/php-sdk/trace/src/Submission/Types/RecordedResponseNotification.php index a3783d21ee08..ebe150cc75c8 100644 --- a/seed/php-sdk/trace/src/Submission/Types/RecordedResponseNotification.php +++ b/seed/php-sdk/trace/src/Submission/Types/RecordedResponseNotification.php @@ -34,15 +34,17 @@ class RecordedResponseNotification extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->submissionId = $values['submissionId'];$this->traceResponsesSize = $values['traceResponsesSize'];$this->testCaseId = $values['testCaseId'] ?? null; + ) { + $this->submissionId = $values['submissionId']; + $this->traceResponsesSize = $values['traceResponsesSize']; + $this->testCaseId = $values['testCaseId'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/RecordedTestCaseUpdate.php b/seed/php-sdk/trace/src/Submission/Types/RecordedTestCaseUpdate.php index 8d2bf4cc0bca..f45787749dfa 100644 --- a/seed/php-sdk/trace/src/Submission/Types/RecordedTestCaseUpdate.php +++ b/seed/php-sdk/trace/src/Submission/Types/RecordedTestCaseUpdate.php @@ -27,15 +27,16 @@ class RecordedTestCaseUpdate extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->testCaseId = $values['testCaseId'];$this->traceResponsesSize = $values['traceResponsesSize']; + ) { + $this->testCaseId = $values['testCaseId']; + $this->traceResponsesSize = $values['traceResponsesSize']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/RecordingResponseNotification.php b/seed/php-sdk/trace/src/Submission/Types/RecordingResponseNotification.php index e31f8210365b..8842760b9d1c 100644 --- a/seed/php-sdk/trace/src/Submission/Types/RecordingResponseNotification.php +++ b/seed/php-sdk/trace/src/Submission/Types/RecordingResponseNotification.php @@ -48,15 +48,19 @@ class RecordingResponseNotification extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->submissionId = $values['submissionId'];$this->testCaseId = $values['testCaseId'] ?? null;$this->lineNumber = $values['lineNumber'];$this->lightweightStackInfo = $values['lightweightStackInfo'];$this->tracedFile = $values['tracedFile'] ?? null; + ) { + $this->submissionId = $values['submissionId']; + $this->testCaseId = $values['testCaseId'] ?? null; + $this->lineNumber = $values['lineNumber']; + $this->lightweightStackInfo = $values['lightweightStackInfo']; + $this->tracedFile = $values['tracedFile'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/RunningResponse.php b/seed/php-sdk/trace/src/Submission/Types/RunningResponse.php index f69539342f84..ba9b524007bf 100644 --- a/seed/php-sdk/trace/src/Submission/Types/RunningResponse.php +++ b/seed/php-sdk/trace/src/Submission/Types/RunningResponse.php @@ -27,15 +27,16 @@ class RunningResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->submissionId = $values['submissionId'];$this->state = $values['state']; + ) { + $this->submissionId = $values['submissionId']; + $this->state = $values['state']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/RunningSubmissionState.php b/seed/php-sdk/trace/src/Submission/Types/RunningSubmissionState.php index 6f03f866877b..9e4a8f49f405 100644 --- a/seed/php-sdk/trace/src/Submission/Types/RunningSubmissionState.php +++ b/seed/php-sdk/trace/src/Submission/Types/RunningSubmissionState.php @@ -2,8 +2,8 @@ namespace Seed\Submission\Types; -enum RunningSubmissionState - : string { +enum RunningSubmissionState: string +{ case QueueingSubmission = "QUEUEING_SUBMISSION"; case KillingHistoricalSubmissions = "KILLING_HISTORICAL_SUBMISSIONS"; case WritingSubmissionToFile = "WRITING_SUBMISSION_TO_FILE"; diff --git a/seed/php-sdk/trace/src/Submission/Types/RuntimeError.php b/seed/php-sdk/trace/src/Submission/Types/RuntimeError.php index 2dd19332cb16..2c91aca86ddd 100644 --- a/seed/php-sdk/trace/src/Submission/Types/RuntimeError.php +++ b/seed/php-sdk/trace/src/Submission/Types/RuntimeError.php @@ -20,15 +20,15 @@ class RuntimeError extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->message = $values['message']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/Scope.php b/seed/php-sdk/trace/src/Submission/Types/Scope.php index 04e732193f7d..d56d3ee55e8f 100644 --- a/seed/php-sdk/trace/src/Submission/Types/Scope.php +++ b/seed/php-sdk/trace/src/Submission/Types/Scope.php @@ -22,15 +22,15 @@ class Scope extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->variables = $values['variables']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/StackFrame.php b/seed/php-sdk/trace/src/Submission/Types/StackFrame.php index 5e6ec5f42348..0c3031886f7f 100644 --- a/seed/php-sdk/trace/src/Submission/Types/StackFrame.php +++ b/seed/php-sdk/trace/src/Submission/Types/StackFrame.php @@ -35,15 +35,17 @@ class StackFrame extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->methodName = $values['methodName'];$this->lineNumber = $values['lineNumber'];$this->scopes = $values['scopes']; + ) { + $this->methodName = $values['methodName']; + $this->lineNumber = $values['lineNumber']; + $this->scopes = $values['scopes']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/StackInformation.php b/seed/php-sdk/trace/src/Submission/Types/StackInformation.php index e377b505e42f..3a9cd23d3092 100644 --- a/seed/php-sdk/trace/src/Submission/Types/StackInformation.php +++ b/seed/php-sdk/trace/src/Submission/Types/StackInformation.php @@ -27,15 +27,16 @@ class StackInformation extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->numStackFrames = $values['numStackFrames'];$this->topStackFrame = $values['topStackFrame'] ?? null; + ) { + $this->numStackFrames = $values['numStackFrames']; + $this->topStackFrame = $values['topStackFrame'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/StderrResponse.php b/seed/php-sdk/trace/src/Submission/Types/StderrResponse.php index f77c90ec1afb..c34a776b8997 100644 --- a/seed/php-sdk/trace/src/Submission/Types/StderrResponse.php +++ b/seed/php-sdk/trace/src/Submission/Types/StderrResponse.php @@ -27,15 +27,16 @@ class StderrResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->submissionId = $values['submissionId'];$this->stderr = $values['stderr']; + ) { + $this->submissionId = $values['submissionId']; + $this->stderr = $values['stderr']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/StdoutResponse.php b/seed/php-sdk/trace/src/Submission/Types/StdoutResponse.php index f06012086a3a..565a7ba20fee 100644 --- a/seed/php-sdk/trace/src/Submission/Types/StdoutResponse.php +++ b/seed/php-sdk/trace/src/Submission/Types/StdoutResponse.php @@ -27,15 +27,16 @@ class StdoutResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->submissionId = $values['submissionId'];$this->stdout = $values['stdout']; + ) { + $this->submissionId = $values['submissionId']; + $this->stdout = $values['stdout']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/StopRequest.php b/seed/php-sdk/trace/src/Submission/Types/StopRequest.php index bd6dae07958e..8251edd72a4b 100644 --- a/seed/php-sdk/trace/src/Submission/Types/StopRequest.php +++ b/seed/php-sdk/trace/src/Submission/Types/StopRequest.php @@ -20,15 +20,15 @@ class StopRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->submissionId = $values['submissionId']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/StoppedResponse.php b/seed/php-sdk/trace/src/Submission/Types/StoppedResponse.php index c21ec7cc2d28..76e8b670f483 100644 --- a/seed/php-sdk/trace/src/Submission/Types/StoppedResponse.php +++ b/seed/php-sdk/trace/src/Submission/Types/StoppedResponse.php @@ -20,15 +20,15 @@ class StoppedResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->submissionId = $values['submissionId']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/SubmissionFileInfo.php b/seed/php-sdk/trace/src/Submission/Types/SubmissionFileInfo.php index ceb1922df7c1..db13770eaa01 100644 --- a/seed/php-sdk/trace/src/Submission/Types/SubmissionFileInfo.php +++ b/seed/php-sdk/trace/src/Submission/Types/SubmissionFileInfo.php @@ -34,15 +34,17 @@ class SubmissionFileInfo extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->directory = $values['directory'];$this->filename = $values['filename'];$this->contents = $values['contents']; + ) { + $this->directory = $values['directory']; + $this->filename = $values['filename']; + $this->contents = $values['contents']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/SubmissionIdNotFound.php b/seed/php-sdk/trace/src/Submission/Types/SubmissionIdNotFound.php index e11109cbf568..c174adf5906a 100644 --- a/seed/php-sdk/trace/src/Submission/Types/SubmissionIdNotFound.php +++ b/seed/php-sdk/trace/src/Submission/Types/SubmissionIdNotFound.php @@ -20,15 +20,15 @@ class SubmissionIdNotFound extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->missingSubmissionId = $values['missingSubmissionId']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/SubmissionRequest.php b/seed/php-sdk/trace/src/Submission/Types/SubmissionRequest.php index 18daae8973c8..800245a46d1a 100644 --- a/seed/php-sdk/trace/src/Submission/Types/SubmissionRequest.php +++ b/seed/php-sdk/trace/src/Submission/Types/SubmissionRequest.php @@ -54,16 +54,17 @@ class SubmissionRequest extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param InitializeProblemRequest $initializeProblemRequest * @return SubmissionRequest */ - public static function initializeProblemRequest(InitializeProblemRequest $initializeProblemRequest): SubmissionRequest { + public static function initializeProblemRequest(InitializeProblemRequest $initializeProblemRequest): SubmissionRequest + { return new SubmissionRequest([ 'type' => 'initializeProblemRequest', 'value' => $initializeProblemRequest, @@ -73,7 +74,8 @@ public static function initializeProblemRequest(InitializeProblemRequest $initia /** * @return SubmissionRequest */ - public static function initializeWorkspaceRequest(): SubmissionRequest { + public static function initializeWorkspaceRequest(): SubmissionRequest + { return new SubmissionRequest([ 'type' => 'initializeWorkspaceRequest', 'value' => null, @@ -84,7 +86,8 @@ public static function initializeWorkspaceRequest(): SubmissionRequest { * @param SubmitRequestV2 $submitV2 * @return SubmissionRequest */ - public static function submitV2(SubmitRequestV2 $submitV2): SubmissionRequest { + public static function submitV2(SubmitRequestV2 $submitV2): SubmissionRequest + { return new SubmissionRequest([ 'type' => 'submitV2', 'value' => $submitV2, @@ -95,7 +98,8 @@ public static function submitV2(SubmitRequestV2 $submitV2): SubmissionRequest { * @param WorkspaceSubmitRequest $workspaceSubmit * @return SubmissionRequest */ - public static function workspaceSubmit(WorkspaceSubmitRequest $workspaceSubmit): SubmissionRequest { + public static function workspaceSubmit(WorkspaceSubmitRequest $workspaceSubmit): SubmissionRequest + { return new SubmissionRequest([ 'type' => 'workspaceSubmit', 'value' => $workspaceSubmit, @@ -106,7 +110,8 @@ public static function workspaceSubmit(WorkspaceSubmitRequest $workspaceSubmit): * @param StopRequest $stop * @return SubmissionRequest */ - public static function stop(StopRequest $stop): SubmissionRequest { + public static function stop(StopRequest $stop): SubmissionRequest + { return new SubmissionRequest([ 'type' => 'stop', 'value' => $stop, @@ -116,108 +121,119 @@ public static function stop(StopRequest $stop): SubmissionRequest { /** * @return bool */ - public function isInitializeProblemRequest(): bool { - return $this->value instanceof InitializeProblemRequest&& $this->type === 'initializeProblemRequest'; + public function isInitializeProblemRequest(): bool + { + return $this->value instanceof InitializeProblemRequest && $this->type === 'initializeProblemRequest'; } /** * @return InitializeProblemRequest */ - public function asInitializeProblemRequest(): InitializeProblemRequest { - if (!($this->value instanceof InitializeProblemRequest&& $this->type === 'initializeProblemRequest')){ + public function asInitializeProblemRequest(): InitializeProblemRequest + { + if (!($this->value instanceof InitializeProblemRequest && $this->type === 'initializeProblemRequest')) { throw new Exception( "Expected initializeProblemRequest; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isInitializeWorkspaceRequest(): bool { - return is_null($this->value)&& $this->type === 'initializeWorkspaceRequest'; + public function isInitializeWorkspaceRequest(): bool + { + return is_null($this->value) && $this->type === 'initializeWorkspaceRequest'; } /** * @return bool */ - public function isSubmitV2(): bool { - return $this->value instanceof SubmitRequestV2&& $this->type === 'submitV2'; + public function isSubmitV2(): bool + { + return $this->value instanceof SubmitRequestV2 && $this->type === 'submitV2'; } /** * @return SubmitRequestV2 */ - public function asSubmitV2(): SubmitRequestV2 { - if (!($this->value instanceof SubmitRequestV2&& $this->type === 'submitV2')){ + public function asSubmitV2(): SubmitRequestV2 + { + if (!($this->value instanceof SubmitRequestV2 && $this->type === 'submitV2')) { throw new Exception( "Expected submitV2; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isWorkspaceSubmit(): bool { - return $this->value instanceof WorkspaceSubmitRequest&& $this->type === 'workspaceSubmit'; + public function isWorkspaceSubmit(): bool + { + return $this->value instanceof WorkspaceSubmitRequest && $this->type === 'workspaceSubmit'; } /** * @return WorkspaceSubmitRequest */ - public function asWorkspaceSubmit(): WorkspaceSubmitRequest { - if (!($this->value instanceof WorkspaceSubmitRequest&& $this->type === 'workspaceSubmit')){ + public function asWorkspaceSubmit(): WorkspaceSubmitRequest + { + if (!($this->value instanceof WorkspaceSubmitRequest && $this->type === 'workspaceSubmit')) { throw new Exception( "Expected workspaceSubmit; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isStop(): bool { - return $this->value instanceof StopRequest&& $this->type === 'stop'; + public function isStop(): bool + { + return $this->value instanceof StopRequest && $this->type === 'stop'; } /** * @return StopRequest */ - public function asStop(): StopRequest { - if (!($this->value instanceof StopRequest&& $this->type === 'stop')){ + public function asStop(): StopRequest + { + if (!($this->value instanceof StopRequest && $this->type === 'stop')) { throw new Exception( "Expected stop; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'initializeProblemRequest': $value = $this->asInitializeProblemRequest()->jsonSerialize(); $result = array_merge($value, $result); @@ -239,26 +255,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -267,22 +284,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'initializeProblemRequest': $args['value'] = InitializeProblemRequest::jsonDeserialize($data); break; @@ -303,7 +321,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/trace/src/Submission/Types/SubmissionResponse.php b/seed/php-sdk/trace/src/Submission/Types/SubmissionResponse.php index 60fa212b9d5d..a1d246b771eb 100644 --- a/seed/php-sdk/trace/src/Submission/Types/SubmissionResponse.php +++ b/seed/php-sdk/trace/src/Submission/Types/SubmissionResponse.php @@ -56,15 +56,16 @@ class SubmissionResponse extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @return SubmissionResponse */ - public static function serverInitialized(): SubmissionResponse { + public static function serverInitialized(): SubmissionResponse + { return new SubmissionResponse([ 'type' => 'serverInitialized', 'value' => null, @@ -75,7 +76,8 @@ public static function serverInitialized(): SubmissionResponse { * @param string $problemInitialized * @return SubmissionResponse */ - public static function problemInitialized(string $problemInitialized): SubmissionResponse { + public static function problemInitialized(string $problemInitialized): SubmissionResponse + { return new SubmissionResponse([ 'type' => 'problemInitialized', 'value' => $problemInitialized, @@ -85,7 +87,8 @@ public static function problemInitialized(string $problemInitialized): Submissio /** * @return SubmissionResponse */ - public static function workspaceInitialized(): SubmissionResponse { + public static function workspaceInitialized(): SubmissionResponse + { return new SubmissionResponse([ 'type' => 'workspaceInitialized', 'value' => null, @@ -96,7 +99,8 @@ public static function workspaceInitialized(): SubmissionResponse { * @param ExceptionInfo $serverErrored * @return SubmissionResponse */ - public static function serverErrored(ExceptionInfo $serverErrored): SubmissionResponse { + public static function serverErrored(ExceptionInfo $serverErrored): SubmissionResponse + { return new SubmissionResponse([ 'type' => 'serverErrored', 'value' => $serverErrored, @@ -107,7 +111,8 @@ public static function serverErrored(ExceptionInfo $serverErrored): SubmissionRe * @param CodeExecutionUpdate $codeExecutionUpdate * @return SubmissionResponse */ - public static function codeExecutionUpdate(CodeExecutionUpdate $codeExecutionUpdate): SubmissionResponse { + public static function codeExecutionUpdate(CodeExecutionUpdate $codeExecutionUpdate): SubmissionResponse + { return new SubmissionResponse([ 'type' => 'codeExecutionUpdate', 'value' => $codeExecutionUpdate, @@ -118,7 +123,8 @@ public static function codeExecutionUpdate(CodeExecutionUpdate $codeExecutionUpd * @param TerminatedResponse $terminated * @return SubmissionResponse */ - public static function terminated(TerminatedResponse $terminated): SubmissionResponse { + public static function terminated(TerminatedResponse $terminated): SubmissionResponse + { return new SubmissionResponse([ 'type' => 'terminated', 'value' => $terminated, @@ -128,115 +134,127 @@ public static function terminated(TerminatedResponse $terminated): SubmissionRes /** * @return bool */ - public function isServerInitialized(): bool { - return is_null($this->value)&& $this->type === 'serverInitialized'; + public function isServerInitialized(): bool + { + return is_null($this->value) && $this->type === 'serverInitialized'; } /** * @return bool */ - public function isProblemInitialized(): bool { - return is_string($this->value)&& $this->type === 'problemInitialized'; + public function isProblemInitialized(): bool + { + return is_string($this->value) && $this->type === 'problemInitialized'; } /** * @return string */ - public function asProblemInitialized(): string { - if (!(is_string($this->value)&& $this->type === 'problemInitialized')){ + public function asProblemInitialized(): string + { + if (!(is_string($this->value) && $this->type === 'problemInitialized')) { throw new Exception( "Expected problemInitialized; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isWorkspaceInitialized(): bool { - return is_null($this->value)&& $this->type === 'workspaceInitialized'; + public function isWorkspaceInitialized(): bool + { + return is_null($this->value) && $this->type === 'workspaceInitialized'; } /** * @return bool */ - public function isServerErrored(): bool { - return $this->value instanceof ExceptionInfo&& $this->type === 'serverErrored'; + public function isServerErrored(): bool + { + return $this->value instanceof ExceptionInfo && $this->type === 'serverErrored'; } /** * @return ExceptionInfo */ - public function asServerErrored(): ExceptionInfo { - if (!($this->value instanceof ExceptionInfo&& $this->type === 'serverErrored')){ + public function asServerErrored(): ExceptionInfo + { + if (!($this->value instanceof ExceptionInfo && $this->type === 'serverErrored')) { throw new Exception( "Expected serverErrored; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isCodeExecutionUpdate(): bool { - return $this->value instanceof CodeExecutionUpdate&& $this->type === 'codeExecutionUpdate'; + public function isCodeExecutionUpdate(): bool + { + return $this->value instanceof CodeExecutionUpdate && $this->type === 'codeExecutionUpdate'; } /** * @return CodeExecutionUpdate */ - public function asCodeExecutionUpdate(): CodeExecutionUpdate { - if (!($this->value instanceof CodeExecutionUpdate&& $this->type === 'codeExecutionUpdate')){ + public function asCodeExecutionUpdate(): CodeExecutionUpdate + { + if (!($this->value instanceof CodeExecutionUpdate && $this->type === 'codeExecutionUpdate')) { throw new Exception( "Expected codeExecutionUpdate; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isTerminated(): bool { - return $this->value instanceof TerminatedResponse&& $this->type === 'terminated'; + public function isTerminated(): bool + { + return $this->value instanceof TerminatedResponse && $this->type === 'terminated'; } /** * @return TerminatedResponse */ - public function asTerminated(): TerminatedResponse { - if (!($this->value instanceof TerminatedResponse&& $this->type === 'terminated')){ + public function asTerminated(): TerminatedResponse + { + if (!($this->value instanceof TerminatedResponse && $this->type === 'terminated')) { throw new Exception( "Expected terminated; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'serverInitialized': $result['serverInitialized'] = []; break; @@ -261,26 +279,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -289,32 +308,33 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'serverInitialized': $args['value'] = null; break; case 'problemInitialized': - if (!array_key_exists('problemInitialized', $data)){ + if (!array_key_exists('problemInitialized', $data)) { throw new Exception( "JSON data is missing property 'problemInitialized'", ); } - + $args['value'] = $data['problemInitialized']; break; case 'workspaceInitialized': @@ -324,13 +344,13 @@ public static function jsonDeserialize(array $data): static { $args['value'] = ExceptionInfo::jsonDeserialize($data); break; case 'codeExecutionUpdate': - if (!array_key_exists('codeExecutionUpdate', $data)){ + if (!array_key_exists('codeExecutionUpdate', $data)) { throw new Exception( "JSON data is missing property 'codeExecutionUpdate'", ); } - - if (!(is_array($data['codeExecutionUpdate']))){ + + if (!(is_array($data['codeExecutionUpdate']))) { throw new Exception( "Expected property 'codeExecutionUpdate' in JSON data to be array, instead received " . get_debug_type($data['codeExecutionUpdate']), ); @@ -345,7 +365,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/trace/src/Submission/Types/SubmissionStatusForTestCase.php b/seed/php-sdk/trace/src/Submission/Types/SubmissionStatusForTestCase.php index 9556febe57a0..0a1ee05794be 100644 --- a/seed/php-sdk/trace/src/Submission/Types/SubmissionStatusForTestCase.php +++ b/seed/php-sdk/trace/src/Submission/Types/SubmissionStatusForTestCase.php @@ -46,16 +46,17 @@ class SubmissionStatusForTestCase extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param TestCaseResultWithStdout $graded * @return SubmissionStatusForTestCase */ - public static function graded(TestCaseResultWithStdout $graded): SubmissionStatusForTestCase { + public static function graded(TestCaseResultWithStdout $graded): SubmissionStatusForTestCase + { return new SubmissionStatusForTestCase([ 'type' => 'graded', 'value' => $graded, @@ -66,7 +67,8 @@ public static function graded(TestCaseResultWithStdout $graded): SubmissionStatu * @param TestCaseGrade $gradedV2 * @return SubmissionStatusForTestCase */ - public static function gradedV2(TestCaseGrade $gradedV2): SubmissionStatusForTestCase { + public static function gradedV2(TestCaseGrade $gradedV2): SubmissionStatusForTestCase + { return new SubmissionStatusForTestCase([ 'type' => 'gradedV2', 'value' => $gradedV2, @@ -77,7 +79,8 @@ public static function gradedV2(TestCaseGrade $gradedV2): SubmissionStatusForTes * @param TracedTestCase $traced * @return SubmissionStatusForTestCase */ - public static function traced(TracedTestCase $traced): SubmissionStatusForTestCase { + public static function traced(TracedTestCase $traced): SubmissionStatusForTestCase + { return new SubmissionStatusForTestCase([ 'type' => 'traced', 'value' => $traced, @@ -87,81 +90,89 @@ public static function traced(TracedTestCase $traced): SubmissionStatusForTestCa /** * @return bool */ - public function isGraded(): bool { - return $this->value instanceof TestCaseResultWithStdout&& $this->type === 'graded'; + public function isGraded(): bool + { + return $this->value instanceof TestCaseResultWithStdout && $this->type === 'graded'; } /** * @return TestCaseResultWithStdout */ - public function asGraded(): TestCaseResultWithStdout { - if (!($this->value instanceof TestCaseResultWithStdout&& $this->type === 'graded')){ + public function asGraded(): TestCaseResultWithStdout + { + if (!($this->value instanceof TestCaseResultWithStdout && $this->type === 'graded')) { throw new Exception( "Expected graded; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isGradedV2(): bool { - return $this->value instanceof TestCaseGrade&& $this->type === 'gradedV2'; + public function isGradedV2(): bool + { + return $this->value instanceof TestCaseGrade && $this->type === 'gradedV2'; } /** * @return TestCaseGrade */ - public function asGradedV2(): TestCaseGrade { - if (!($this->value instanceof TestCaseGrade&& $this->type === 'gradedV2')){ + public function asGradedV2(): TestCaseGrade + { + if (!($this->value instanceof TestCaseGrade && $this->type === 'gradedV2')) { throw new Exception( "Expected gradedV2; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isTraced(): bool { - return $this->value instanceof TracedTestCase&& $this->type === 'traced'; + public function isTraced(): bool + { + return $this->value instanceof TracedTestCase && $this->type === 'traced'; } /** * @return TracedTestCase */ - public function asTraced(): TracedTestCase { - if (!($this->value instanceof TracedTestCase&& $this->type === 'traced')){ + public function asTraced(): TracedTestCase + { + if (!($this->value instanceof TracedTestCase && $this->type === 'traced')) { throw new Exception( "Expected traced; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'graded': $value = $this->asGraded()->jsonSerialize(); $result = array_merge($value, $result); @@ -176,26 +187,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -204,33 +216,34 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'graded': $args['value'] = TestCaseResultWithStdout::jsonDeserialize($data); break; case 'gradedV2': - if (!array_key_exists('gradedV2', $data)){ + if (!array_key_exists('gradedV2', $data)) { throw new Exception( "JSON data is missing property 'gradedV2'", ); } - - if (!(is_array($data['gradedV2']))){ + + if (!(is_array($data['gradedV2']))) { throw new Exception( "Expected property 'gradedV2' in JSON data to be array, instead received " . get_debug_type($data['gradedV2']), ); @@ -245,7 +258,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/trace/src/Submission/Types/SubmissionStatusV2.php b/seed/php-sdk/trace/src/Submission/Types/SubmissionStatusV2.php index a64453e85611..26f1f8c21373 100644 --- a/seed/php-sdk/trace/src/Submission/Types/SubmissionStatusV2.php +++ b/seed/php-sdk/trace/src/Submission/Types/SubmissionStatusV2.php @@ -42,16 +42,17 @@ class SubmissionStatusV2 extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param TestSubmissionStatusV2 $test * @return SubmissionStatusV2 */ - public static function test(TestSubmissionStatusV2 $test): SubmissionStatusV2 { + public static function test(TestSubmissionStatusV2 $test): SubmissionStatusV2 + { return new SubmissionStatusV2([ 'type' => 'test', 'value' => $test, @@ -62,7 +63,8 @@ public static function test(TestSubmissionStatusV2 $test): SubmissionStatusV2 { * @param WorkspaceSubmissionStatusV2 $workspace * @return SubmissionStatusV2 */ - public static function workspace(WorkspaceSubmissionStatusV2 $workspace): SubmissionStatusV2 { + public static function workspace(WorkspaceSubmissionStatusV2 $workspace): SubmissionStatusV2 + { return new SubmissionStatusV2([ 'type' => 'workspace', 'value' => $workspace, @@ -72,61 +74,67 @@ public static function workspace(WorkspaceSubmissionStatusV2 $workspace): Submis /** * @return bool */ - public function isTest(): bool { - return $this->value instanceof TestSubmissionStatusV2&& $this->type === 'test'; + public function isTest(): bool + { + return $this->value instanceof TestSubmissionStatusV2 && $this->type === 'test'; } /** * @return TestSubmissionStatusV2 */ - public function asTest(): TestSubmissionStatusV2 { - if (!($this->value instanceof TestSubmissionStatusV2&& $this->type === 'test')){ + public function asTest(): TestSubmissionStatusV2 + { + if (!($this->value instanceof TestSubmissionStatusV2 && $this->type === 'test')) { throw new Exception( "Expected test; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isWorkspace(): bool { - return $this->value instanceof WorkspaceSubmissionStatusV2&& $this->type === 'workspace'; + public function isWorkspace(): bool + { + return $this->value instanceof WorkspaceSubmissionStatusV2 && $this->type === 'workspace'; } /** * @return WorkspaceSubmissionStatusV2 */ - public function asWorkspace(): WorkspaceSubmissionStatusV2 { - if (!($this->value instanceof WorkspaceSubmissionStatusV2&& $this->type === 'workspace')){ + public function asWorkspace(): WorkspaceSubmissionStatusV2 + { + if (!($this->value instanceof WorkspaceSubmissionStatusV2 && $this->type === 'workspace')) { throw new Exception( "Expected workspace; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'test': $value = $this->asTest()->jsonSerialize(); $result = array_merge($value, $result); @@ -137,26 +145,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -165,22 +174,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'test': $args['value'] = TestSubmissionStatusV2::jsonDeserialize($data); break; @@ -192,7 +202,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/trace/src/Submission/Types/SubmissionTypeEnum.php b/seed/php-sdk/trace/src/Submission/Types/SubmissionTypeEnum.php index b2dad93e0ce7..4875731751c0 100644 --- a/seed/php-sdk/trace/src/Submission/Types/SubmissionTypeEnum.php +++ b/seed/php-sdk/trace/src/Submission/Types/SubmissionTypeEnum.php @@ -2,7 +2,7 @@ namespace Seed\Submission\Types; -enum SubmissionTypeEnum - : string { +enum SubmissionTypeEnum: string +{ case Test = "TEST"; } diff --git a/seed/php-sdk/trace/src/Submission/Types/SubmissionTypeState.php b/seed/php-sdk/trace/src/Submission/Types/SubmissionTypeState.php index e7c2b1825587..f109561a5669 100644 --- a/seed/php-sdk/trace/src/Submission/Types/SubmissionTypeState.php +++ b/seed/php-sdk/trace/src/Submission/Types/SubmissionTypeState.php @@ -42,16 +42,17 @@ class SubmissionTypeState extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param TestSubmissionState $test * @return SubmissionTypeState */ - public static function test(TestSubmissionState $test): SubmissionTypeState { + public static function test(TestSubmissionState $test): SubmissionTypeState + { return new SubmissionTypeState([ 'type' => 'test', 'value' => $test, @@ -62,7 +63,8 @@ public static function test(TestSubmissionState $test): SubmissionTypeState { * @param WorkspaceSubmissionState $workspace * @return SubmissionTypeState */ - public static function workspace(WorkspaceSubmissionState $workspace): SubmissionTypeState { + public static function workspace(WorkspaceSubmissionState $workspace): SubmissionTypeState + { return new SubmissionTypeState([ 'type' => 'workspace', 'value' => $workspace, @@ -72,61 +74,67 @@ public static function workspace(WorkspaceSubmissionState $workspace): Submissio /** * @return bool */ - public function isTest(): bool { - return $this->value instanceof TestSubmissionState&& $this->type === 'test'; + public function isTest(): bool + { + return $this->value instanceof TestSubmissionState && $this->type === 'test'; } /** * @return TestSubmissionState */ - public function asTest(): TestSubmissionState { - if (!($this->value instanceof TestSubmissionState&& $this->type === 'test')){ + public function asTest(): TestSubmissionState + { + if (!($this->value instanceof TestSubmissionState && $this->type === 'test')) { throw new Exception( "Expected test; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isWorkspace(): bool { - return $this->value instanceof WorkspaceSubmissionState&& $this->type === 'workspace'; + public function isWorkspace(): bool + { + return $this->value instanceof WorkspaceSubmissionState && $this->type === 'workspace'; } /** * @return WorkspaceSubmissionState */ - public function asWorkspace(): WorkspaceSubmissionState { - if (!($this->value instanceof WorkspaceSubmissionState&& $this->type === 'workspace')){ + public function asWorkspace(): WorkspaceSubmissionState + { + if (!($this->value instanceof WorkspaceSubmissionState && $this->type === 'workspace')) { throw new Exception( "Expected workspace; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'test': $value = $this->asTest()->jsonSerialize(); $result = array_merge($value, $result); @@ -137,26 +145,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -165,22 +174,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'test': $args['value'] = TestSubmissionState::jsonDeserialize($data); break; @@ -192,7 +202,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/trace/src/Submission/Types/SubmitRequestV2.php b/seed/php-sdk/trace/src/Submission/Types/SubmitRequestV2.php index 6eff4bd85fa6..90578453af26 100644 --- a/seed/php-sdk/trace/src/Submission/Types/SubmitRequestV2.php +++ b/seed/php-sdk/trace/src/Submission/Types/SubmitRequestV2.php @@ -57,15 +57,20 @@ class SubmitRequestV2 extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->submissionId = $values['submissionId'];$this->language = $values['language'];$this->submissionFiles = $values['submissionFiles'];$this->problemId = $values['problemId'];$this->problemVersion = $values['problemVersion'] ?? null;$this->userId = $values['userId'] ?? null; + ) { + $this->submissionId = $values['submissionId']; + $this->language = $values['language']; + $this->submissionFiles = $values['submissionFiles']; + $this->problemId = $values['problemId']; + $this->problemVersion = $values['problemVersion'] ?? null; + $this->userId = $values['userId'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/TerminatedResponse.php b/seed/php-sdk/trace/src/Submission/Types/TerminatedResponse.php index afe670c74035..1b9fe0eb54c5 100644 --- a/seed/php-sdk/trace/src/Submission/Types/TerminatedResponse.php +++ b/seed/php-sdk/trace/src/Submission/Types/TerminatedResponse.php @@ -6,22 +6,21 @@ class TerminatedResponse extends JsonSerializableType { - /** * @param array{ * } $values */ public function __construct( array $values = [], - ) - { + ) { unset($values); } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/TestCaseGrade.php b/seed/php-sdk/trace/src/Submission/Types/TestCaseGrade.php index 38685eaa6539..503a04e085f4 100644 --- a/seed/php-sdk/trace/src/Submission/Types/TestCaseGrade.php +++ b/seed/php-sdk/trace/src/Submission/Types/TestCaseGrade.php @@ -42,16 +42,17 @@ class TestCaseGrade extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param TestCaseHiddenGrade $hidden * @return TestCaseGrade */ - public static function hidden(TestCaseHiddenGrade $hidden): TestCaseGrade { + public static function hidden(TestCaseHiddenGrade $hidden): TestCaseGrade + { return new TestCaseGrade([ 'type' => 'hidden', 'value' => $hidden, @@ -62,7 +63,8 @@ public static function hidden(TestCaseHiddenGrade $hidden): TestCaseGrade { * @param TestCaseNonHiddenGrade $nonHidden * @return TestCaseGrade */ - public static function nonHidden(TestCaseNonHiddenGrade $nonHidden): TestCaseGrade { + public static function nonHidden(TestCaseNonHiddenGrade $nonHidden): TestCaseGrade + { return new TestCaseGrade([ 'type' => 'nonHidden', 'value' => $nonHidden, @@ -72,61 +74,67 @@ public static function nonHidden(TestCaseNonHiddenGrade $nonHidden): TestCaseGra /** * @return bool */ - public function isHidden(): bool { - return $this->value instanceof TestCaseHiddenGrade&& $this->type === 'hidden'; + public function isHidden(): bool + { + return $this->value instanceof TestCaseHiddenGrade && $this->type === 'hidden'; } /** * @return TestCaseHiddenGrade */ - public function asHidden(): TestCaseHiddenGrade { - if (!($this->value instanceof TestCaseHiddenGrade&& $this->type === 'hidden')){ + public function asHidden(): TestCaseHiddenGrade + { + if (!($this->value instanceof TestCaseHiddenGrade && $this->type === 'hidden')) { throw new Exception( "Expected hidden; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isNonHidden(): bool { - return $this->value instanceof TestCaseNonHiddenGrade&& $this->type === 'nonHidden'; + public function isNonHidden(): bool + { + return $this->value instanceof TestCaseNonHiddenGrade && $this->type === 'nonHidden'; } /** * @return TestCaseNonHiddenGrade */ - public function asNonHidden(): TestCaseNonHiddenGrade { - if (!($this->value instanceof TestCaseNonHiddenGrade&& $this->type === 'nonHidden')){ + public function asNonHidden(): TestCaseNonHiddenGrade + { + if (!($this->value instanceof TestCaseNonHiddenGrade && $this->type === 'nonHidden')) { throw new Exception( "Expected nonHidden; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'hidden': $value = $this->asHidden()->jsonSerialize(); $result = array_merge($value, $result); @@ -137,26 +145,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -165,22 +174,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'hidden': $args['value'] = TestCaseHiddenGrade::jsonDeserialize($data); break; @@ -192,7 +202,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/trace/src/Submission/Types/TestCaseHiddenGrade.php b/seed/php-sdk/trace/src/Submission/Types/TestCaseHiddenGrade.php index 7766925ec8b4..3678fbe7deb6 100644 --- a/seed/php-sdk/trace/src/Submission/Types/TestCaseHiddenGrade.php +++ b/seed/php-sdk/trace/src/Submission/Types/TestCaseHiddenGrade.php @@ -20,15 +20,15 @@ class TestCaseHiddenGrade extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->passed = $values['passed']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/TestCaseNonHiddenGrade.php b/seed/php-sdk/trace/src/Submission/Types/TestCaseNonHiddenGrade.php index 1fcec086555d..94b43757f64e 100644 --- a/seed/php-sdk/trace/src/Submission/Types/TestCaseNonHiddenGrade.php +++ b/seed/php-sdk/trace/src/Submission/Types/TestCaseNonHiddenGrade.php @@ -42,15 +42,18 @@ class TestCaseNonHiddenGrade extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->passed = $values['passed'];$this->actualResult = $values['actualResult'] ?? null;$this->exception = $values['exception'] ?? null;$this->stdout = $values['stdout']; + ) { + $this->passed = $values['passed']; + $this->actualResult = $values['actualResult'] ?? null; + $this->exception = $values['exception'] ?? null; + $this->stdout = $values['stdout']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/TestCaseResult.php b/seed/php-sdk/trace/src/Submission/Types/TestCaseResult.php index eb00a03f1fee..9dc1c9ddba97 100644 --- a/seed/php-sdk/trace/src/Submission/Types/TestCaseResult.php +++ b/seed/php-sdk/trace/src/Submission/Types/TestCaseResult.php @@ -35,15 +35,17 @@ class TestCaseResult extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->expectedResult = $values['expectedResult'];$this->actualResult = $values['actualResult'];$this->passed = $values['passed']; + ) { + $this->expectedResult = $values['expectedResult']; + $this->actualResult = $values['actualResult']; + $this->passed = $values['passed']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/TestCaseResultWithStdout.php b/seed/php-sdk/trace/src/Submission/Types/TestCaseResultWithStdout.php index 1726ddd3c308..48a302861bb8 100644 --- a/seed/php-sdk/trace/src/Submission/Types/TestCaseResultWithStdout.php +++ b/seed/php-sdk/trace/src/Submission/Types/TestCaseResultWithStdout.php @@ -27,15 +27,16 @@ class TestCaseResultWithStdout extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->result = $values['result'];$this->stdout = $values['stdout']; + ) { + $this->result = $values['result']; + $this->stdout = $values['stdout']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/TestSubmissionState.php b/seed/php-sdk/trace/src/Submission/Types/TestSubmissionState.php index 36c796923c1b..a6a570298685 100644 --- a/seed/php-sdk/trace/src/Submission/Types/TestSubmissionState.php +++ b/seed/php-sdk/trace/src/Submission/Types/TestSubmissionState.php @@ -43,15 +43,18 @@ class TestSubmissionState extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->problemId = $values['problemId'];$this->defaultTestCases = $values['defaultTestCases'];$this->customTestCases = $values['customTestCases'];$this->status = $values['status']; + ) { + $this->problemId = $values['problemId']; + $this->defaultTestCases = $values['defaultTestCases']; + $this->customTestCases = $values['customTestCases']; + $this->status = $values['status']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/TestSubmissionStatus.php b/seed/php-sdk/trace/src/Submission/Types/TestSubmissionStatus.php index 3baebb9dd5d7..4f7cccd8b0b0 100644 --- a/seed/php-sdk/trace/src/Submission/Types/TestSubmissionStatus.php +++ b/seed/php-sdk/trace/src/Submission/Types/TestSubmissionStatus.php @@ -50,15 +50,16 @@ class TestSubmissionStatus extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @return TestSubmissionStatus */ - public static function stopped(): TestSubmissionStatus { + public static function stopped(): TestSubmissionStatus + { return new TestSubmissionStatus([ 'type' => 'stopped', 'value' => null, @@ -69,7 +70,8 @@ public static function stopped(): TestSubmissionStatus { * @param ErrorInfo $errored * @return TestSubmissionStatus */ - public static function errored(ErrorInfo $errored): TestSubmissionStatus { + public static function errored(ErrorInfo $errored): TestSubmissionStatus + { return new TestSubmissionStatus([ 'type' => 'errored', 'value' => $errored, @@ -80,7 +82,8 @@ public static function errored(ErrorInfo $errored): TestSubmissionStatus { * @param value-of $running * @return TestSubmissionStatus */ - public static function running(string $running): TestSubmissionStatus { + public static function running(string $running): TestSubmissionStatus + { return new TestSubmissionStatus([ 'type' => 'running', 'value' => $running, @@ -91,7 +94,8 @@ public static function running(string $running): TestSubmissionStatus { * @param array $testCaseIdToState * @return TestSubmissionStatus */ - public static function testCaseIdToState(array $testCaseIdToState): TestSubmissionStatus { + public static function testCaseIdToState(array $testCaseIdToState): TestSubmissionStatus + { return new TestSubmissionStatus([ 'type' => 'testCaseIdToState', 'value' => $testCaseIdToState, @@ -101,88 +105,97 @@ public static function testCaseIdToState(array $testCaseIdToState): TestSubmissi /** * @return bool */ - public function isStopped(): bool { - return is_null($this->value)&& $this->type === 'stopped'; + public function isStopped(): bool + { + return is_null($this->value) && $this->type === 'stopped'; } /** * @return bool */ - public function isErrored(): bool { - return $this->value instanceof ErrorInfo&& $this->type === 'errored'; + public function isErrored(): bool + { + return $this->value instanceof ErrorInfo && $this->type === 'errored'; } /** * @return ErrorInfo */ - public function asErrored(): ErrorInfo { - if (!($this->value instanceof ErrorInfo&& $this->type === 'errored')){ + public function asErrored(): ErrorInfo + { + if (!($this->value instanceof ErrorInfo && $this->type === 'errored')) { throw new Exception( "Expected errored; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isRunning(): bool { - return $this->value instanceof RunningSubmissionState&& $this->type === 'running'; + public function isRunning(): bool + { + return $this->value instanceof RunningSubmissionState && $this->type === 'running'; } /** * @return value-of */ - public function asRunning(): string { - if (!($this->value instanceof RunningSubmissionState&& $this->type === 'running')){ + public function asRunning(): string + { + if (!($this->value instanceof RunningSubmissionState && $this->type === 'running')) { throw new Exception( "Expected running; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isTestCaseIdToState(): bool { - return is_array($this->value)&& $this->type === 'testCaseIdToState'; + public function isTestCaseIdToState(): bool + { + return is_array($this->value) && $this->type === 'testCaseIdToState'; } /** * @return array */ - public function asTestCaseIdToState(): array { - if (!(is_array($this->value)&& $this->type === 'testCaseIdToState')){ + public function asTestCaseIdToState(): array + { + if (!(is_array($this->value) && $this->type === 'testCaseIdToState')) { throw new Exception( "Expected testCaseIdToState; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'stopped': $result['stopped'] = []; break; @@ -200,26 +213,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -228,33 +242,34 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'stopped': $args['value'] = null; break; case 'errored': - if (!array_key_exists('errored', $data)){ + if (!array_key_exists('errored', $data)) { throw new Exception( "JSON data is missing property 'errored'", ); } - - if (!(is_array($data['errored']))){ + + if (!(is_array($data['errored']))) { throw new Exception( "Expected property 'errored' in JSON data to be array, instead received " . get_debug_type($data['errored']), ); @@ -262,21 +277,21 @@ public static function jsonDeserialize(array $data): static { $args['value'] = ErrorInfo::jsonDeserialize($data['errored']); break; case 'running': - if (!array_key_exists('running', $data)){ + if (!array_key_exists('running', $data)) { throw new Exception( "JSON data is missing property 'running'", ); } - + $args['value'] = $data['running']; break; case 'testCaseIdToState': - if (!array_key_exists('testCaseIdToState', $data)){ + if (!array_key_exists('testCaseIdToState', $data)) { throw new Exception( "JSON data is missing property 'testCaseIdToState'", ); } - + $args['value'] = $data['testCaseIdToState']; break; case '_unknown': @@ -284,7 +299,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/trace/src/Submission/Types/TestSubmissionStatusV2.php b/seed/php-sdk/trace/src/Submission/Types/TestSubmissionStatusV2.php index ab54241ab223..5d7572f185a8 100644 --- a/seed/php-sdk/trace/src/Submission/Types/TestSubmissionStatusV2.php +++ b/seed/php-sdk/trace/src/Submission/Types/TestSubmissionStatusV2.php @@ -43,15 +43,18 @@ class TestSubmissionStatusV2 extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->updates = $values['updates'];$this->problemId = $values['problemId'];$this->problemVersion = $values['problemVersion'];$this->problemInfo = $values['problemInfo']; + ) { + $this->updates = $values['updates']; + $this->problemId = $values['problemId']; + $this->problemVersion = $values['problemVersion']; + $this->problemInfo = $values['problemInfo']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/TestSubmissionUpdate.php b/seed/php-sdk/trace/src/Submission/Types/TestSubmissionUpdate.php index fa4e0345d503..9e78f0928a8b 100644 --- a/seed/php-sdk/trace/src/Submission/Types/TestSubmissionUpdate.php +++ b/seed/php-sdk/trace/src/Submission/Types/TestSubmissionUpdate.php @@ -29,15 +29,16 @@ class TestSubmissionUpdate extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->updateTime = $values['updateTime'];$this->updateInfo = $values['updateInfo']; + ) { + $this->updateTime = $values['updateTime']; + $this->updateInfo = $values['updateInfo']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/TestSubmissionUpdateInfo.php b/seed/php-sdk/trace/src/Submission/Types/TestSubmissionUpdateInfo.php index b85c12952ad7..1c2a1e8a6526 100644 --- a/seed/php-sdk/trace/src/Submission/Types/TestSubmissionUpdateInfo.php +++ b/seed/php-sdk/trace/src/Submission/Types/TestSubmissionUpdateInfo.php @@ -56,16 +56,17 @@ class TestSubmissionUpdateInfo extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param value-of $running * @return TestSubmissionUpdateInfo */ - public static function running(string $running): TestSubmissionUpdateInfo { + public static function running(string $running): TestSubmissionUpdateInfo + { return new TestSubmissionUpdateInfo([ 'type' => 'running', 'value' => $running, @@ -75,7 +76,8 @@ public static function running(string $running): TestSubmissionUpdateInfo { /** * @return TestSubmissionUpdateInfo */ - public static function stopped(): TestSubmissionUpdateInfo { + public static function stopped(): TestSubmissionUpdateInfo + { return new TestSubmissionUpdateInfo([ 'type' => 'stopped', 'value' => null, @@ -86,7 +88,8 @@ public static function stopped(): TestSubmissionUpdateInfo { * @param ErrorInfo $errored * @return TestSubmissionUpdateInfo */ - public static function errored(ErrorInfo $errored): TestSubmissionUpdateInfo { + public static function errored(ErrorInfo $errored): TestSubmissionUpdateInfo + { return new TestSubmissionUpdateInfo([ 'type' => 'errored', 'value' => $errored, @@ -97,7 +100,8 @@ public static function errored(ErrorInfo $errored): TestSubmissionUpdateInfo { * @param GradedTestCaseUpdate $gradedTestCase * @return TestSubmissionUpdateInfo */ - public static function gradedTestCase(GradedTestCaseUpdate $gradedTestCase): TestSubmissionUpdateInfo { + public static function gradedTestCase(GradedTestCaseUpdate $gradedTestCase): TestSubmissionUpdateInfo + { return new TestSubmissionUpdateInfo([ 'type' => 'gradedTestCase', 'value' => $gradedTestCase, @@ -108,7 +112,8 @@ public static function gradedTestCase(GradedTestCaseUpdate $gradedTestCase): Tes * @param RecordedTestCaseUpdate $recordedTestCase * @return TestSubmissionUpdateInfo */ - public static function recordedTestCase(RecordedTestCaseUpdate $recordedTestCase): TestSubmissionUpdateInfo { + public static function recordedTestCase(RecordedTestCaseUpdate $recordedTestCase): TestSubmissionUpdateInfo + { return new TestSubmissionUpdateInfo([ 'type' => 'recordedTestCase', 'value' => $recordedTestCase, @@ -118,7 +123,8 @@ public static function recordedTestCase(RecordedTestCaseUpdate $recordedTestCase /** * @return TestSubmissionUpdateInfo */ - public static function finished(): TestSubmissionUpdateInfo { + public static function finished(): TestSubmissionUpdateInfo + { return new TestSubmissionUpdateInfo([ 'type' => 'finished', 'value' => null, @@ -128,115 +134,127 @@ public static function finished(): TestSubmissionUpdateInfo { /** * @return bool */ - public function isRunning(): bool { - return $this->value instanceof RunningSubmissionState&& $this->type === 'running'; + public function isRunning(): bool + { + return $this->value instanceof RunningSubmissionState && $this->type === 'running'; } /** * @return value-of */ - public function asRunning(): string { - if (!($this->value instanceof RunningSubmissionState&& $this->type === 'running')){ + public function asRunning(): string + { + if (!($this->value instanceof RunningSubmissionState && $this->type === 'running')) { throw new Exception( "Expected running; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isStopped(): bool { - return is_null($this->value)&& $this->type === 'stopped'; + public function isStopped(): bool + { + return is_null($this->value) && $this->type === 'stopped'; } /** * @return bool */ - public function isErrored(): bool { - return $this->value instanceof ErrorInfo&& $this->type === 'errored'; + public function isErrored(): bool + { + return $this->value instanceof ErrorInfo && $this->type === 'errored'; } /** * @return ErrorInfo */ - public function asErrored(): ErrorInfo { - if (!($this->value instanceof ErrorInfo&& $this->type === 'errored')){ + public function asErrored(): ErrorInfo + { + if (!($this->value instanceof ErrorInfo && $this->type === 'errored')) { throw new Exception( "Expected errored; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isGradedTestCase(): bool { - return $this->value instanceof GradedTestCaseUpdate&& $this->type === 'gradedTestCase'; + public function isGradedTestCase(): bool + { + return $this->value instanceof GradedTestCaseUpdate && $this->type === 'gradedTestCase'; } /** * @return GradedTestCaseUpdate */ - public function asGradedTestCase(): GradedTestCaseUpdate { - if (!($this->value instanceof GradedTestCaseUpdate&& $this->type === 'gradedTestCase')){ + public function asGradedTestCase(): GradedTestCaseUpdate + { + if (!($this->value instanceof GradedTestCaseUpdate && $this->type === 'gradedTestCase')) { throw new Exception( "Expected gradedTestCase; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isRecordedTestCase(): bool { - return $this->value instanceof RecordedTestCaseUpdate&& $this->type === 'recordedTestCase'; + public function isRecordedTestCase(): bool + { + return $this->value instanceof RecordedTestCaseUpdate && $this->type === 'recordedTestCase'; } /** * @return RecordedTestCaseUpdate */ - public function asRecordedTestCase(): RecordedTestCaseUpdate { - if (!($this->value instanceof RecordedTestCaseUpdate&& $this->type === 'recordedTestCase')){ + public function asRecordedTestCase(): RecordedTestCaseUpdate + { + if (!($this->value instanceof RecordedTestCaseUpdate && $this->type === 'recordedTestCase')) { throw new Exception( "Expected recordedTestCase; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isFinished(): bool { - return is_null($this->value)&& $this->type === 'finished'; + public function isFinished(): bool + { + return is_null($this->value) && $this->type === 'finished'; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'running': $value = $this->value; $result['running'] = $value; @@ -261,26 +279,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -289,42 +308,43 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'running': - if (!array_key_exists('running', $data)){ + if (!array_key_exists('running', $data)) { throw new Exception( "JSON data is missing property 'running'", ); } - + $args['value'] = $data['running']; break; case 'stopped': $args['value'] = null; break; case 'errored': - if (!array_key_exists('errored', $data)){ + if (!array_key_exists('errored', $data)) { throw new Exception( "JSON data is missing property 'errored'", ); } - - if (!(is_array($data['errored']))){ + + if (!(is_array($data['errored']))) { throw new Exception( "Expected property 'errored' in JSON data to be array, instead received " . get_debug_type($data['errored']), ); @@ -345,7 +365,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/trace/src/Submission/Types/TraceResponse.php b/seed/php-sdk/trace/src/Submission/Types/TraceResponse.php index e6cb29dd0c71..1a0a4dde108b 100644 --- a/seed/php-sdk/trace/src/Submission/Types/TraceResponse.php +++ b/seed/php-sdk/trace/src/Submission/Types/TraceResponse.php @@ -56,15 +56,20 @@ class TraceResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->submissionId = $values['submissionId'];$this->lineNumber = $values['lineNumber'];$this->returnValue = $values['returnValue'] ?? null;$this->expressionLocation = $values['expressionLocation'] ?? null;$this->stack = $values['stack'];$this->stdout = $values['stdout'] ?? null; + ) { + $this->submissionId = $values['submissionId']; + $this->lineNumber = $values['lineNumber']; + $this->returnValue = $values['returnValue'] ?? null; + $this->expressionLocation = $values['expressionLocation'] ?? null; + $this->stack = $values['stack']; + $this->stdout = $values['stdout'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/TraceResponseV2.php b/seed/php-sdk/trace/src/Submission/Types/TraceResponseV2.php index fccccd65efcf..a5089320675f 100644 --- a/seed/php-sdk/trace/src/Submission/Types/TraceResponseV2.php +++ b/seed/php-sdk/trace/src/Submission/Types/TraceResponseV2.php @@ -63,15 +63,21 @@ class TraceResponseV2 extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->submissionId = $values['submissionId'];$this->lineNumber = $values['lineNumber'];$this->file = $values['file'];$this->returnValue = $values['returnValue'] ?? null;$this->expressionLocation = $values['expressionLocation'] ?? null;$this->stack = $values['stack'];$this->stdout = $values['stdout'] ?? null; + ) { + $this->submissionId = $values['submissionId']; + $this->lineNumber = $values['lineNumber']; + $this->file = $values['file']; + $this->returnValue = $values['returnValue'] ?? null; + $this->expressionLocation = $values['expressionLocation'] ?? null; + $this->stack = $values['stack']; + $this->stdout = $values['stdout'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/TraceResponsesPage.php b/seed/php-sdk/trace/src/Submission/Types/TraceResponsesPage.php index bb9b1f63d003..7369cb26e648 100644 --- a/seed/php-sdk/trace/src/Submission/Types/TraceResponsesPage.php +++ b/seed/php-sdk/trace/src/Submission/Types/TraceResponsesPage.php @@ -31,15 +31,16 @@ class TraceResponsesPage extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->offset = $values['offset'] ?? null;$this->traceResponses = $values['traceResponses']; + ) { + $this->offset = $values['offset'] ?? null; + $this->traceResponses = $values['traceResponses']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/TraceResponsesPageV2.php b/seed/php-sdk/trace/src/Submission/Types/TraceResponsesPageV2.php index 27307d1e041a..c89603fa28b3 100644 --- a/seed/php-sdk/trace/src/Submission/Types/TraceResponsesPageV2.php +++ b/seed/php-sdk/trace/src/Submission/Types/TraceResponsesPageV2.php @@ -31,15 +31,16 @@ class TraceResponsesPageV2 extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->offset = $values['offset'] ?? null;$this->traceResponses = $values['traceResponses']; + ) { + $this->offset = $values['offset'] ?? null; + $this->traceResponses = $values['traceResponses']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/TracedFile.php b/seed/php-sdk/trace/src/Submission/Types/TracedFile.php index 911fe6362524..eba1d59d96b0 100644 --- a/seed/php-sdk/trace/src/Submission/Types/TracedFile.php +++ b/seed/php-sdk/trace/src/Submission/Types/TracedFile.php @@ -27,15 +27,16 @@ class TracedFile extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->filename = $values['filename'];$this->directory = $values['directory']; + ) { + $this->filename = $values['filename']; + $this->directory = $values['directory']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/TracedTestCase.php b/seed/php-sdk/trace/src/Submission/Types/TracedTestCase.php index 78b740d3c099..e5d7a3e2ffaa 100644 --- a/seed/php-sdk/trace/src/Submission/Types/TracedTestCase.php +++ b/seed/php-sdk/trace/src/Submission/Types/TracedTestCase.php @@ -27,15 +27,16 @@ class TracedTestCase extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->result = $values['result'];$this->traceResponsesSize = $values['traceResponsesSize']; + ) { + $this->result = $values['result']; + $this->traceResponsesSize = $values['traceResponsesSize']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/UnexpectedLanguageError.php b/seed/php-sdk/trace/src/Submission/Types/UnexpectedLanguageError.php index ee0049f72897..1dac96b52a64 100644 --- a/seed/php-sdk/trace/src/Submission/Types/UnexpectedLanguageError.php +++ b/seed/php-sdk/trace/src/Submission/Types/UnexpectedLanguageError.php @@ -28,15 +28,16 @@ class UnexpectedLanguageError extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->expectedLanguage = $values['expectedLanguage'];$this->actualLanguage = $values['actualLanguage']; + ) { + $this->expectedLanguage = $values['expectedLanguage']; + $this->actualLanguage = $values['actualLanguage']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/WorkspaceFiles.php b/seed/php-sdk/trace/src/Submission/Types/WorkspaceFiles.php index 133b41553141..65ba3a517115 100644 --- a/seed/php-sdk/trace/src/Submission/Types/WorkspaceFiles.php +++ b/seed/php-sdk/trace/src/Submission/Types/WorkspaceFiles.php @@ -29,15 +29,16 @@ class WorkspaceFiles extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->mainFile = $values['mainFile'];$this->readOnlyFiles = $values['readOnlyFiles']; + ) { + $this->mainFile = $values['mainFile']; + $this->readOnlyFiles = $values['readOnlyFiles']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/WorkspaceRanResponse.php b/seed/php-sdk/trace/src/Submission/Types/WorkspaceRanResponse.php index 867fa7a14bef..eb11a207c808 100644 --- a/seed/php-sdk/trace/src/Submission/Types/WorkspaceRanResponse.php +++ b/seed/php-sdk/trace/src/Submission/Types/WorkspaceRanResponse.php @@ -27,15 +27,16 @@ class WorkspaceRanResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->submissionId = $values['submissionId'];$this->runDetails = $values['runDetails']; + ) { + $this->submissionId = $values['submissionId']; + $this->runDetails = $values['runDetails']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/WorkspaceRunDetails.php b/seed/php-sdk/trace/src/Submission/Types/WorkspaceRunDetails.php index 0cbf99dbb95a..40797b2d3979 100644 --- a/seed/php-sdk/trace/src/Submission/Types/WorkspaceRunDetails.php +++ b/seed/php-sdk/trace/src/Submission/Types/WorkspaceRunDetails.php @@ -34,15 +34,17 @@ class WorkspaceRunDetails extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->exceptionV2 = $values['exceptionV2'] ?? null;$this->exception = $values['exception'] ?? null;$this->stdout = $values['stdout']; + ) { + $this->exceptionV2 = $values['exceptionV2'] ?? null; + $this->exception = $values['exception'] ?? null; + $this->stdout = $values['stdout']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/WorkspaceStarterFilesResponse.php b/seed/php-sdk/trace/src/Submission/Types/WorkspaceStarterFilesResponse.php index 85551889bbeb..12374741d0e6 100644 --- a/seed/php-sdk/trace/src/Submission/Types/WorkspaceStarterFilesResponse.php +++ b/seed/php-sdk/trace/src/Submission/Types/WorkspaceStarterFilesResponse.php @@ -22,15 +22,15 @@ class WorkspaceStarterFilesResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->files = $values['files']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/WorkspaceStarterFilesResponseV2.php b/seed/php-sdk/trace/src/Submission/Types/WorkspaceStarterFilesResponseV2.php index 70304dddeec2..d7f683ff2a8d 100644 --- a/seed/php-sdk/trace/src/Submission/Types/WorkspaceStarterFilesResponseV2.php +++ b/seed/php-sdk/trace/src/Submission/Types/WorkspaceStarterFilesResponseV2.php @@ -23,15 +23,15 @@ class WorkspaceStarterFilesResponseV2 extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->filesByLanguage = $values['filesByLanguage']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/WorkspaceSubmissionState.php b/seed/php-sdk/trace/src/Submission/Types/WorkspaceSubmissionState.php index dca3cc4a682e..3ae73f2abe84 100644 --- a/seed/php-sdk/trace/src/Submission/Types/WorkspaceSubmissionState.php +++ b/seed/php-sdk/trace/src/Submission/Types/WorkspaceSubmissionState.php @@ -20,15 +20,15 @@ class WorkspaceSubmissionState extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->status = $values['status']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/WorkspaceSubmissionStatus.php b/seed/php-sdk/trace/src/Submission/Types/WorkspaceSubmissionStatus.php index e4d1ae230b74..5a196cc3a1a3 100644 --- a/seed/php-sdk/trace/src/Submission/Types/WorkspaceSubmissionStatus.php +++ b/seed/php-sdk/trace/src/Submission/Types/WorkspaceSubmissionStatus.php @@ -52,15 +52,16 @@ class WorkspaceSubmissionStatus extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @return WorkspaceSubmissionStatus */ - public static function stopped(): WorkspaceSubmissionStatus { + public static function stopped(): WorkspaceSubmissionStatus + { return new WorkspaceSubmissionStatus([ 'type' => 'stopped', 'value' => null, @@ -71,7 +72,8 @@ public static function stopped(): WorkspaceSubmissionStatus { * @param ErrorInfo $errored * @return WorkspaceSubmissionStatus */ - public static function errored(ErrorInfo $errored): WorkspaceSubmissionStatus { + public static function errored(ErrorInfo $errored): WorkspaceSubmissionStatus + { return new WorkspaceSubmissionStatus([ 'type' => 'errored', 'value' => $errored, @@ -82,7 +84,8 @@ public static function errored(ErrorInfo $errored): WorkspaceSubmissionStatus { * @param value-of $running * @return WorkspaceSubmissionStatus */ - public static function running(string $running): WorkspaceSubmissionStatus { + public static function running(string $running): WorkspaceSubmissionStatus + { return new WorkspaceSubmissionStatus([ 'type' => 'running', 'value' => $running, @@ -93,7 +96,8 @@ public static function running(string $running): WorkspaceSubmissionStatus { * @param WorkspaceRunDetails $ran * @return WorkspaceSubmissionStatus */ - public static function ran(WorkspaceRunDetails $ran): WorkspaceSubmissionStatus { + public static function ran(WorkspaceRunDetails $ran): WorkspaceSubmissionStatus + { return new WorkspaceSubmissionStatus([ 'type' => 'ran', 'value' => $ran, @@ -104,7 +108,8 @@ public static function ran(WorkspaceRunDetails $ran): WorkspaceSubmissionStatus * @param WorkspaceRunDetails $traced * @return WorkspaceSubmissionStatus */ - public static function traced(WorkspaceRunDetails $traced): WorkspaceSubmissionStatus { + public static function traced(WorkspaceRunDetails $traced): WorkspaceSubmissionStatus + { return new WorkspaceSubmissionStatus([ 'type' => 'traced', 'value' => $traced, @@ -114,108 +119,119 @@ public static function traced(WorkspaceRunDetails $traced): WorkspaceSubmissionS /** * @return bool */ - public function isStopped(): bool { - return is_null($this->value)&& $this->type === 'stopped'; + public function isStopped(): bool + { + return is_null($this->value) && $this->type === 'stopped'; } /** * @return bool */ - public function isErrored(): bool { - return $this->value instanceof ErrorInfo&& $this->type === 'errored'; + public function isErrored(): bool + { + return $this->value instanceof ErrorInfo && $this->type === 'errored'; } /** * @return ErrorInfo */ - public function asErrored(): ErrorInfo { - if (!($this->value instanceof ErrorInfo&& $this->type === 'errored')){ + public function asErrored(): ErrorInfo + { + if (!($this->value instanceof ErrorInfo && $this->type === 'errored')) { throw new Exception( "Expected errored; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isRunning(): bool { - return $this->value instanceof RunningSubmissionState&& $this->type === 'running'; + public function isRunning(): bool + { + return $this->value instanceof RunningSubmissionState && $this->type === 'running'; } /** * @return value-of */ - public function asRunning(): string { - if (!($this->value instanceof RunningSubmissionState&& $this->type === 'running')){ + public function asRunning(): string + { + if (!($this->value instanceof RunningSubmissionState && $this->type === 'running')) { throw new Exception( "Expected running; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isRan(): bool { - return $this->value instanceof WorkspaceRunDetails&& $this->type === 'ran'; + public function isRan(): bool + { + return $this->value instanceof WorkspaceRunDetails && $this->type === 'ran'; } /** * @return WorkspaceRunDetails */ - public function asRan(): WorkspaceRunDetails { - if (!($this->value instanceof WorkspaceRunDetails&& $this->type === 'ran')){ + public function asRan(): WorkspaceRunDetails + { + if (!($this->value instanceof WorkspaceRunDetails && $this->type === 'ran')) { throw new Exception( "Expected ran; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isTraced(): bool { - return $this->value instanceof WorkspaceRunDetails&& $this->type === 'traced'; + public function isTraced(): bool + { + return $this->value instanceof WorkspaceRunDetails && $this->type === 'traced'; } /** * @return WorkspaceRunDetails */ - public function asTraced(): WorkspaceRunDetails { - if (!($this->value instanceof WorkspaceRunDetails&& $this->type === 'traced')){ + public function asTraced(): WorkspaceRunDetails + { + if (!($this->value instanceof WorkspaceRunDetails && $this->type === 'traced')) { throw new Exception( "Expected traced; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'stopped': $result['stopped'] = []; break; @@ -237,26 +253,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -265,33 +282,34 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'stopped': $args['value'] = null; break; case 'errored': - if (!array_key_exists('errored', $data)){ + if (!array_key_exists('errored', $data)) { throw new Exception( "JSON data is missing property 'errored'", ); } - - if (!(is_array($data['errored']))){ + + if (!(is_array($data['errored']))) { throw new Exception( "Expected property 'errored' in JSON data to be array, instead received " . get_debug_type($data['errored']), ); @@ -299,12 +317,12 @@ public static function jsonDeserialize(array $data): static { $args['value'] = ErrorInfo::jsonDeserialize($data['errored']); break; case 'running': - if (!array_key_exists('running', $data)){ + if (!array_key_exists('running', $data)) { throw new Exception( "JSON data is missing property 'running'", ); } - + $args['value'] = $data['running']; break; case 'ran': @@ -318,7 +336,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/trace/src/Submission/Types/WorkspaceSubmissionStatusV2.php b/seed/php-sdk/trace/src/Submission/Types/WorkspaceSubmissionStatusV2.php index 5c0df16ee199..0c5d4997114a 100644 --- a/seed/php-sdk/trace/src/Submission/Types/WorkspaceSubmissionStatusV2.php +++ b/seed/php-sdk/trace/src/Submission/Types/WorkspaceSubmissionStatusV2.php @@ -21,15 +21,15 @@ class WorkspaceSubmissionStatusV2 extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->updates = $values['updates']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/WorkspaceSubmissionUpdate.php b/seed/php-sdk/trace/src/Submission/Types/WorkspaceSubmissionUpdate.php index 8a01cf7a594e..8239962be097 100644 --- a/seed/php-sdk/trace/src/Submission/Types/WorkspaceSubmissionUpdate.php +++ b/seed/php-sdk/trace/src/Submission/Types/WorkspaceSubmissionUpdate.php @@ -29,15 +29,16 @@ class WorkspaceSubmissionUpdate extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->updateTime = $values['updateTime'];$this->updateInfo = $values['updateInfo']; + ) { + $this->updateTime = $values['updateTime']; + $this->updateInfo = $values['updateInfo']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/WorkspaceSubmissionUpdateInfo.php b/seed/php-sdk/trace/src/Submission/Types/WorkspaceSubmissionUpdateInfo.php index 97811e33734b..6b6ca098155f 100644 --- a/seed/php-sdk/trace/src/Submission/Types/WorkspaceSubmissionUpdateInfo.php +++ b/seed/php-sdk/trace/src/Submission/Types/WorkspaceSubmissionUpdateInfo.php @@ -58,16 +58,17 @@ class WorkspaceSubmissionUpdateInfo extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param value-of $running * @return WorkspaceSubmissionUpdateInfo */ - public static function running(string $running): WorkspaceSubmissionUpdateInfo { + public static function running(string $running): WorkspaceSubmissionUpdateInfo + { return new WorkspaceSubmissionUpdateInfo([ 'type' => 'running', 'value' => $running, @@ -78,7 +79,8 @@ public static function running(string $running): WorkspaceSubmissionUpdateInfo { * @param WorkspaceRunDetails $ran * @return WorkspaceSubmissionUpdateInfo */ - public static function ran(WorkspaceRunDetails $ran): WorkspaceSubmissionUpdateInfo { + public static function ran(WorkspaceRunDetails $ran): WorkspaceSubmissionUpdateInfo + { return new WorkspaceSubmissionUpdateInfo([ 'type' => 'ran', 'value' => $ran, @@ -88,7 +90,8 @@ public static function ran(WorkspaceRunDetails $ran): WorkspaceSubmissionUpdateI /** * @return WorkspaceSubmissionUpdateInfo */ - public static function stopped(): WorkspaceSubmissionUpdateInfo { + public static function stopped(): WorkspaceSubmissionUpdateInfo + { return new WorkspaceSubmissionUpdateInfo([ 'type' => 'stopped', 'value' => null, @@ -98,7 +101,8 @@ public static function stopped(): WorkspaceSubmissionUpdateInfo { /** * @return WorkspaceSubmissionUpdateInfo */ - public static function traced(): WorkspaceSubmissionUpdateInfo { + public static function traced(): WorkspaceSubmissionUpdateInfo + { return new WorkspaceSubmissionUpdateInfo([ 'type' => 'traced', 'value' => null, @@ -109,7 +113,8 @@ public static function traced(): WorkspaceSubmissionUpdateInfo { * @param WorkspaceTracedUpdate $tracedV2 * @return WorkspaceSubmissionUpdateInfo */ - public static function tracedV2(WorkspaceTracedUpdate $tracedV2): WorkspaceSubmissionUpdateInfo { + public static function tracedV2(WorkspaceTracedUpdate $tracedV2): WorkspaceSubmissionUpdateInfo + { return new WorkspaceSubmissionUpdateInfo([ 'type' => 'tracedV2', 'value' => $tracedV2, @@ -120,7 +125,8 @@ public static function tracedV2(WorkspaceTracedUpdate $tracedV2): WorkspaceSubmi * @param ErrorInfo $errored * @return WorkspaceSubmissionUpdateInfo */ - public static function errored(ErrorInfo $errored): WorkspaceSubmissionUpdateInfo { + public static function errored(ErrorInfo $errored): WorkspaceSubmissionUpdateInfo + { return new WorkspaceSubmissionUpdateInfo([ 'type' => 'errored', 'value' => $errored, @@ -130,7 +136,8 @@ public static function errored(ErrorInfo $errored): WorkspaceSubmissionUpdateInf /** * @return WorkspaceSubmissionUpdateInfo */ - public static function finished(): WorkspaceSubmissionUpdateInfo { + public static function finished(): WorkspaceSubmissionUpdateInfo + { return new WorkspaceSubmissionUpdateInfo([ 'type' => 'finished', 'value' => null, @@ -140,122 +147,135 @@ public static function finished(): WorkspaceSubmissionUpdateInfo { /** * @return bool */ - public function isRunning(): bool { - return $this->value instanceof RunningSubmissionState&& $this->type === 'running'; + public function isRunning(): bool + { + return $this->value instanceof RunningSubmissionState && $this->type === 'running'; } /** * @return value-of */ - public function asRunning(): string { - if (!($this->value instanceof RunningSubmissionState&& $this->type === 'running')){ + public function asRunning(): string + { + if (!($this->value instanceof RunningSubmissionState && $this->type === 'running')) { throw new Exception( "Expected running; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isRan(): bool { - return $this->value instanceof WorkspaceRunDetails&& $this->type === 'ran'; + public function isRan(): bool + { + return $this->value instanceof WorkspaceRunDetails && $this->type === 'ran'; } /** * @return WorkspaceRunDetails */ - public function asRan(): WorkspaceRunDetails { - if (!($this->value instanceof WorkspaceRunDetails&& $this->type === 'ran')){ + public function asRan(): WorkspaceRunDetails + { + if (!($this->value instanceof WorkspaceRunDetails && $this->type === 'ran')) { throw new Exception( "Expected ran; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isStopped(): bool { - return is_null($this->value)&& $this->type === 'stopped'; + public function isStopped(): bool + { + return is_null($this->value) && $this->type === 'stopped'; } /** * @return bool */ - public function isTraced(): bool { - return is_null($this->value)&& $this->type === 'traced'; + public function isTraced(): bool + { + return is_null($this->value) && $this->type === 'traced'; } /** * @return bool */ - public function isTracedV2(): bool { - return $this->value instanceof WorkspaceTracedUpdate&& $this->type === 'tracedV2'; + public function isTracedV2(): bool + { + return $this->value instanceof WorkspaceTracedUpdate && $this->type === 'tracedV2'; } /** * @return WorkspaceTracedUpdate */ - public function asTracedV2(): WorkspaceTracedUpdate { - if (!($this->value instanceof WorkspaceTracedUpdate&& $this->type === 'tracedV2')){ + public function asTracedV2(): WorkspaceTracedUpdate + { + if (!($this->value instanceof WorkspaceTracedUpdate && $this->type === 'tracedV2')) { throw new Exception( "Expected tracedV2; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isErrored(): bool { - return $this->value instanceof ErrorInfo&& $this->type === 'errored'; + public function isErrored(): bool + { + return $this->value instanceof ErrorInfo && $this->type === 'errored'; } /** * @return ErrorInfo */ - public function asErrored(): ErrorInfo { - if (!($this->value instanceof ErrorInfo&& $this->type === 'errored')){ + public function asErrored(): ErrorInfo + { + if (!($this->value instanceof ErrorInfo && $this->type === 'errored')) { throw new Exception( "Expected errored; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isFinished(): bool { - return is_null($this->value)&& $this->type === 'finished'; + public function isFinished(): bool + { + return is_null($this->value) && $this->type === 'finished'; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'running': $value = $this->value; $result['running'] = $value; @@ -283,26 +303,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -311,29 +332,30 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'running': - if (!array_key_exists('running', $data)){ + if (!array_key_exists('running', $data)) { throw new Exception( "JSON data is missing property 'running'", ); } - + $args['value'] = $data['running']; break; case 'ran': @@ -349,13 +371,13 @@ public static function jsonDeserialize(array $data): static { $args['value'] = WorkspaceTracedUpdate::jsonDeserialize($data); break; case 'errored': - if (!array_key_exists('errored', $data)){ + if (!array_key_exists('errored', $data)) { throw new Exception( "JSON data is missing property 'errored'", ); } - - if (!(is_array($data['errored']))){ + + if (!(is_array($data['errored']))) { throw new Exception( "Expected property 'errored' in JSON data to be array, instead received " . get_debug_type($data['errored']), ); @@ -370,7 +392,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/trace/src/Submission/Types/WorkspaceSubmitRequest.php b/seed/php-sdk/trace/src/Submission/Types/WorkspaceSubmitRequest.php index 45ac0970fd62..c2275d4aead7 100644 --- a/seed/php-sdk/trace/src/Submission/Types/WorkspaceSubmitRequest.php +++ b/seed/php-sdk/trace/src/Submission/Types/WorkspaceSubmitRequest.php @@ -43,15 +43,18 @@ class WorkspaceSubmitRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->submissionId = $values['submissionId'];$this->language = $values['language'];$this->submissionFiles = $values['submissionFiles'];$this->userId = $values['userId'] ?? null; + ) { + $this->submissionId = $values['submissionId']; + $this->language = $values['language']; + $this->submissionFiles = $values['submissionFiles']; + $this->userId = $values['userId'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Submission/Types/WorkspaceTracedUpdate.php b/seed/php-sdk/trace/src/Submission/Types/WorkspaceTracedUpdate.php index 367a61c0161b..96ca5fe33997 100644 --- a/seed/php-sdk/trace/src/Submission/Types/WorkspaceTracedUpdate.php +++ b/seed/php-sdk/trace/src/Submission/Types/WorkspaceTracedUpdate.php @@ -20,15 +20,15 @@ class WorkspaceTracedUpdate extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->traceResponsesSize = $values['traceResponsesSize']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/Sysprop/SyspropClient.php b/seed/php-sdk/trace/src/Sysprop/SyspropClient.php index da80e21140ad..36ae269fef67 100644 --- a/seed/php-sdk/trace/src/Sysprop/SyspropClient.php +++ b/seed/php-sdk/trace/src/Sysprop/SyspropClient.php @@ -15,7 +15,7 @@ use Seed\Core\Json\JsonDecoder; use JsonException; -class SyspropClient +class SyspropClient { /** * @var array{ @@ -43,11 +43,10 @@ class SyspropClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -66,7 +65,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function setNumWarmInstances(string $language, int $numWarmInstances, ?array $options = null): void { + public function setNumWarmInstances(string $language, int $numWarmInstances, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -78,12 +78,12 @@ public function setNumWarmInstances(string $language, int $numWarmInstances, ?ar $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -114,7 +114,8 @@ public function setNumWarmInstances(string $language, int $numWarmInstances, ?ar * @throws SeedException * @throws SeedApiException */ - public function getNumWarmInstances(?array $options = null): array { + public function getNumWarmInstances(?array $options = null): array + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -126,15 +127,15 @@ public function getNumWarmInstances(?array $options = null): array { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, ['string' => 'integer']); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/trace/src/Utils/File.php b/seed/php-sdk/trace/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/trace/src/Utils/File.php +++ b/seed/php-sdk/trace/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/trace/src/V2/Problem/ProblemClient.php b/seed/php-sdk/trace/src/V2/Problem/ProblemClient.php index 9fc129a0107e..f526c6adcf5f 100644 --- a/seed/php-sdk/trace/src/V2/Problem/ProblemClient.php +++ b/seed/php-sdk/trace/src/V2/Problem/ProblemClient.php @@ -16,7 +16,7 @@ use Psr\Http\Client\ClientExceptionInterface; use Seed\V2\Problem\Types\ProblemInfoV2; -class ProblemClient +class ProblemClient { /** * @var array{ @@ -44,11 +44,10 @@ class ProblemClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -68,7 +67,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getLightweightProblems(?array $options = null): array { + public function getLightweightProblems(?array $options = null): array + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -80,15 +80,15 @@ public function getLightweightProblems(?array $options = null): array { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, [LightweightProblemInfoV2::class]); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -121,7 +121,8 @@ public function getLightweightProblems(?array $options = null): array { * @throws SeedException * @throws SeedApiException */ - public function getProblems(?array $options = null): array { + public function getProblems(?array $options = null): array + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -133,15 +134,15 @@ public function getProblems(?array $options = null): array { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, [ProblemInfoV2::class]); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -175,7 +176,8 @@ public function getProblems(?array $options = null): array { * @throws SeedException * @throws SeedApiException */ - public function getLatestProblem(string $problemId, ?array $options = null): ProblemInfoV2 { + public function getLatestProblem(string $problemId, ?array $options = null): ProblemInfoV2 + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -187,15 +189,15 @@ public function getLatestProblem(string $problemId, ?array $options = null): Pro $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ProblemInfoV2::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -230,7 +232,8 @@ public function getLatestProblem(string $problemId, ?array $options = null): Pro * @throws SeedException * @throws SeedApiException */ - public function getProblemVersion(string $problemId, int $problemVersion, ?array $options = null): ProblemInfoV2 { + public function getProblemVersion(string $problemId, int $problemVersion, ?array $options = null): ProblemInfoV2 + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -242,15 +245,15 @@ public function getProblemVersion(string $problemId, int $problemVersion, ?array $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ProblemInfoV2::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/trace/src/V2/Problem/Types/AssertCorrectnessCheck.php b/seed/php-sdk/trace/src/V2/Problem/Types/AssertCorrectnessCheck.php index 4b830a633e43..c3f41b4d18c1 100644 --- a/seed/php-sdk/trace/src/V2/Problem/Types/AssertCorrectnessCheck.php +++ b/seed/php-sdk/trace/src/V2/Problem/Types/AssertCorrectnessCheck.php @@ -42,16 +42,17 @@ class AssertCorrectnessCheck extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param DeepEqualityCorrectnessCheck $deepEquality * @return AssertCorrectnessCheck */ - public static function deepEquality(DeepEqualityCorrectnessCheck $deepEquality): AssertCorrectnessCheck { + public static function deepEquality(DeepEqualityCorrectnessCheck $deepEquality): AssertCorrectnessCheck + { return new AssertCorrectnessCheck([ 'type' => 'deepEquality', 'value' => $deepEquality, @@ -62,7 +63,8 @@ public static function deepEquality(DeepEqualityCorrectnessCheck $deepEquality): * @param VoidFunctionDefinitionThatTakesActualResult $custom * @return AssertCorrectnessCheck */ - public static function custom(VoidFunctionDefinitionThatTakesActualResult $custom): AssertCorrectnessCheck { + public static function custom(VoidFunctionDefinitionThatTakesActualResult $custom): AssertCorrectnessCheck + { return new AssertCorrectnessCheck([ 'type' => 'custom', 'value' => $custom, @@ -72,61 +74,67 @@ public static function custom(VoidFunctionDefinitionThatTakesActualResult $custo /** * @return bool */ - public function isDeepEquality(): bool { - return $this->value instanceof DeepEqualityCorrectnessCheck&& $this->type === 'deepEquality'; + public function isDeepEquality(): bool + { + return $this->value instanceof DeepEqualityCorrectnessCheck && $this->type === 'deepEquality'; } /** * @return DeepEqualityCorrectnessCheck */ - public function asDeepEquality(): DeepEqualityCorrectnessCheck { - if (!($this->value instanceof DeepEqualityCorrectnessCheck&& $this->type === 'deepEquality')){ + public function asDeepEquality(): DeepEqualityCorrectnessCheck + { + if (!($this->value instanceof DeepEqualityCorrectnessCheck && $this->type === 'deepEquality')) { throw new Exception( "Expected deepEquality; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isCustom(): bool { - return $this->value instanceof VoidFunctionDefinitionThatTakesActualResult&& $this->type === 'custom'; + public function isCustom(): bool + { + return $this->value instanceof VoidFunctionDefinitionThatTakesActualResult && $this->type === 'custom'; } /** * @return VoidFunctionDefinitionThatTakesActualResult */ - public function asCustom(): VoidFunctionDefinitionThatTakesActualResult { - if (!($this->value instanceof VoidFunctionDefinitionThatTakesActualResult&& $this->type === 'custom')){ + public function asCustom(): VoidFunctionDefinitionThatTakesActualResult + { + if (!($this->value instanceof VoidFunctionDefinitionThatTakesActualResult && $this->type === 'custom')) { throw new Exception( "Expected custom; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'deepEquality': $value = $this->asDeepEquality()->jsonSerialize(); $result = array_merge($value, $result); @@ -137,26 +145,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -165,22 +174,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'deepEquality': $args['value'] = DeepEqualityCorrectnessCheck::jsonDeserialize($data); break; @@ -192,7 +202,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/trace/src/V2/Problem/Types/BasicCustomFiles.php b/seed/php-sdk/trace/src/V2/Problem/Types/BasicCustomFiles.php index 13804fab4129..788befee2c47 100644 --- a/seed/php-sdk/trace/src/V2/Problem/Types/BasicCustomFiles.php +++ b/seed/php-sdk/trace/src/V2/Problem/Types/BasicCustomFiles.php @@ -43,15 +43,18 @@ class BasicCustomFiles extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->methodName = $values['methodName'];$this->signature = $values['signature'];$this->additionalFiles = $values['additionalFiles'];$this->basicTestCaseTemplate = $values['basicTestCaseTemplate']; + ) { + $this->methodName = $values['methodName']; + $this->signature = $values['signature']; + $this->additionalFiles = $values['additionalFiles']; + $this->basicTestCaseTemplate = $values['basicTestCaseTemplate']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/Problem/Types/BasicTestCaseTemplate.php b/seed/php-sdk/trace/src/V2/Problem/Types/BasicTestCaseTemplate.php index 8abfb2eb61d8..c84308dd43d0 100644 --- a/seed/php-sdk/trace/src/V2/Problem/Types/BasicTestCaseTemplate.php +++ b/seed/php-sdk/trace/src/V2/Problem/Types/BasicTestCaseTemplate.php @@ -41,15 +41,18 @@ class BasicTestCaseTemplate extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->templateId = $values['templateId'];$this->name = $values['name'];$this->description = $values['description'];$this->expectedValueParameterId = $values['expectedValueParameterId']; + ) { + $this->templateId = $values['templateId']; + $this->name = $values['name']; + $this->description = $values['description']; + $this->expectedValueParameterId = $values['expectedValueParameterId']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/Problem/Types/CreateProblemRequestV2.php b/seed/php-sdk/trace/src/V2/Problem/Types/CreateProblemRequestV2.php index 752fa0bd0b89..1e13d4df9d9a 100644 --- a/seed/php-sdk/trace/src/V2/Problem/Types/CreateProblemRequestV2.php +++ b/seed/php-sdk/trace/src/V2/Problem/Types/CreateProblemRequestV2.php @@ -65,15 +65,21 @@ class CreateProblemRequestV2 extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->problemName = $values['problemName'];$this->problemDescription = $values['problemDescription'];$this->customFiles = $values['customFiles'];$this->customTestCaseTemplates = $values['customTestCaseTemplates'];$this->testcases = $values['testcases'];$this->supportedLanguages = $values['supportedLanguages'];$this->isPublic = $values['isPublic']; + ) { + $this->problemName = $values['problemName']; + $this->problemDescription = $values['problemDescription']; + $this->customFiles = $values['customFiles']; + $this->customTestCaseTemplates = $values['customTestCaseTemplates']; + $this->testcases = $values['testcases']; + $this->supportedLanguages = $values['supportedLanguages']; + $this->isPublic = $values['isPublic']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/Problem/Types/CustomFiles.php b/seed/php-sdk/trace/src/V2/Problem/Types/CustomFiles.php index 316adca8711c..c71e091facae 100644 --- a/seed/php-sdk/trace/src/V2/Problem/Types/CustomFiles.php +++ b/seed/php-sdk/trace/src/V2/Problem/Types/CustomFiles.php @@ -43,16 +43,17 @@ class CustomFiles extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param BasicCustomFiles $basic * @return CustomFiles */ - public static function basic(BasicCustomFiles $basic): CustomFiles { + public static function basic(BasicCustomFiles $basic): CustomFiles + { return new CustomFiles([ 'type' => 'basic', 'value' => $basic, @@ -63,7 +64,8 @@ public static function basic(BasicCustomFiles $basic): CustomFiles { * @param array, Files> $custom * @return CustomFiles */ - public static function custom(array $custom): CustomFiles { + public static function custom(array $custom): CustomFiles + { return new CustomFiles([ 'type' => 'custom', 'value' => $custom, @@ -73,61 +75,67 @@ public static function custom(array $custom): CustomFiles { /** * @return bool */ - public function isBasic(): bool { - return $this->value instanceof BasicCustomFiles&& $this->type === 'basic'; + public function isBasic(): bool + { + return $this->value instanceof BasicCustomFiles && $this->type === 'basic'; } /** * @return BasicCustomFiles */ - public function asBasic(): BasicCustomFiles { - if (!($this->value instanceof BasicCustomFiles&& $this->type === 'basic')){ + public function asBasic(): BasicCustomFiles + { + if (!($this->value instanceof BasicCustomFiles && $this->type === 'basic')) { throw new Exception( "Expected basic; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isCustom(): bool { - return is_array($this->value)&& $this->type === 'custom'; + public function isCustom(): bool + { + return is_array($this->value) && $this->type === 'custom'; } /** * @return array, Files> */ - public function asCustom(): array { - if (!(is_array($this->value)&& $this->type === 'custom')){ + public function asCustom(): array + { + if (!(is_array($this->value) && $this->type === 'custom')) { throw new Exception( "Expected custom; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'basic': $value = $this->asBasic()->jsonSerialize(); $result = array_merge($value, $result); @@ -138,26 +146,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -166,32 +175,33 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'basic': $args['value'] = BasicCustomFiles::jsonDeserialize($data); break; case 'custom': - if (!array_key_exists('custom', $data)){ + if (!array_key_exists('custom', $data)) { throw new Exception( "JSON data is missing property 'custom'", ); } - + $args['value'] = $data['custom']; break; case '_unknown': @@ -199,7 +209,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/trace/src/V2/Problem/Types/DeepEqualityCorrectnessCheck.php b/seed/php-sdk/trace/src/V2/Problem/Types/DeepEqualityCorrectnessCheck.php index c3f1b0314b4f..34f96db6e89c 100644 --- a/seed/php-sdk/trace/src/V2/Problem/Types/DeepEqualityCorrectnessCheck.php +++ b/seed/php-sdk/trace/src/V2/Problem/Types/DeepEqualityCorrectnessCheck.php @@ -20,15 +20,15 @@ class DeepEqualityCorrectnessCheck extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->expectedValueParameterId = $values['expectedValueParameterId']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/Problem/Types/DefaultProvidedFile.php b/seed/php-sdk/trace/src/V2/Problem/Types/DefaultProvidedFile.php index 0a315677dadb..bfa640b40408 100644 --- a/seed/php-sdk/trace/src/V2/Problem/Types/DefaultProvidedFile.php +++ b/seed/php-sdk/trace/src/V2/Problem/Types/DefaultProvidedFile.php @@ -29,15 +29,16 @@ class DefaultProvidedFile extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->file = $values['file'];$this->relatedTypes = $values['relatedTypes']; + ) { + $this->file = $values['file']; + $this->relatedTypes = $values['relatedTypes']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/Problem/Types/FileInfoV2.php b/seed/php-sdk/trace/src/V2/Problem/Types/FileInfoV2.php index 12fe86119341..1e5fac8e365f 100644 --- a/seed/php-sdk/trace/src/V2/Problem/Types/FileInfoV2.php +++ b/seed/php-sdk/trace/src/V2/Problem/Types/FileInfoV2.php @@ -41,15 +41,18 @@ class FileInfoV2 extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->filename = $values['filename'];$this->directory = $values['directory'];$this->contents = $values['contents'];$this->editable = $values['editable']; + ) { + $this->filename = $values['filename']; + $this->directory = $values['directory']; + $this->contents = $values['contents']; + $this->editable = $values['editable']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/Problem/Types/Files.php b/seed/php-sdk/trace/src/V2/Problem/Types/Files.php index 69e90779d539..808d6c5b1610 100644 --- a/seed/php-sdk/trace/src/V2/Problem/Types/Files.php +++ b/seed/php-sdk/trace/src/V2/Problem/Types/Files.php @@ -21,15 +21,15 @@ class Files extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->files = $values['files']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/Problem/Types/FunctionImplementation.php b/seed/php-sdk/trace/src/V2/Problem/Types/FunctionImplementation.php index e976dbd1b8df..90dd2267751a 100644 --- a/seed/php-sdk/trace/src/V2/Problem/Types/FunctionImplementation.php +++ b/seed/php-sdk/trace/src/V2/Problem/Types/FunctionImplementation.php @@ -27,15 +27,16 @@ class FunctionImplementation extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->impl = $values['impl'];$this->imports = $values['imports'] ?? null; + ) { + $this->impl = $values['impl']; + $this->imports = $values['imports'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/Problem/Types/FunctionImplementationForMultipleLanguages.php b/seed/php-sdk/trace/src/V2/Problem/Types/FunctionImplementationForMultipleLanguages.php index 316af55a1eb0..a139b7e4eba9 100644 --- a/seed/php-sdk/trace/src/V2/Problem/Types/FunctionImplementationForMultipleLanguages.php +++ b/seed/php-sdk/trace/src/V2/Problem/Types/FunctionImplementationForMultipleLanguages.php @@ -22,15 +22,15 @@ class FunctionImplementationForMultipleLanguages extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->codeByLanguage = $values['codeByLanguage']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/Problem/Types/FunctionSignature.php b/seed/php-sdk/trace/src/V2/Problem/Types/FunctionSignature.php index f267fae9b283..a760f30785a6 100644 --- a/seed/php-sdk/trace/src/V2/Problem/Types/FunctionSignature.php +++ b/seed/php-sdk/trace/src/V2/Problem/Types/FunctionSignature.php @@ -46,16 +46,17 @@ class FunctionSignature extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param VoidFunctionSignature $void * @return FunctionSignature */ - public static function void(VoidFunctionSignature $void): FunctionSignature { + public static function void(VoidFunctionSignature $void): FunctionSignature + { return new FunctionSignature([ 'type' => 'void', 'value' => $void, @@ -66,7 +67,8 @@ public static function void(VoidFunctionSignature $void): FunctionSignature { * @param NonVoidFunctionSignature $nonVoid * @return FunctionSignature */ - public static function nonVoid(NonVoidFunctionSignature $nonVoid): FunctionSignature { + public static function nonVoid(NonVoidFunctionSignature $nonVoid): FunctionSignature + { return new FunctionSignature([ 'type' => 'nonVoid', 'value' => $nonVoid, @@ -77,7 +79,8 @@ public static function nonVoid(NonVoidFunctionSignature $nonVoid): FunctionSigna * @param VoidFunctionSignatureThatTakesActualResult $voidThatTakesActualResult * @return FunctionSignature */ - public static function voidThatTakesActualResult(VoidFunctionSignatureThatTakesActualResult $voidThatTakesActualResult): FunctionSignature { + public static function voidThatTakesActualResult(VoidFunctionSignatureThatTakesActualResult $voidThatTakesActualResult): FunctionSignature + { return new FunctionSignature([ 'type' => 'voidThatTakesActualResult', 'value' => $voidThatTakesActualResult, @@ -87,81 +90,89 @@ public static function voidThatTakesActualResult(VoidFunctionSignatureThatTakesA /** * @return bool */ - public function isVoid(): bool { - return $this->value instanceof VoidFunctionSignature&& $this->type === 'void'; + public function isVoid(): bool + { + return $this->value instanceof VoidFunctionSignature && $this->type === 'void'; } /** * @return VoidFunctionSignature */ - public function asVoid(): VoidFunctionSignature { - if (!($this->value instanceof VoidFunctionSignature&& $this->type === 'void')){ + public function asVoid(): VoidFunctionSignature + { + if (!($this->value instanceof VoidFunctionSignature && $this->type === 'void')) { throw new Exception( "Expected void; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isNonVoid(): bool { - return $this->value instanceof NonVoidFunctionSignature&& $this->type === 'nonVoid'; + public function isNonVoid(): bool + { + return $this->value instanceof NonVoidFunctionSignature && $this->type === 'nonVoid'; } /** * @return NonVoidFunctionSignature */ - public function asNonVoid(): NonVoidFunctionSignature { - if (!($this->value instanceof NonVoidFunctionSignature&& $this->type === 'nonVoid')){ + public function asNonVoid(): NonVoidFunctionSignature + { + if (!($this->value instanceof NonVoidFunctionSignature && $this->type === 'nonVoid')) { throw new Exception( "Expected nonVoid; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isVoidThatTakesActualResult(): bool { - return $this->value instanceof VoidFunctionSignatureThatTakesActualResult&& $this->type === 'voidThatTakesActualResult'; + public function isVoidThatTakesActualResult(): bool + { + return $this->value instanceof VoidFunctionSignatureThatTakesActualResult && $this->type === 'voidThatTakesActualResult'; } /** * @return VoidFunctionSignatureThatTakesActualResult */ - public function asVoidThatTakesActualResult(): VoidFunctionSignatureThatTakesActualResult { - if (!($this->value instanceof VoidFunctionSignatureThatTakesActualResult&& $this->type === 'voidThatTakesActualResult')){ + public function asVoidThatTakesActualResult(): VoidFunctionSignatureThatTakesActualResult + { + if (!($this->value instanceof VoidFunctionSignatureThatTakesActualResult && $this->type === 'voidThatTakesActualResult')) { throw new Exception( "Expected voidThatTakesActualResult; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'void': $value = $this->asVoid()->jsonSerialize(); $result = array_merge($value, $result); @@ -176,26 +187,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -204,22 +216,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'void': $args['value'] = VoidFunctionSignature::jsonDeserialize($data); break; @@ -234,7 +247,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/trace/src/V2/Problem/Types/GeneratedFiles.php b/seed/php-sdk/trace/src/V2/Problem/Types/GeneratedFiles.php index 63cdc1612596..daaf93f0a135 100644 --- a/seed/php-sdk/trace/src/V2/Problem/Types/GeneratedFiles.php +++ b/seed/php-sdk/trace/src/V2/Problem/Types/GeneratedFiles.php @@ -36,15 +36,17 @@ class GeneratedFiles extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->generatedTestCaseFiles = $values['generatedTestCaseFiles'];$this->generatedTemplateFiles = $values['generatedTemplateFiles'];$this->other = $values['other']; + ) { + $this->generatedTestCaseFiles = $values['generatedTestCaseFiles']; + $this->generatedTemplateFiles = $values['generatedTemplateFiles']; + $this->other = $values['other']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/Problem/Types/GetBasicSolutionFileRequest.php b/seed/php-sdk/trace/src/V2/Problem/Types/GetBasicSolutionFileRequest.php index 2d7762872c4b..74933e2ed3b0 100644 --- a/seed/php-sdk/trace/src/V2/Problem/Types/GetBasicSolutionFileRequest.php +++ b/seed/php-sdk/trace/src/V2/Problem/Types/GetBasicSolutionFileRequest.php @@ -27,15 +27,16 @@ class GetBasicSolutionFileRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->methodName = $values['methodName'];$this->signature = $values['signature']; + ) { + $this->methodName = $values['methodName']; + $this->signature = $values['signature']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/Problem/Types/GetBasicSolutionFileResponse.php b/seed/php-sdk/trace/src/V2/Problem/Types/GetBasicSolutionFileResponse.php index 026bef302a22..e1eb1f0ccd6a 100644 --- a/seed/php-sdk/trace/src/V2/Problem/Types/GetBasicSolutionFileResponse.php +++ b/seed/php-sdk/trace/src/V2/Problem/Types/GetBasicSolutionFileResponse.php @@ -22,15 +22,15 @@ class GetBasicSolutionFileResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->solutionFileByLanguage = $values['solutionFileByLanguage']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/Problem/Types/GetFunctionSignatureRequest.php b/seed/php-sdk/trace/src/V2/Problem/Types/GetFunctionSignatureRequest.php index ce6499a21879..fc4f236527db 100644 --- a/seed/php-sdk/trace/src/V2/Problem/Types/GetFunctionSignatureRequest.php +++ b/seed/php-sdk/trace/src/V2/Problem/Types/GetFunctionSignatureRequest.php @@ -20,15 +20,15 @@ class GetFunctionSignatureRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->functionSignature = $values['functionSignature']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/Problem/Types/GetFunctionSignatureResponse.php b/seed/php-sdk/trace/src/V2/Problem/Types/GetFunctionSignatureResponse.php index e67f3be2ba11..f59239d385e9 100644 --- a/seed/php-sdk/trace/src/V2/Problem/Types/GetFunctionSignatureResponse.php +++ b/seed/php-sdk/trace/src/V2/Problem/Types/GetFunctionSignatureResponse.php @@ -22,15 +22,15 @@ class GetFunctionSignatureResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->functionByLanguage = $values['functionByLanguage']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/Problem/Types/GetGeneratedTestCaseFileRequest.php b/seed/php-sdk/trace/src/V2/Problem/Types/GetGeneratedTestCaseFileRequest.php index ccf1520bfb8d..551145ee1172 100644 --- a/seed/php-sdk/trace/src/V2/Problem/Types/GetGeneratedTestCaseFileRequest.php +++ b/seed/php-sdk/trace/src/V2/Problem/Types/GetGeneratedTestCaseFileRequest.php @@ -27,15 +27,16 @@ class GetGeneratedTestCaseFileRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->template = $values['template'] ?? null;$this->testCase = $values['testCase']; + ) { + $this->template = $values['template'] ?? null; + $this->testCase = $values['testCase']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/Problem/Types/GetGeneratedTestCaseTemplateFileRequest.php b/seed/php-sdk/trace/src/V2/Problem/Types/GetGeneratedTestCaseTemplateFileRequest.php index f1d271b646fe..5f171ccfaeb0 100644 --- a/seed/php-sdk/trace/src/V2/Problem/Types/GetGeneratedTestCaseTemplateFileRequest.php +++ b/seed/php-sdk/trace/src/V2/Problem/Types/GetGeneratedTestCaseTemplateFileRequest.php @@ -20,15 +20,15 @@ class GetGeneratedTestCaseTemplateFileRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->template = $values['template']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/Problem/Types/LightweightProblemInfoV2.php b/seed/php-sdk/trace/src/V2/Problem/Types/LightweightProblemInfoV2.php index fa9e5a65c26b..cbed3e0b7c0f 100644 --- a/seed/php-sdk/trace/src/V2/Problem/Types/LightweightProblemInfoV2.php +++ b/seed/php-sdk/trace/src/V2/Problem/Types/LightweightProblemInfoV2.php @@ -43,15 +43,18 @@ class LightweightProblemInfoV2 extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->problemId = $values['problemId'];$this->problemName = $values['problemName'];$this->problemVersion = $values['problemVersion'];$this->variableTypes = $values['variableTypes']; + ) { + $this->problemId = $values['problemId']; + $this->problemName = $values['problemName']; + $this->problemVersion = $values['problemVersion']; + $this->variableTypes = $values['variableTypes']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/Problem/Types/NonVoidFunctionDefinition.php b/seed/php-sdk/trace/src/V2/Problem/Types/NonVoidFunctionDefinition.php index 7cde66194e01..e02277732907 100644 --- a/seed/php-sdk/trace/src/V2/Problem/Types/NonVoidFunctionDefinition.php +++ b/seed/php-sdk/trace/src/V2/Problem/Types/NonVoidFunctionDefinition.php @@ -27,15 +27,16 @@ class NonVoidFunctionDefinition extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->signature = $values['signature'];$this->code = $values['code']; + ) { + $this->signature = $values['signature']; + $this->code = $values['code']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/Problem/Types/NonVoidFunctionSignature.php b/seed/php-sdk/trace/src/V2/Problem/Types/NonVoidFunctionSignature.php index 5bd2f484dc52..4f574915d052 100644 --- a/seed/php-sdk/trace/src/V2/Problem/Types/NonVoidFunctionSignature.php +++ b/seed/php-sdk/trace/src/V2/Problem/Types/NonVoidFunctionSignature.php @@ -29,15 +29,16 @@ class NonVoidFunctionSignature extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->parameters = $values['parameters'];$this->returnType = $values['returnType']; + ) { + $this->parameters = $values['parameters']; + $this->returnType = $values['returnType']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/Problem/Types/Parameter.php b/seed/php-sdk/trace/src/V2/Problem/Types/Parameter.php index 99518b2848ff..eb5dd175be5c 100644 --- a/seed/php-sdk/trace/src/V2/Problem/Types/Parameter.php +++ b/seed/php-sdk/trace/src/V2/Problem/Types/Parameter.php @@ -35,15 +35,17 @@ class Parameter extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->parameterId = $values['parameterId'];$this->name = $values['name'];$this->variableType = $values['variableType']; + ) { + $this->parameterId = $values['parameterId']; + $this->name = $values['name']; + $this->variableType = $values['variableType']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/Problem/Types/ProblemInfoV2.php b/seed/php-sdk/trace/src/V2/Problem/Types/ProblemInfoV2.php index 6abf461a5871..d25899484993 100644 --- a/seed/php-sdk/trace/src/V2/Problem/Types/ProblemInfoV2.php +++ b/seed/php-sdk/trace/src/V2/Problem/Types/ProblemInfoV2.php @@ -86,15 +86,24 @@ class ProblemInfoV2 extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->problemId = $values['problemId'];$this->problemDescription = $values['problemDescription'];$this->problemName = $values['problemName'];$this->problemVersion = $values['problemVersion'];$this->supportedLanguages = $values['supportedLanguages'];$this->customFiles = $values['customFiles'];$this->generatedFiles = $values['generatedFiles'];$this->customTestCaseTemplates = $values['customTestCaseTemplates'];$this->testcases = $values['testcases'];$this->isPublic = $values['isPublic']; + ) { + $this->problemId = $values['problemId']; + $this->problemDescription = $values['problemDescription']; + $this->problemName = $values['problemName']; + $this->problemVersion = $values['problemVersion']; + $this->supportedLanguages = $values['supportedLanguages']; + $this->customFiles = $values['customFiles']; + $this->generatedFiles = $values['generatedFiles']; + $this->customTestCaseTemplates = $values['customTestCaseTemplates']; + $this->testcases = $values['testcases']; + $this->isPublic = $values['isPublic']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/Problem/Types/TestCaseExpects.php b/seed/php-sdk/trace/src/V2/Problem/Types/TestCaseExpects.php index 6b52b8053e7f..944824ecef43 100644 --- a/seed/php-sdk/trace/src/V2/Problem/Types/TestCaseExpects.php +++ b/seed/php-sdk/trace/src/V2/Problem/Types/TestCaseExpects.php @@ -20,15 +20,15 @@ class TestCaseExpects extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->expectedStdout = $values['expectedStdout'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/Problem/Types/TestCaseFunction.php b/seed/php-sdk/trace/src/V2/Problem/Types/TestCaseFunction.php index 76380ae1a57b..b63fbf037962 100644 --- a/seed/php-sdk/trace/src/V2/Problem/Types/TestCaseFunction.php +++ b/seed/php-sdk/trace/src/V2/Problem/Types/TestCaseFunction.php @@ -42,16 +42,17 @@ class TestCaseFunction extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param TestCaseWithActualResultImplementation $withActualResult * @return TestCaseFunction */ - public static function withActualResult(TestCaseWithActualResultImplementation $withActualResult): TestCaseFunction { + public static function withActualResult(TestCaseWithActualResultImplementation $withActualResult): TestCaseFunction + { return new TestCaseFunction([ 'type' => 'withActualResult', 'value' => $withActualResult, @@ -62,7 +63,8 @@ public static function withActualResult(TestCaseWithActualResultImplementation $ * @param VoidFunctionDefinition $custom * @return TestCaseFunction */ - public static function custom(VoidFunctionDefinition $custom): TestCaseFunction { + public static function custom(VoidFunctionDefinition $custom): TestCaseFunction + { return new TestCaseFunction([ 'type' => 'custom', 'value' => $custom, @@ -72,61 +74,67 @@ public static function custom(VoidFunctionDefinition $custom): TestCaseFunction /** * @return bool */ - public function isWithActualResult(): bool { - return $this->value instanceof TestCaseWithActualResultImplementation&& $this->type === 'withActualResult'; + public function isWithActualResult(): bool + { + return $this->value instanceof TestCaseWithActualResultImplementation && $this->type === 'withActualResult'; } /** * @return TestCaseWithActualResultImplementation */ - public function asWithActualResult(): TestCaseWithActualResultImplementation { - if (!($this->value instanceof TestCaseWithActualResultImplementation&& $this->type === 'withActualResult')){ + public function asWithActualResult(): TestCaseWithActualResultImplementation + { + if (!($this->value instanceof TestCaseWithActualResultImplementation && $this->type === 'withActualResult')) { throw new Exception( "Expected withActualResult; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isCustom(): bool { - return $this->value instanceof VoidFunctionDefinition&& $this->type === 'custom'; + public function isCustom(): bool + { + return $this->value instanceof VoidFunctionDefinition && $this->type === 'custom'; } /** * @return VoidFunctionDefinition */ - public function asCustom(): VoidFunctionDefinition { - if (!($this->value instanceof VoidFunctionDefinition&& $this->type === 'custom')){ + public function asCustom(): VoidFunctionDefinition + { + if (!($this->value instanceof VoidFunctionDefinition && $this->type === 'custom')) { throw new Exception( "Expected custom; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'withActualResult': $value = $this->asWithActualResult()->jsonSerialize(); $result = array_merge($value, $result); @@ -137,26 +145,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -165,22 +174,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'withActualResult': $args['value'] = TestCaseWithActualResultImplementation::jsonDeserialize($data); break; @@ -192,7 +202,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/trace/src/V2/Problem/Types/TestCaseImplementation.php b/seed/php-sdk/trace/src/V2/Problem/Types/TestCaseImplementation.php index 9225c4d1c51a..dea7ad4600be 100644 --- a/seed/php-sdk/trace/src/V2/Problem/Types/TestCaseImplementation.php +++ b/seed/php-sdk/trace/src/V2/Problem/Types/TestCaseImplementation.php @@ -27,15 +27,16 @@ class TestCaseImplementation extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->description = $values['description'];$this->function = $values['function']; + ) { + $this->description = $values['description']; + $this->function = $values['function']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/Problem/Types/TestCaseImplementationDescription.php b/seed/php-sdk/trace/src/V2/Problem/Types/TestCaseImplementationDescription.php index e15d3f0c370c..a22ae8dc0def 100644 --- a/seed/php-sdk/trace/src/V2/Problem/Types/TestCaseImplementationDescription.php +++ b/seed/php-sdk/trace/src/V2/Problem/Types/TestCaseImplementationDescription.php @@ -21,15 +21,15 @@ class TestCaseImplementationDescription extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->boards = $values['boards']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/Problem/Types/TestCaseImplementationDescriptionBoard.php b/seed/php-sdk/trace/src/V2/Problem/Types/TestCaseImplementationDescriptionBoard.php index 0aebd8dff535..8cb21ac79d48 100644 --- a/seed/php-sdk/trace/src/V2/Problem/Types/TestCaseImplementationDescriptionBoard.php +++ b/seed/php-sdk/trace/src/V2/Problem/Types/TestCaseImplementationDescriptionBoard.php @@ -40,16 +40,17 @@ class TestCaseImplementationDescriptionBoard extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param string $html * @return TestCaseImplementationDescriptionBoard */ - public static function html(string $html): TestCaseImplementationDescriptionBoard { + public static function html(string $html): TestCaseImplementationDescriptionBoard + { return new TestCaseImplementationDescriptionBoard([ 'type' => 'html', 'value' => $html, @@ -60,7 +61,8 @@ public static function html(string $html): TestCaseImplementationDescriptionBoar * @param string $paramId * @return TestCaseImplementationDescriptionBoard */ - public static function paramId(string $paramId): TestCaseImplementationDescriptionBoard { + public static function paramId(string $paramId): TestCaseImplementationDescriptionBoard + { return new TestCaseImplementationDescriptionBoard([ 'type' => 'paramId', 'value' => $paramId, @@ -70,61 +72,67 @@ public static function paramId(string $paramId): TestCaseImplementationDescripti /** * @return bool */ - public function isHtml(): bool { - return is_string($this->value)&& $this->type === 'html'; + public function isHtml(): bool + { + return is_string($this->value) && $this->type === 'html'; } /** * @return string */ - public function asHtml(): string { - if (!(is_string($this->value)&& $this->type === 'html')){ + public function asHtml(): string + { + if (!(is_string($this->value) && $this->type === 'html')) { throw new Exception( "Expected html; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isParamId(): bool { - return is_string($this->value)&& $this->type === 'paramId'; + public function isParamId(): bool + { + return is_string($this->value) && $this->type === 'paramId'; } /** * @return string */ - public function asParamId(): string { - if (!(is_string($this->value)&& $this->type === 'paramId')){ + public function asParamId(): string + { + if (!(is_string($this->value) && $this->type === 'paramId')) { throw new Exception( "Expected paramId; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'html': $value = $this->value; $result['html'] = $value; @@ -135,26 +143,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -163,38 +172,39 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'html': - if (!array_key_exists('html', $data)){ + if (!array_key_exists('html', $data)) { throw new Exception( "JSON data is missing property 'html'", ); } - + $args['value'] = $data['html']; break; case 'paramId': - if (!array_key_exists('paramId', $data)){ + if (!array_key_exists('paramId', $data)) { throw new Exception( "JSON data is missing property 'paramId'", ); } - + $args['value'] = $data['paramId']; break; case '_unknown': @@ -202,7 +212,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/trace/src/V2/Problem/Types/TestCaseImplementationReference.php b/seed/php-sdk/trace/src/V2/Problem/Types/TestCaseImplementationReference.php index 33c34ca0cb26..526cc0b93538 100644 --- a/seed/php-sdk/trace/src/V2/Problem/Types/TestCaseImplementationReference.php +++ b/seed/php-sdk/trace/src/V2/Problem/Types/TestCaseImplementationReference.php @@ -42,16 +42,17 @@ class TestCaseImplementationReference extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param string $templateId * @return TestCaseImplementationReference */ - public static function templateId(string $templateId): TestCaseImplementationReference { + public static function templateId(string $templateId): TestCaseImplementationReference + { return new TestCaseImplementationReference([ 'type' => 'templateId', 'value' => $templateId, @@ -62,7 +63,8 @@ public static function templateId(string $templateId): TestCaseImplementationRef * @param TestCaseImplementation $implementation * @return TestCaseImplementationReference */ - public static function implementation(TestCaseImplementation $implementation): TestCaseImplementationReference { + public static function implementation(TestCaseImplementation $implementation): TestCaseImplementationReference + { return new TestCaseImplementationReference([ 'type' => 'implementation', 'value' => $implementation, @@ -72,61 +74,67 @@ public static function implementation(TestCaseImplementation $implementation): T /** * @return bool */ - public function isTemplateId(): bool { - return is_string($this->value)&& $this->type === 'templateId'; + public function isTemplateId(): bool + { + return is_string($this->value) && $this->type === 'templateId'; } /** * @return string */ - public function asTemplateId(): string { - if (!(is_string($this->value)&& $this->type === 'templateId')){ + public function asTemplateId(): string + { + if (!(is_string($this->value) && $this->type === 'templateId')) { throw new Exception( "Expected templateId; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isImplementation(): bool { - return $this->value instanceof TestCaseImplementation&& $this->type === 'implementation'; + public function isImplementation(): bool + { + return $this->value instanceof TestCaseImplementation && $this->type === 'implementation'; } /** * @return TestCaseImplementation */ - public function asImplementation(): TestCaseImplementation { - if (!($this->value instanceof TestCaseImplementation&& $this->type === 'implementation')){ + public function asImplementation(): TestCaseImplementation + { + if (!($this->value instanceof TestCaseImplementation && $this->type === 'implementation')) { throw new Exception( "Expected implementation; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'templateId': $value = $this->value; $result['templateId'] = $value; @@ -137,26 +145,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -165,29 +174,30 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'templateId': - if (!array_key_exists('templateId', $data)){ + if (!array_key_exists('templateId', $data)) { throw new Exception( "JSON data is missing property 'templateId'", ); } - + $args['value'] = $data['templateId']; break; case 'implementation': @@ -198,7 +208,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/trace/src/V2/Problem/Types/TestCaseMetadata.php b/seed/php-sdk/trace/src/V2/Problem/Types/TestCaseMetadata.php index 841da46f3f6a..a0b758b78c19 100644 --- a/seed/php-sdk/trace/src/V2/Problem/Types/TestCaseMetadata.php +++ b/seed/php-sdk/trace/src/V2/Problem/Types/TestCaseMetadata.php @@ -34,15 +34,17 @@ class TestCaseMetadata extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->name = $values['name'];$this->hidden = $values['hidden']; + ) { + $this->id = $values['id']; + $this->name = $values['name']; + $this->hidden = $values['hidden']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/Problem/Types/TestCaseTemplate.php b/seed/php-sdk/trace/src/V2/Problem/Types/TestCaseTemplate.php index ff7881f8371b..9fa4815a38e9 100644 --- a/seed/php-sdk/trace/src/V2/Problem/Types/TestCaseTemplate.php +++ b/seed/php-sdk/trace/src/V2/Problem/Types/TestCaseTemplate.php @@ -34,15 +34,17 @@ class TestCaseTemplate extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->templateId = $values['templateId'];$this->name = $values['name'];$this->implementation = $values['implementation']; + ) { + $this->templateId = $values['templateId']; + $this->name = $values['name']; + $this->implementation = $values['implementation']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/Problem/Types/TestCaseV2.php b/seed/php-sdk/trace/src/V2/Problem/Types/TestCaseV2.php index 5baaa4eae47f..45f0c1d6d354 100644 --- a/seed/php-sdk/trace/src/V2/Problem/Types/TestCaseV2.php +++ b/seed/php-sdk/trace/src/V2/Problem/Types/TestCaseV2.php @@ -43,15 +43,18 @@ class TestCaseV2 extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->metadata = $values['metadata'];$this->implementation = $values['implementation'];$this->arguments = $values['arguments'];$this->expects = $values['expects'] ?? null; + ) { + $this->metadata = $values['metadata']; + $this->implementation = $values['implementation']; + $this->arguments = $values['arguments']; + $this->expects = $values['expects'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/Problem/Types/TestCaseWithActualResultImplementation.php b/seed/php-sdk/trace/src/V2/Problem/Types/TestCaseWithActualResultImplementation.php index b093e9c645a2..2d2e86aab826 100644 --- a/seed/php-sdk/trace/src/V2/Problem/Types/TestCaseWithActualResultImplementation.php +++ b/seed/php-sdk/trace/src/V2/Problem/Types/TestCaseWithActualResultImplementation.php @@ -27,15 +27,16 @@ class TestCaseWithActualResultImplementation extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->getActualResult = $values['getActualResult'];$this->assertCorrectnessCheck = $values['assertCorrectnessCheck']; + ) { + $this->getActualResult = $values['getActualResult']; + $this->assertCorrectnessCheck = $values['assertCorrectnessCheck']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/Problem/Types/VoidFunctionDefinition.php b/seed/php-sdk/trace/src/V2/Problem/Types/VoidFunctionDefinition.php index cacd23f59007..da45997e0ae1 100644 --- a/seed/php-sdk/trace/src/V2/Problem/Types/VoidFunctionDefinition.php +++ b/seed/php-sdk/trace/src/V2/Problem/Types/VoidFunctionDefinition.php @@ -28,15 +28,16 @@ class VoidFunctionDefinition extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->parameters = $values['parameters'];$this->code = $values['code']; + ) { + $this->parameters = $values['parameters']; + $this->code = $values['code']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/Problem/Types/VoidFunctionDefinitionThatTakesActualResult.php b/seed/php-sdk/trace/src/V2/Problem/Types/VoidFunctionDefinitionThatTakesActualResult.php index 1907d554293c..7caffcab879e 100644 --- a/seed/php-sdk/trace/src/V2/Problem/Types/VoidFunctionDefinitionThatTakesActualResult.php +++ b/seed/php-sdk/trace/src/V2/Problem/Types/VoidFunctionDefinitionThatTakesActualResult.php @@ -31,15 +31,16 @@ class VoidFunctionDefinitionThatTakesActualResult extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->additionalParameters = $values['additionalParameters'];$this->code = $values['code']; + ) { + $this->additionalParameters = $values['additionalParameters']; + $this->code = $values['code']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/Problem/Types/VoidFunctionSignature.php b/seed/php-sdk/trace/src/V2/Problem/Types/VoidFunctionSignature.php index a10d019460c3..6c64c07e647e 100644 --- a/seed/php-sdk/trace/src/V2/Problem/Types/VoidFunctionSignature.php +++ b/seed/php-sdk/trace/src/V2/Problem/Types/VoidFunctionSignature.php @@ -21,15 +21,15 @@ class VoidFunctionSignature extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->parameters = $values['parameters']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/Problem/Types/VoidFunctionSignatureThatTakesActualResult.php b/seed/php-sdk/trace/src/V2/Problem/Types/VoidFunctionSignatureThatTakesActualResult.php index 7c65388adce3..16ae96941163 100644 --- a/seed/php-sdk/trace/src/V2/Problem/Types/VoidFunctionSignatureThatTakesActualResult.php +++ b/seed/php-sdk/trace/src/V2/Problem/Types/VoidFunctionSignatureThatTakesActualResult.php @@ -29,15 +29,16 @@ class VoidFunctionSignatureThatTakesActualResult extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->parameters = $values['parameters'];$this->actualResultType = $values['actualResultType']; + ) { + $this->parameters = $values['parameters']; + $this->actualResultType = $values['actualResultType']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/V2Client.php b/seed/php-sdk/trace/src/V2/V2Client.php index e567cc38388c..56be0e5f0f4e 100644 --- a/seed/php-sdk/trace/src/V2/V2Client.php +++ b/seed/php-sdk/trace/src/V2/V2Client.php @@ -14,7 +14,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class V2Client +class V2Client { /** * @var ProblemClient $problem @@ -52,11 +52,10 @@ class V2Client * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; $this->problem = new ProblemClient($this->client, $this->options); @@ -75,7 +74,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function test(?array $options = null): void { + public function test(?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -87,12 +87,12 @@ public function test(?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/trace/src/V2/V3/Problem/ProblemClient.php b/seed/php-sdk/trace/src/V2/V3/Problem/ProblemClient.php index 9075a452476b..97ac078662cd 100644 --- a/seed/php-sdk/trace/src/V2/V3/Problem/ProblemClient.php +++ b/seed/php-sdk/trace/src/V2/V3/Problem/ProblemClient.php @@ -16,7 +16,7 @@ use Psr\Http\Client\ClientExceptionInterface; use Seed\V2\V3\Problem\Types\ProblemInfoV2; -class ProblemClient +class ProblemClient { /** * @var array{ @@ -44,11 +44,10 @@ class ProblemClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -68,7 +67,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getLightweightProblems(?array $options = null): array { + public function getLightweightProblems(?array $options = null): array + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -80,15 +80,15 @@ public function getLightweightProblems(?array $options = null): array { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, [LightweightProblemInfoV2::class]); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -121,7 +121,8 @@ public function getLightweightProblems(?array $options = null): array { * @throws SeedException * @throws SeedApiException */ - public function getProblems(?array $options = null): array { + public function getProblems(?array $options = null): array + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -133,15 +134,15 @@ public function getProblems(?array $options = null): array { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, [ProblemInfoV2::class]); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -175,7 +176,8 @@ public function getProblems(?array $options = null): array { * @throws SeedException * @throws SeedApiException */ - public function getLatestProblem(string $problemId, ?array $options = null): ProblemInfoV2 { + public function getLatestProblem(string $problemId, ?array $options = null): ProblemInfoV2 + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -187,15 +189,15 @@ public function getLatestProblem(string $problemId, ?array $options = null): Pro $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ProblemInfoV2::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -230,7 +232,8 @@ public function getLatestProblem(string $problemId, ?array $options = null): Pro * @throws SeedException * @throws SeedApiException */ - public function getProblemVersion(string $problemId, int $problemVersion, ?array $options = null): ProblemInfoV2 { + public function getProblemVersion(string $problemId, int $problemVersion, ?array $options = null): ProblemInfoV2 + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -242,15 +245,15 @@ public function getProblemVersion(string $problemId, int $problemVersion, ?array $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return ProblemInfoV2::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/trace/src/V2/V3/Problem/Types/AssertCorrectnessCheck.php b/seed/php-sdk/trace/src/V2/V3/Problem/Types/AssertCorrectnessCheck.php index 3cadbf618691..51c211354856 100644 --- a/seed/php-sdk/trace/src/V2/V3/Problem/Types/AssertCorrectnessCheck.php +++ b/seed/php-sdk/trace/src/V2/V3/Problem/Types/AssertCorrectnessCheck.php @@ -42,16 +42,17 @@ class AssertCorrectnessCheck extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param DeepEqualityCorrectnessCheck $deepEquality * @return AssertCorrectnessCheck */ - public static function deepEquality(DeepEqualityCorrectnessCheck $deepEquality): AssertCorrectnessCheck { + public static function deepEquality(DeepEqualityCorrectnessCheck $deepEquality): AssertCorrectnessCheck + { return new AssertCorrectnessCheck([ 'type' => 'deepEquality', 'value' => $deepEquality, @@ -62,7 +63,8 @@ public static function deepEquality(DeepEqualityCorrectnessCheck $deepEquality): * @param VoidFunctionDefinitionThatTakesActualResult $custom * @return AssertCorrectnessCheck */ - public static function custom(VoidFunctionDefinitionThatTakesActualResult $custom): AssertCorrectnessCheck { + public static function custom(VoidFunctionDefinitionThatTakesActualResult $custom): AssertCorrectnessCheck + { return new AssertCorrectnessCheck([ 'type' => 'custom', 'value' => $custom, @@ -72,61 +74,67 @@ public static function custom(VoidFunctionDefinitionThatTakesActualResult $custo /** * @return bool */ - public function isDeepEquality(): bool { - return $this->value instanceof DeepEqualityCorrectnessCheck&& $this->type === 'deepEquality'; + public function isDeepEquality(): bool + { + return $this->value instanceof DeepEqualityCorrectnessCheck && $this->type === 'deepEquality'; } /** * @return DeepEqualityCorrectnessCheck */ - public function asDeepEquality(): DeepEqualityCorrectnessCheck { - if (!($this->value instanceof DeepEqualityCorrectnessCheck&& $this->type === 'deepEquality')){ + public function asDeepEquality(): DeepEqualityCorrectnessCheck + { + if (!($this->value instanceof DeepEqualityCorrectnessCheck && $this->type === 'deepEquality')) { throw new Exception( "Expected deepEquality; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isCustom(): bool { - return $this->value instanceof VoidFunctionDefinitionThatTakesActualResult&& $this->type === 'custom'; + public function isCustom(): bool + { + return $this->value instanceof VoidFunctionDefinitionThatTakesActualResult && $this->type === 'custom'; } /** * @return VoidFunctionDefinitionThatTakesActualResult */ - public function asCustom(): VoidFunctionDefinitionThatTakesActualResult { - if (!($this->value instanceof VoidFunctionDefinitionThatTakesActualResult&& $this->type === 'custom')){ + public function asCustom(): VoidFunctionDefinitionThatTakesActualResult + { + if (!($this->value instanceof VoidFunctionDefinitionThatTakesActualResult && $this->type === 'custom')) { throw new Exception( "Expected custom; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'deepEquality': $value = $this->asDeepEquality()->jsonSerialize(); $result = array_merge($value, $result); @@ -137,26 +145,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -165,22 +174,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'deepEquality': $args['value'] = DeepEqualityCorrectnessCheck::jsonDeserialize($data); break; @@ -192,7 +202,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/trace/src/V2/V3/Problem/Types/BasicCustomFiles.php b/seed/php-sdk/trace/src/V2/V3/Problem/Types/BasicCustomFiles.php index 27b831a8a938..a4230efd39d9 100644 --- a/seed/php-sdk/trace/src/V2/V3/Problem/Types/BasicCustomFiles.php +++ b/seed/php-sdk/trace/src/V2/V3/Problem/Types/BasicCustomFiles.php @@ -43,15 +43,18 @@ class BasicCustomFiles extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->methodName = $values['methodName'];$this->signature = $values['signature'];$this->additionalFiles = $values['additionalFiles'];$this->basicTestCaseTemplate = $values['basicTestCaseTemplate']; + ) { + $this->methodName = $values['methodName']; + $this->signature = $values['signature']; + $this->additionalFiles = $values['additionalFiles']; + $this->basicTestCaseTemplate = $values['basicTestCaseTemplate']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/V3/Problem/Types/BasicTestCaseTemplate.php b/seed/php-sdk/trace/src/V2/V3/Problem/Types/BasicTestCaseTemplate.php index 3dfc50492c67..6058844fc7ab 100644 --- a/seed/php-sdk/trace/src/V2/V3/Problem/Types/BasicTestCaseTemplate.php +++ b/seed/php-sdk/trace/src/V2/V3/Problem/Types/BasicTestCaseTemplate.php @@ -41,15 +41,18 @@ class BasicTestCaseTemplate extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->templateId = $values['templateId'];$this->name = $values['name'];$this->description = $values['description'];$this->expectedValueParameterId = $values['expectedValueParameterId']; + ) { + $this->templateId = $values['templateId']; + $this->name = $values['name']; + $this->description = $values['description']; + $this->expectedValueParameterId = $values['expectedValueParameterId']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/V3/Problem/Types/CreateProblemRequestV2.php b/seed/php-sdk/trace/src/V2/V3/Problem/Types/CreateProblemRequestV2.php index 24b6bad5abf9..420d1fa2a10d 100644 --- a/seed/php-sdk/trace/src/V2/V3/Problem/Types/CreateProblemRequestV2.php +++ b/seed/php-sdk/trace/src/V2/V3/Problem/Types/CreateProblemRequestV2.php @@ -65,15 +65,21 @@ class CreateProblemRequestV2 extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->problemName = $values['problemName'];$this->problemDescription = $values['problemDescription'];$this->customFiles = $values['customFiles'];$this->customTestCaseTemplates = $values['customTestCaseTemplates'];$this->testcases = $values['testcases'];$this->supportedLanguages = $values['supportedLanguages'];$this->isPublic = $values['isPublic']; + ) { + $this->problemName = $values['problemName']; + $this->problemDescription = $values['problemDescription']; + $this->customFiles = $values['customFiles']; + $this->customTestCaseTemplates = $values['customTestCaseTemplates']; + $this->testcases = $values['testcases']; + $this->supportedLanguages = $values['supportedLanguages']; + $this->isPublic = $values['isPublic']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/V3/Problem/Types/CustomFiles.php b/seed/php-sdk/trace/src/V2/V3/Problem/Types/CustomFiles.php index f13ec9a907eb..5dce6b241738 100644 --- a/seed/php-sdk/trace/src/V2/V3/Problem/Types/CustomFiles.php +++ b/seed/php-sdk/trace/src/V2/V3/Problem/Types/CustomFiles.php @@ -43,16 +43,17 @@ class CustomFiles extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param BasicCustomFiles $basic * @return CustomFiles */ - public static function basic(BasicCustomFiles $basic): CustomFiles { + public static function basic(BasicCustomFiles $basic): CustomFiles + { return new CustomFiles([ 'type' => 'basic', 'value' => $basic, @@ -63,7 +64,8 @@ public static function basic(BasicCustomFiles $basic): CustomFiles { * @param array, Files> $custom * @return CustomFiles */ - public static function custom(array $custom): CustomFiles { + public static function custom(array $custom): CustomFiles + { return new CustomFiles([ 'type' => 'custom', 'value' => $custom, @@ -73,61 +75,67 @@ public static function custom(array $custom): CustomFiles { /** * @return bool */ - public function isBasic(): bool { - return $this->value instanceof BasicCustomFiles&& $this->type === 'basic'; + public function isBasic(): bool + { + return $this->value instanceof BasicCustomFiles && $this->type === 'basic'; } /** * @return BasicCustomFiles */ - public function asBasic(): BasicCustomFiles { - if (!($this->value instanceof BasicCustomFiles&& $this->type === 'basic')){ + public function asBasic(): BasicCustomFiles + { + if (!($this->value instanceof BasicCustomFiles && $this->type === 'basic')) { throw new Exception( "Expected basic; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isCustom(): bool { - return is_array($this->value)&& $this->type === 'custom'; + public function isCustom(): bool + { + return is_array($this->value) && $this->type === 'custom'; } /** * @return array, Files> */ - public function asCustom(): array { - if (!(is_array($this->value)&& $this->type === 'custom')){ + public function asCustom(): array + { + if (!(is_array($this->value) && $this->type === 'custom')) { throw new Exception( "Expected custom; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'basic': $value = $this->asBasic()->jsonSerialize(); $result = array_merge($value, $result); @@ -138,26 +146,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -166,32 +175,33 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'basic': $args['value'] = BasicCustomFiles::jsonDeserialize($data); break; case 'custom': - if (!array_key_exists('custom', $data)){ + if (!array_key_exists('custom', $data)) { throw new Exception( "JSON data is missing property 'custom'", ); } - + $args['value'] = $data['custom']; break; case '_unknown': @@ -199,7 +209,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/trace/src/V2/V3/Problem/Types/DeepEqualityCorrectnessCheck.php b/seed/php-sdk/trace/src/V2/V3/Problem/Types/DeepEqualityCorrectnessCheck.php index 17dd6c188920..5e7adc47f62a 100644 --- a/seed/php-sdk/trace/src/V2/V3/Problem/Types/DeepEqualityCorrectnessCheck.php +++ b/seed/php-sdk/trace/src/V2/V3/Problem/Types/DeepEqualityCorrectnessCheck.php @@ -20,15 +20,15 @@ class DeepEqualityCorrectnessCheck extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->expectedValueParameterId = $values['expectedValueParameterId']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/V3/Problem/Types/DefaultProvidedFile.php b/seed/php-sdk/trace/src/V2/V3/Problem/Types/DefaultProvidedFile.php index 2029ee93eed1..955b91661e65 100644 --- a/seed/php-sdk/trace/src/V2/V3/Problem/Types/DefaultProvidedFile.php +++ b/seed/php-sdk/trace/src/V2/V3/Problem/Types/DefaultProvidedFile.php @@ -29,15 +29,16 @@ class DefaultProvidedFile extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->file = $values['file'];$this->relatedTypes = $values['relatedTypes']; + ) { + $this->file = $values['file']; + $this->relatedTypes = $values['relatedTypes']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/V3/Problem/Types/FileInfoV2.php b/seed/php-sdk/trace/src/V2/V3/Problem/Types/FileInfoV2.php index f9deb87edaa4..ac488dbbd725 100644 --- a/seed/php-sdk/trace/src/V2/V3/Problem/Types/FileInfoV2.php +++ b/seed/php-sdk/trace/src/V2/V3/Problem/Types/FileInfoV2.php @@ -41,15 +41,18 @@ class FileInfoV2 extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->filename = $values['filename'];$this->directory = $values['directory'];$this->contents = $values['contents'];$this->editable = $values['editable']; + ) { + $this->filename = $values['filename']; + $this->directory = $values['directory']; + $this->contents = $values['contents']; + $this->editable = $values['editable']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/V3/Problem/Types/Files.php b/seed/php-sdk/trace/src/V2/V3/Problem/Types/Files.php index 619a43f108ed..652c42fb4800 100644 --- a/seed/php-sdk/trace/src/V2/V3/Problem/Types/Files.php +++ b/seed/php-sdk/trace/src/V2/V3/Problem/Types/Files.php @@ -21,15 +21,15 @@ class Files extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->files = $values['files']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/V3/Problem/Types/FunctionImplementation.php b/seed/php-sdk/trace/src/V2/V3/Problem/Types/FunctionImplementation.php index 9c67036e205d..0684de1a2f6c 100644 --- a/seed/php-sdk/trace/src/V2/V3/Problem/Types/FunctionImplementation.php +++ b/seed/php-sdk/trace/src/V2/V3/Problem/Types/FunctionImplementation.php @@ -27,15 +27,16 @@ class FunctionImplementation extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->impl = $values['impl'];$this->imports = $values['imports'] ?? null; + ) { + $this->impl = $values['impl']; + $this->imports = $values['imports'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/V3/Problem/Types/FunctionImplementationForMultipleLanguages.php b/seed/php-sdk/trace/src/V2/V3/Problem/Types/FunctionImplementationForMultipleLanguages.php index 70a196b44a48..0192af5fe885 100644 --- a/seed/php-sdk/trace/src/V2/V3/Problem/Types/FunctionImplementationForMultipleLanguages.php +++ b/seed/php-sdk/trace/src/V2/V3/Problem/Types/FunctionImplementationForMultipleLanguages.php @@ -22,15 +22,15 @@ class FunctionImplementationForMultipleLanguages extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->codeByLanguage = $values['codeByLanguage']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/V3/Problem/Types/FunctionSignature.php b/seed/php-sdk/trace/src/V2/V3/Problem/Types/FunctionSignature.php index 29aee34203ab..89d604493192 100644 --- a/seed/php-sdk/trace/src/V2/V3/Problem/Types/FunctionSignature.php +++ b/seed/php-sdk/trace/src/V2/V3/Problem/Types/FunctionSignature.php @@ -46,16 +46,17 @@ class FunctionSignature extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param VoidFunctionSignature $void * @return FunctionSignature */ - public static function void(VoidFunctionSignature $void): FunctionSignature { + public static function void(VoidFunctionSignature $void): FunctionSignature + { return new FunctionSignature([ 'type' => 'void', 'value' => $void, @@ -66,7 +67,8 @@ public static function void(VoidFunctionSignature $void): FunctionSignature { * @param NonVoidFunctionSignature $nonVoid * @return FunctionSignature */ - public static function nonVoid(NonVoidFunctionSignature $nonVoid): FunctionSignature { + public static function nonVoid(NonVoidFunctionSignature $nonVoid): FunctionSignature + { return new FunctionSignature([ 'type' => 'nonVoid', 'value' => $nonVoid, @@ -77,7 +79,8 @@ public static function nonVoid(NonVoidFunctionSignature $nonVoid): FunctionSigna * @param VoidFunctionSignatureThatTakesActualResult $voidThatTakesActualResult * @return FunctionSignature */ - public static function voidThatTakesActualResult(VoidFunctionSignatureThatTakesActualResult $voidThatTakesActualResult): FunctionSignature { + public static function voidThatTakesActualResult(VoidFunctionSignatureThatTakesActualResult $voidThatTakesActualResult): FunctionSignature + { return new FunctionSignature([ 'type' => 'voidThatTakesActualResult', 'value' => $voidThatTakesActualResult, @@ -87,81 +90,89 @@ public static function voidThatTakesActualResult(VoidFunctionSignatureThatTakesA /** * @return bool */ - public function isVoid(): bool { - return $this->value instanceof VoidFunctionSignature&& $this->type === 'void'; + public function isVoid(): bool + { + return $this->value instanceof VoidFunctionSignature && $this->type === 'void'; } /** * @return VoidFunctionSignature */ - public function asVoid(): VoidFunctionSignature { - if (!($this->value instanceof VoidFunctionSignature&& $this->type === 'void')){ + public function asVoid(): VoidFunctionSignature + { + if (!($this->value instanceof VoidFunctionSignature && $this->type === 'void')) { throw new Exception( "Expected void; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isNonVoid(): bool { - return $this->value instanceof NonVoidFunctionSignature&& $this->type === 'nonVoid'; + public function isNonVoid(): bool + { + return $this->value instanceof NonVoidFunctionSignature && $this->type === 'nonVoid'; } /** * @return NonVoidFunctionSignature */ - public function asNonVoid(): NonVoidFunctionSignature { - if (!($this->value instanceof NonVoidFunctionSignature&& $this->type === 'nonVoid')){ + public function asNonVoid(): NonVoidFunctionSignature + { + if (!($this->value instanceof NonVoidFunctionSignature && $this->type === 'nonVoid')) { throw new Exception( "Expected nonVoid; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isVoidThatTakesActualResult(): bool { - return $this->value instanceof VoidFunctionSignatureThatTakesActualResult&& $this->type === 'voidThatTakesActualResult'; + public function isVoidThatTakesActualResult(): bool + { + return $this->value instanceof VoidFunctionSignatureThatTakesActualResult && $this->type === 'voidThatTakesActualResult'; } /** * @return VoidFunctionSignatureThatTakesActualResult */ - public function asVoidThatTakesActualResult(): VoidFunctionSignatureThatTakesActualResult { - if (!($this->value instanceof VoidFunctionSignatureThatTakesActualResult&& $this->type === 'voidThatTakesActualResult')){ + public function asVoidThatTakesActualResult(): VoidFunctionSignatureThatTakesActualResult + { + if (!($this->value instanceof VoidFunctionSignatureThatTakesActualResult && $this->type === 'voidThatTakesActualResult')) { throw new Exception( "Expected voidThatTakesActualResult; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'void': $value = $this->asVoid()->jsonSerialize(); $result = array_merge($value, $result); @@ -176,26 +187,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -204,22 +216,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'void': $args['value'] = VoidFunctionSignature::jsonDeserialize($data); break; @@ -234,7 +247,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/trace/src/V2/V3/Problem/Types/GeneratedFiles.php b/seed/php-sdk/trace/src/V2/V3/Problem/Types/GeneratedFiles.php index e73b86a6ae42..f6efac327d66 100644 --- a/seed/php-sdk/trace/src/V2/V3/Problem/Types/GeneratedFiles.php +++ b/seed/php-sdk/trace/src/V2/V3/Problem/Types/GeneratedFiles.php @@ -36,15 +36,17 @@ class GeneratedFiles extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->generatedTestCaseFiles = $values['generatedTestCaseFiles'];$this->generatedTemplateFiles = $values['generatedTemplateFiles'];$this->other = $values['other']; + ) { + $this->generatedTestCaseFiles = $values['generatedTestCaseFiles']; + $this->generatedTemplateFiles = $values['generatedTemplateFiles']; + $this->other = $values['other']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/V3/Problem/Types/GetBasicSolutionFileRequest.php b/seed/php-sdk/trace/src/V2/V3/Problem/Types/GetBasicSolutionFileRequest.php index 5d327818f590..d4a9f8f09226 100644 --- a/seed/php-sdk/trace/src/V2/V3/Problem/Types/GetBasicSolutionFileRequest.php +++ b/seed/php-sdk/trace/src/V2/V3/Problem/Types/GetBasicSolutionFileRequest.php @@ -27,15 +27,16 @@ class GetBasicSolutionFileRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->methodName = $values['methodName'];$this->signature = $values['signature']; + ) { + $this->methodName = $values['methodName']; + $this->signature = $values['signature']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/V3/Problem/Types/GetBasicSolutionFileResponse.php b/seed/php-sdk/trace/src/V2/V3/Problem/Types/GetBasicSolutionFileResponse.php index bd34d4a983be..fcbd0e8d7d7c 100644 --- a/seed/php-sdk/trace/src/V2/V3/Problem/Types/GetBasicSolutionFileResponse.php +++ b/seed/php-sdk/trace/src/V2/V3/Problem/Types/GetBasicSolutionFileResponse.php @@ -22,15 +22,15 @@ class GetBasicSolutionFileResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->solutionFileByLanguage = $values['solutionFileByLanguage']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/V3/Problem/Types/GetFunctionSignatureRequest.php b/seed/php-sdk/trace/src/V2/V3/Problem/Types/GetFunctionSignatureRequest.php index 6bc984e3a861..f2475adec0c4 100644 --- a/seed/php-sdk/trace/src/V2/V3/Problem/Types/GetFunctionSignatureRequest.php +++ b/seed/php-sdk/trace/src/V2/V3/Problem/Types/GetFunctionSignatureRequest.php @@ -20,15 +20,15 @@ class GetFunctionSignatureRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->functionSignature = $values['functionSignature']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/V3/Problem/Types/GetFunctionSignatureResponse.php b/seed/php-sdk/trace/src/V2/V3/Problem/Types/GetFunctionSignatureResponse.php index 0da642fda79e..6b31a214f573 100644 --- a/seed/php-sdk/trace/src/V2/V3/Problem/Types/GetFunctionSignatureResponse.php +++ b/seed/php-sdk/trace/src/V2/V3/Problem/Types/GetFunctionSignatureResponse.php @@ -22,15 +22,15 @@ class GetFunctionSignatureResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->functionByLanguage = $values['functionByLanguage']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/V3/Problem/Types/GetGeneratedTestCaseFileRequest.php b/seed/php-sdk/trace/src/V2/V3/Problem/Types/GetGeneratedTestCaseFileRequest.php index 15f43557072b..b54607a5e641 100644 --- a/seed/php-sdk/trace/src/V2/V3/Problem/Types/GetGeneratedTestCaseFileRequest.php +++ b/seed/php-sdk/trace/src/V2/V3/Problem/Types/GetGeneratedTestCaseFileRequest.php @@ -27,15 +27,16 @@ class GetGeneratedTestCaseFileRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->template = $values['template'] ?? null;$this->testCase = $values['testCase']; + ) { + $this->template = $values['template'] ?? null; + $this->testCase = $values['testCase']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/V3/Problem/Types/GetGeneratedTestCaseTemplateFileRequest.php b/seed/php-sdk/trace/src/V2/V3/Problem/Types/GetGeneratedTestCaseTemplateFileRequest.php index 2f3a12cc51fb..faa1a6bcf7d6 100644 --- a/seed/php-sdk/trace/src/V2/V3/Problem/Types/GetGeneratedTestCaseTemplateFileRequest.php +++ b/seed/php-sdk/trace/src/V2/V3/Problem/Types/GetGeneratedTestCaseTemplateFileRequest.php @@ -20,15 +20,15 @@ class GetGeneratedTestCaseTemplateFileRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->template = $values['template']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/V3/Problem/Types/LightweightProblemInfoV2.php b/seed/php-sdk/trace/src/V2/V3/Problem/Types/LightweightProblemInfoV2.php index 6d18ac1ecd39..4d4f4b372227 100644 --- a/seed/php-sdk/trace/src/V2/V3/Problem/Types/LightweightProblemInfoV2.php +++ b/seed/php-sdk/trace/src/V2/V3/Problem/Types/LightweightProblemInfoV2.php @@ -43,15 +43,18 @@ class LightweightProblemInfoV2 extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->problemId = $values['problemId'];$this->problemName = $values['problemName'];$this->problemVersion = $values['problemVersion'];$this->variableTypes = $values['variableTypes']; + ) { + $this->problemId = $values['problemId']; + $this->problemName = $values['problemName']; + $this->problemVersion = $values['problemVersion']; + $this->variableTypes = $values['variableTypes']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/V3/Problem/Types/NonVoidFunctionDefinition.php b/seed/php-sdk/trace/src/V2/V3/Problem/Types/NonVoidFunctionDefinition.php index c5abb7d9fefb..8b20e6f5520e 100644 --- a/seed/php-sdk/trace/src/V2/V3/Problem/Types/NonVoidFunctionDefinition.php +++ b/seed/php-sdk/trace/src/V2/V3/Problem/Types/NonVoidFunctionDefinition.php @@ -27,15 +27,16 @@ class NonVoidFunctionDefinition extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->signature = $values['signature'];$this->code = $values['code']; + ) { + $this->signature = $values['signature']; + $this->code = $values['code']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/V3/Problem/Types/NonVoidFunctionSignature.php b/seed/php-sdk/trace/src/V2/V3/Problem/Types/NonVoidFunctionSignature.php index db5f34e7f164..daa8c76ee5ee 100644 --- a/seed/php-sdk/trace/src/V2/V3/Problem/Types/NonVoidFunctionSignature.php +++ b/seed/php-sdk/trace/src/V2/V3/Problem/Types/NonVoidFunctionSignature.php @@ -29,15 +29,16 @@ class NonVoidFunctionSignature extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->parameters = $values['parameters'];$this->returnType = $values['returnType']; + ) { + $this->parameters = $values['parameters']; + $this->returnType = $values['returnType']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/V3/Problem/Types/Parameter.php b/seed/php-sdk/trace/src/V2/V3/Problem/Types/Parameter.php index a0189789f91b..706137938728 100644 --- a/seed/php-sdk/trace/src/V2/V3/Problem/Types/Parameter.php +++ b/seed/php-sdk/trace/src/V2/V3/Problem/Types/Parameter.php @@ -35,15 +35,17 @@ class Parameter extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->parameterId = $values['parameterId'];$this->name = $values['name'];$this->variableType = $values['variableType']; + ) { + $this->parameterId = $values['parameterId']; + $this->name = $values['name']; + $this->variableType = $values['variableType']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/V3/Problem/Types/ProblemInfoV2.php b/seed/php-sdk/trace/src/V2/V3/Problem/Types/ProblemInfoV2.php index ca982808e985..0b53044445d6 100644 --- a/seed/php-sdk/trace/src/V2/V3/Problem/Types/ProblemInfoV2.php +++ b/seed/php-sdk/trace/src/V2/V3/Problem/Types/ProblemInfoV2.php @@ -86,15 +86,24 @@ class ProblemInfoV2 extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->problemId = $values['problemId'];$this->problemDescription = $values['problemDescription'];$this->problemName = $values['problemName'];$this->problemVersion = $values['problemVersion'];$this->supportedLanguages = $values['supportedLanguages'];$this->customFiles = $values['customFiles'];$this->generatedFiles = $values['generatedFiles'];$this->customTestCaseTemplates = $values['customTestCaseTemplates'];$this->testcases = $values['testcases'];$this->isPublic = $values['isPublic']; + ) { + $this->problemId = $values['problemId']; + $this->problemDescription = $values['problemDescription']; + $this->problemName = $values['problemName']; + $this->problemVersion = $values['problemVersion']; + $this->supportedLanguages = $values['supportedLanguages']; + $this->customFiles = $values['customFiles']; + $this->generatedFiles = $values['generatedFiles']; + $this->customTestCaseTemplates = $values['customTestCaseTemplates']; + $this->testcases = $values['testcases']; + $this->isPublic = $values['isPublic']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/V3/Problem/Types/TestCaseExpects.php b/seed/php-sdk/trace/src/V2/V3/Problem/Types/TestCaseExpects.php index cdb3379fb0c8..dd90a6043e59 100644 --- a/seed/php-sdk/trace/src/V2/V3/Problem/Types/TestCaseExpects.php +++ b/seed/php-sdk/trace/src/V2/V3/Problem/Types/TestCaseExpects.php @@ -20,15 +20,15 @@ class TestCaseExpects extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->expectedStdout = $values['expectedStdout'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/V3/Problem/Types/TestCaseFunction.php b/seed/php-sdk/trace/src/V2/V3/Problem/Types/TestCaseFunction.php index fcb57baeb47b..f473efad85dc 100644 --- a/seed/php-sdk/trace/src/V2/V3/Problem/Types/TestCaseFunction.php +++ b/seed/php-sdk/trace/src/V2/V3/Problem/Types/TestCaseFunction.php @@ -42,16 +42,17 @@ class TestCaseFunction extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param TestCaseWithActualResultImplementation $withActualResult * @return TestCaseFunction */ - public static function withActualResult(TestCaseWithActualResultImplementation $withActualResult): TestCaseFunction { + public static function withActualResult(TestCaseWithActualResultImplementation $withActualResult): TestCaseFunction + { return new TestCaseFunction([ 'type' => 'withActualResult', 'value' => $withActualResult, @@ -62,7 +63,8 @@ public static function withActualResult(TestCaseWithActualResultImplementation $ * @param VoidFunctionDefinition $custom * @return TestCaseFunction */ - public static function custom(VoidFunctionDefinition $custom): TestCaseFunction { + public static function custom(VoidFunctionDefinition $custom): TestCaseFunction + { return new TestCaseFunction([ 'type' => 'custom', 'value' => $custom, @@ -72,61 +74,67 @@ public static function custom(VoidFunctionDefinition $custom): TestCaseFunction /** * @return bool */ - public function isWithActualResult(): bool { - return $this->value instanceof TestCaseWithActualResultImplementation&& $this->type === 'withActualResult'; + public function isWithActualResult(): bool + { + return $this->value instanceof TestCaseWithActualResultImplementation && $this->type === 'withActualResult'; } /** * @return TestCaseWithActualResultImplementation */ - public function asWithActualResult(): TestCaseWithActualResultImplementation { - if (!($this->value instanceof TestCaseWithActualResultImplementation&& $this->type === 'withActualResult')){ + public function asWithActualResult(): TestCaseWithActualResultImplementation + { + if (!($this->value instanceof TestCaseWithActualResultImplementation && $this->type === 'withActualResult')) { throw new Exception( "Expected withActualResult; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isCustom(): bool { - return $this->value instanceof VoidFunctionDefinition&& $this->type === 'custom'; + public function isCustom(): bool + { + return $this->value instanceof VoidFunctionDefinition && $this->type === 'custom'; } /** * @return VoidFunctionDefinition */ - public function asCustom(): VoidFunctionDefinition { - if (!($this->value instanceof VoidFunctionDefinition&& $this->type === 'custom')){ + public function asCustom(): VoidFunctionDefinition + { + if (!($this->value instanceof VoidFunctionDefinition && $this->type === 'custom')) { throw new Exception( "Expected custom; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'withActualResult': $value = $this->asWithActualResult()->jsonSerialize(); $result = array_merge($value, $result); @@ -137,26 +145,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -165,22 +174,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'withActualResult': $args['value'] = TestCaseWithActualResultImplementation::jsonDeserialize($data); break; @@ -192,7 +202,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/trace/src/V2/V3/Problem/Types/TestCaseImplementation.php b/seed/php-sdk/trace/src/V2/V3/Problem/Types/TestCaseImplementation.php index 81c82f88bce0..7d80e011765e 100644 --- a/seed/php-sdk/trace/src/V2/V3/Problem/Types/TestCaseImplementation.php +++ b/seed/php-sdk/trace/src/V2/V3/Problem/Types/TestCaseImplementation.php @@ -27,15 +27,16 @@ class TestCaseImplementation extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->description = $values['description'];$this->function = $values['function']; + ) { + $this->description = $values['description']; + $this->function = $values['function']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/V3/Problem/Types/TestCaseImplementationDescription.php b/seed/php-sdk/trace/src/V2/V3/Problem/Types/TestCaseImplementationDescription.php index f221e741ae26..cdad344631fd 100644 --- a/seed/php-sdk/trace/src/V2/V3/Problem/Types/TestCaseImplementationDescription.php +++ b/seed/php-sdk/trace/src/V2/V3/Problem/Types/TestCaseImplementationDescription.php @@ -21,15 +21,15 @@ class TestCaseImplementationDescription extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->boards = $values['boards']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/V3/Problem/Types/TestCaseImplementationDescriptionBoard.php b/seed/php-sdk/trace/src/V2/V3/Problem/Types/TestCaseImplementationDescriptionBoard.php index cfedf7cde952..7f52460dce15 100644 --- a/seed/php-sdk/trace/src/V2/V3/Problem/Types/TestCaseImplementationDescriptionBoard.php +++ b/seed/php-sdk/trace/src/V2/V3/Problem/Types/TestCaseImplementationDescriptionBoard.php @@ -40,16 +40,17 @@ class TestCaseImplementationDescriptionBoard extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param string $html * @return TestCaseImplementationDescriptionBoard */ - public static function html(string $html): TestCaseImplementationDescriptionBoard { + public static function html(string $html): TestCaseImplementationDescriptionBoard + { return new TestCaseImplementationDescriptionBoard([ 'type' => 'html', 'value' => $html, @@ -60,7 +61,8 @@ public static function html(string $html): TestCaseImplementationDescriptionBoar * @param string $paramId * @return TestCaseImplementationDescriptionBoard */ - public static function paramId(string $paramId): TestCaseImplementationDescriptionBoard { + public static function paramId(string $paramId): TestCaseImplementationDescriptionBoard + { return new TestCaseImplementationDescriptionBoard([ 'type' => 'paramId', 'value' => $paramId, @@ -70,61 +72,67 @@ public static function paramId(string $paramId): TestCaseImplementationDescripti /** * @return bool */ - public function isHtml(): bool { - return is_string($this->value)&& $this->type === 'html'; + public function isHtml(): bool + { + return is_string($this->value) && $this->type === 'html'; } /** * @return string */ - public function asHtml(): string { - if (!(is_string($this->value)&& $this->type === 'html')){ + public function asHtml(): string + { + if (!(is_string($this->value) && $this->type === 'html')) { throw new Exception( "Expected html; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isParamId(): bool { - return is_string($this->value)&& $this->type === 'paramId'; + public function isParamId(): bool + { + return is_string($this->value) && $this->type === 'paramId'; } /** * @return string */ - public function asParamId(): string { - if (!(is_string($this->value)&& $this->type === 'paramId')){ + public function asParamId(): string + { + if (!(is_string($this->value) && $this->type === 'paramId')) { throw new Exception( "Expected paramId; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'html': $value = $this->value; $result['html'] = $value; @@ -135,26 +143,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -163,38 +172,39 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'html': - if (!array_key_exists('html', $data)){ + if (!array_key_exists('html', $data)) { throw new Exception( "JSON data is missing property 'html'", ); } - + $args['value'] = $data['html']; break; case 'paramId': - if (!array_key_exists('paramId', $data)){ + if (!array_key_exists('paramId', $data)) { throw new Exception( "JSON data is missing property 'paramId'", ); } - + $args['value'] = $data['paramId']; break; case '_unknown': @@ -202,7 +212,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/trace/src/V2/V3/Problem/Types/TestCaseImplementationReference.php b/seed/php-sdk/trace/src/V2/V3/Problem/Types/TestCaseImplementationReference.php index 719e391ca62f..2f2ae64019ae 100644 --- a/seed/php-sdk/trace/src/V2/V3/Problem/Types/TestCaseImplementationReference.php +++ b/seed/php-sdk/trace/src/V2/V3/Problem/Types/TestCaseImplementationReference.php @@ -42,16 +42,17 @@ class TestCaseImplementationReference extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param string $templateId * @return TestCaseImplementationReference */ - public static function templateId(string $templateId): TestCaseImplementationReference { + public static function templateId(string $templateId): TestCaseImplementationReference + { return new TestCaseImplementationReference([ 'type' => 'templateId', 'value' => $templateId, @@ -62,7 +63,8 @@ public static function templateId(string $templateId): TestCaseImplementationRef * @param TestCaseImplementation $implementation * @return TestCaseImplementationReference */ - public static function implementation(TestCaseImplementation $implementation): TestCaseImplementationReference { + public static function implementation(TestCaseImplementation $implementation): TestCaseImplementationReference + { return new TestCaseImplementationReference([ 'type' => 'implementation', 'value' => $implementation, @@ -72,61 +74,67 @@ public static function implementation(TestCaseImplementation $implementation): T /** * @return bool */ - public function isTemplateId(): bool { - return is_string($this->value)&& $this->type === 'templateId'; + public function isTemplateId(): bool + { + return is_string($this->value) && $this->type === 'templateId'; } /** * @return string */ - public function asTemplateId(): string { - if (!(is_string($this->value)&& $this->type === 'templateId')){ + public function asTemplateId(): string + { + if (!(is_string($this->value) && $this->type === 'templateId')) { throw new Exception( "Expected templateId; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isImplementation(): bool { - return $this->value instanceof TestCaseImplementation&& $this->type === 'implementation'; + public function isImplementation(): bool + { + return $this->value instanceof TestCaseImplementation && $this->type === 'implementation'; } /** * @return TestCaseImplementation */ - public function asImplementation(): TestCaseImplementation { - if (!($this->value instanceof TestCaseImplementation&& $this->type === 'implementation')){ + public function asImplementation(): TestCaseImplementation + { + if (!($this->value instanceof TestCaseImplementation && $this->type === 'implementation')) { throw new Exception( "Expected implementation; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'templateId': $value = $this->value; $result['templateId'] = $value; @@ -137,26 +145,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -165,29 +174,30 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'templateId': - if (!array_key_exists('templateId', $data)){ + if (!array_key_exists('templateId', $data)) { throw new Exception( "JSON data is missing property 'templateId'", ); } - + $args['value'] = $data['templateId']; break; case 'implementation': @@ -198,7 +208,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/trace/src/V2/V3/Problem/Types/TestCaseMetadata.php b/seed/php-sdk/trace/src/V2/V3/Problem/Types/TestCaseMetadata.php index af16e737d28e..c00339ee0680 100644 --- a/seed/php-sdk/trace/src/V2/V3/Problem/Types/TestCaseMetadata.php +++ b/seed/php-sdk/trace/src/V2/V3/Problem/Types/TestCaseMetadata.php @@ -34,15 +34,17 @@ class TestCaseMetadata extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->name = $values['name'];$this->hidden = $values['hidden']; + ) { + $this->id = $values['id']; + $this->name = $values['name']; + $this->hidden = $values['hidden']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/V3/Problem/Types/TestCaseTemplate.php b/seed/php-sdk/trace/src/V2/V3/Problem/Types/TestCaseTemplate.php index 5af78a4ef91b..3105c9c4e0ae 100644 --- a/seed/php-sdk/trace/src/V2/V3/Problem/Types/TestCaseTemplate.php +++ b/seed/php-sdk/trace/src/V2/V3/Problem/Types/TestCaseTemplate.php @@ -34,15 +34,17 @@ class TestCaseTemplate extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->templateId = $values['templateId'];$this->name = $values['name'];$this->implementation = $values['implementation']; + ) { + $this->templateId = $values['templateId']; + $this->name = $values['name']; + $this->implementation = $values['implementation']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/V3/Problem/Types/TestCaseV2.php b/seed/php-sdk/trace/src/V2/V3/Problem/Types/TestCaseV2.php index 00b3a93620ce..b3e27fae7c7c 100644 --- a/seed/php-sdk/trace/src/V2/V3/Problem/Types/TestCaseV2.php +++ b/seed/php-sdk/trace/src/V2/V3/Problem/Types/TestCaseV2.php @@ -43,15 +43,18 @@ class TestCaseV2 extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->metadata = $values['metadata'];$this->implementation = $values['implementation'];$this->arguments = $values['arguments'];$this->expects = $values['expects'] ?? null; + ) { + $this->metadata = $values['metadata']; + $this->implementation = $values['implementation']; + $this->arguments = $values['arguments']; + $this->expects = $values['expects'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/V3/Problem/Types/TestCaseWithActualResultImplementation.php b/seed/php-sdk/trace/src/V2/V3/Problem/Types/TestCaseWithActualResultImplementation.php index 11c4b630a8a0..082672d8cd83 100644 --- a/seed/php-sdk/trace/src/V2/V3/Problem/Types/TestCaseWithActualResultImplementation.php +++ b/seed/php-sdk/trace/src/V2/V3/Problem/Types/TestCaseWithActualResultImplementation.php @@ -27,15 +27,16 @@ class TestCaseWithActualResultImplementation extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->getActualResult = $values['getActualResult'];$this->assertCorrectnessCheck = $values['assertCorrectnessCheck']; + ) { + $this->getActualResult = $values['getActualResult']; + $this->assertCorrectnessCheck = $values['assertCorrectnessCheck']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/V3/Problem/Types/VoidFunctionDefinition.php b/seed/php-sdk/trace/src/V2/V3/Problem/Types/VoidFunctionDefinition.php index 0b78c34c9a0f..2abaa2ff1b55 100644 --- a/seed/php-sdk/trace/src/V2/V3/Problem/Types/VoidFunctionDefinition.php +++ b/seed/php-sdk/trace/src/V2/V3/Problem/Types/VoidFunctionDefinition.php @@ -28,15 +28,16 @@ class VoidFunctionDefinition extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->parameters = $values['parameters'];$this->code = $values['code']; + ) { + $this->parameters = $values['parameters']; + $this->code = $values['code']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/V3/Problem/Types/VoidFunctionDefinitionThatTakesActualResult.php b/seed/php-sdk/trace/src/V2/V3/Problem/Types/VoidFunctionDefinitionThatTakesActualResult.php index 70433323eabd..661af4c67233 100644 --- a/seed/php-sdk/trace/src/V2/V3/Problem/Types/VoidFunctionDefinitionThatTakesActualResult.php +++ b/seed/php-sdk/trace/src/V2/V3/Problem/Types/VoidFunctionDefinitionThatTakesActualResult.php @@ -31,15 +31,16 @@ class VoidFunctionDefinitionThatTakesActualResult extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->additionalParameters = $values['additionalParameters'];$this->code = $values['code']; + ) { + $this->additionalParameters = $values['additionalParameters']; + $this->code = $values['code']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/V3/Problem/Types/VoidFunctionSignature.php b/seed/php-sdk/trace/src/V2/V3/Problem/Types/VoidFunctionSignature.php index 0bea74350303..64f9b37af4f5 100644 --- a/seed/php-sdk/trace/src/V2/V3/Problem/Types/VoidFunctionSignature.php +++ b/seed/php-sdk/trace/src/V2/V3/Problem/Types/VoidFunctionSignature.php @@ -21,15 +21,15 @@ class VoidFunctionSignature extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->parameters = $values['parameters']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/V3/Problem/Types/VoidFunctionSignatureThatTakesActualResult.php b/seed/php-sdk/trace/src/V2/V3/Problem/Types/VoidFunctionSignatureThatTakesActualResult.php index 66807cd7d277..a99f4f7e0dbf 100644 --- a/seed/php-sdk/trace/src/V2/V3/Problem/Types/VoidFunctionSignatureThatTakesActualResult.php +++ b/seed/php-sdk/trace/src/V2/V3/Problem/Types/VoidFunctionSignatureThatTakesActualResult.php @@ -29,15 +29,16 @@ class VoidFunctionSignatureThatTakesActualResult extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->parameters = $values['parameters'];$this->actualResultType = $values['actualResultType']; + ) { + $this->parameters = $values['parameters']; + $this->actualResultType = $values['actualResultType']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/trace/src/V2/V3/V3Client.php b/seed/php-sdk/trace/src/V2/V3/V3Client.php index df40fb3067a8..b34481016fb5 100644 --- a/seed/php-sdk/trace/src/V2/V3/V3Client.php +++ b/seed/php-sdk/trace/src/V2/V3/V3Client.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class V3Client +class V3Client { /** * @var ProblemClient $problem @@ -39,11 +39,10 @@ class V3Client * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; $this->problem = new ProblemClient($this->client, $this->options); diff --git a/seed/php-sdk/trace/tests/Core/Client/RawClientTest.php b/seed/php-sdk/trace/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/trace/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/trace/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/trace/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/trace/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/trace/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/trace/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/trace/tests/Core/Json/EnumTest.php b/seed/php-sdk/trace/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/trace/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/trace/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/trace/tests/Core/Json/TraitTest.php b/seed/php-sdk/trace/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/trace/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/trace/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/trace/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/trace/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/trace/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/trace/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/undiscriminated-union-with-response-property/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/undiscriminated-union-with-response-property/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/undiscriminated-union-with-response-property/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/undiscriminated-union-with-response-property/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/undiscriminated-union-with-response-property/src/Core/Client/RawClient.php b/seed/php-sdk/undiscriminated-union-with-response-property/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/undiscriminated-union-with-response-property/src/Core/Client/RawClient.php +++ b/seed/php-sdk/undiscriminated-union-with-response-property/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/undiscriminated-union-with-response-property/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/undiscriminated-union-with-response-property/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/undiscriminated-union-with-response-property/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/undiscriminated-union-with-response-property/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/undiscriminated-union-with-response-property/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/undiscriminated-union-with-response-property/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/undiscriminated-union-with-response-property/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/undiscriminated-union-with-response-property/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/undiscriminated-union-with-response-property/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/undiscriminated-union-with-response-property/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/undiscriminated-union-with-response-property/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/undiscriminated-union-with-response-property/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/undiscriminated-union-with-response-property/src/Core/Types/ArrayType.php b/seed/php-sdk/undiscriminated-union-with-response-property/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/undiscriminated-union-with-response-property/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/undiscriminated-union-with-response-property/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/undiscriminated-union-with-response-property/src/Core/Types/Constant.php b/seed/php-sdk/undiscriminated-union-with-response-property/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/undiscriminated-union-with-response-property/src/Core/Types/Constant.php +++ b/seed/php-sdk/undiscriminated-union-with-response-property/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/undiscriminated-union-with-response-property/src/Core/Types/Union.php b/seed/php-sdk/undiscriminated-union-with-response-property/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/undiscriminated-union-with-response-property/src/Core/Types/Union.php +++ b/seed/php-sdk/undiscriminated-union-with-response-property/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/undiscriminated-union-with-response-property/src/Exceptions/SeedApiException.php b/seed/php-sdk/undiscriminated-union-with-response-property/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/undiscriminated-union-with-response-property/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/undiscriminated-union-with-response-property/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/undiscriminated-union-with-response-property/src/Exceptions/SeedException.php b/seed/php-sdk/undiscriminated-union-with-response-property/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/undiscriminated-union-with-response-property/src/Exceptions/SeedException.php +++ b/seed/php-sdk/undiscriminated-union-with-response-property/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/undiscriminated-union-with-response-property/src/SeedClient.php b/seed/php-sdk/undiscriminated-union-with-response-property/src/SeedClient.php index dbb2f741fac1..836548d572bc 100644 --- a/seed/php-sdk/undiscriminated-union-with-response-property/src/SeedClient.php +++ b/seed/php-sdk/undiscriminated-union-with-response-property/src/SeedClient.php @@ -14,7 +14,7 @@ use Psr\Http\Client\ClientExceptionInterface; use Seed\Types\UnionListResponse; -class SeedClient +class SeedClient { /** * @var array{ @@ -43,22 +43,21 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); @@ -77,7 +76,8 @@ public function __construct( * @throws SeedException * @throws SeedApiException */ - public function getUnion(?array $options = null): UnionResponse { + public function getUnion(?array $options = null): UnionResponse + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -89,15 +89,15 @@ public function getUnion(?array $options = null): UnionResponse { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return UnionResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -128,7 +128,8 @@ public function getUnion(?array $options = null): UnionResponse { * @throws SeedException * @throws SeedApiException */ - public function listUnions(?array $options = null): UnionListResponse { + public function listUnions(?array $options = null): UnionListResponse + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -140,15 +141,15 @@ public function listUnions(?array $options = null): UnionListResponse { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return UnionListResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/undiscriminated-union-with-response-property/src/Types/UnionListResponse.php b/seed/php-sdk/undiscriminated-union-with-response-property/src/Types/UnionListResponse.php index b20ba722c8a6..19e7efa41041 100644 --- a/seed/php-sdk/undiscriminated-union-with-response-property/src/Types/UnionListResponse.php +++ b/seed/php-sdk/undiscriminated-union-with-response-property/src/Types/UnionListResponse.php @@ -30,15 +30,15 @@ class UnionListResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->data = $values['data']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/undiscriminated-union-with-response-property/src/Types/UnionResponse.php b/seed/php-sdk/undiscriminated-union-with-response-property/src/Types/UnionResponse.php index 2c1bcca445b6..0aa4aab2c402 100644 --- a/seed/php-sdk/undiscriminated-union-with-response-property/src/Types/UnionResponse.php +++ b/seed/php-sdk/undiscriminated-union-with-response-property/src/Types/UnionResponse.php @@ -15,7 +15,7 @@ class UnionResponse extends JsonSerializableType * |VariantC * ) $data */ - #[JsonProperty('data'), Union(VariantA::class,VariantB::class,VariantC::class)] + #[JsonProperty('data'), Union(VariantA::class, VariantB::class, VariantC::class)] public VariantA|VariantB|VariantC $data; /** @@ -29,15 +29,15 @@ class UnionResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->data = $values['data']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/undiscriminated-union-with-response-property/src/Types/VariantA.php b/seed/php-sdk/undiscriminated-union-with-response-property/src/Types/VariantA.php index 20625ea5a002..1a59cb75422e 100644 --- a/seed/php-sdk/undiscriminated-union-with-response-property/src/Types/VariantA.php +++ b/seed/php-sdk/undiscriminated-union-with-response-property/src/Types/VariantA.php @@ -27,15 +27,16 @@ class VariantA extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->type = $values['type'];$this->valueA = $values['valueA']; + ) { + $this->type = $values['type']; + $this->valueA = $values['valueA']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/undiscriminated-union-with-response-property/src/Types/VariantB.php b/seed/php-sdk/undiscriminated-union-with-response-property/src/Types/VariantB.php index 17831f543298..8e36765879ac 100644 --- a/seed/php-sdk/undiscriminated-union-with-response-property/src/Types/VariantB.php +++ b/seed/php-sdk/undiscriminated-union-with-response-property/src/Types/VariantB.php @@ -27,15 +27,16 @@ class VariantB extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->type = $values['type'];$this->valueB = $values['valueB']; + ) { + $this->type = $values['type']; + $this->valueB = $values['valueB']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/undiscriminated-union-with-response-property/src/Types/VariantC.php b/seed/php-sdk/undiscriminated-union-with-response-property/src/Types/VariantC.php index c26d5d8536e6..cd35762df0a0 100644 --- a/seed/php-sdk/undiscriminated-union-with-response-property/src/Types/VariantC.php +++ b/seed/php-sdk/undiscriminated-union-with-response-property/src/Types/VariantC.php @@ -27,15 +27,16 @@ class VariantC extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->type = $values['type'];$this->valueC = $values['valueC']; + ) { + $this->type = $values['type']; + $this->valueC = $values['valueC']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/undiscriminated-union-with-response-property/src/Utils/File.php b/seed/php-sdk/undiscriminated-union-with-response-property/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/undiscriminated-union-with-response-property/src/Utils/File.php +++ b/seed/php-sdk/undiscriminated-union-with-response-property/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/undiscriminated-union-with-response-property/tests/Core/Client/RawClientTest.php b/seed/php-sdk/undiscriminated-union-with-response-property/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/undiscriminated-union-with-response-property/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/undiscriminated-union-with-response-property/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/undiscriminated-union-with-response-property/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/undiscriminated-union-with-response-property/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/undiscriminated-union-with-response-property/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/undiscriminated-union-with-response-property/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/undiscriminated-union-with-response-property/tests/Core/Json/EnumTest.php b/seed/php-sdk/undiscriminated-union-with-response-property/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/undiscriminated-union-with-response-property/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/undiscriminated-union-with-response-property/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/undiscriminated-union-with-response-property/tests/Core/Json/TraitTest.php b/seed/php-sdk/undiscriminated-union-with-response-property/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/undiscriminated-union-with-response-property/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/undiscriminated-union-with-response-property/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/undiscriminated-union-with-response-property/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/undiscriminated-union-with-response-property/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/undiscriminated-union-with-response-property/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/undiscriminated-union-with-response-property/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/undiscriminated-unions/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/undiscriminated-unions/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/undiscriminated-unions/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/undiscriminated-unions/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/undiscriminated-unions/src/Core/Client/RawClient.php b/seed/php-sdk/undiscriminated-unions/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/undiscriminated-unions/src/Core/Client/RawClient.php +++ b/seed/php-sdk/undiscriminated-unions/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/undiscriminated-unions/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/undiscriminated-unions/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/undiscriminated-unions/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/undiscriminated-unions/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/undiscriminated-unions/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/undiscriminated-unions/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/undiscriminated-unions/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/undiscriminated-unions/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/undiscriminated-unions/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/undiscriminated-unions/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/undiscriminated-unions/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/undiscriminated-unions/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/undiscriminated-unions/src/Core/Types/ArrayType.php b/seed/php-sdk/undiscriminated-unions/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/undiscriminated-unions/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/undiscriminated-unions/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/undiscriminated-unions/src/Core/Types/Constant.php b/seed/php-sdk/undiscriminated-unions/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/undiscriminated-unions/src/Core/Types/Constant.php +++ b/seed/php-sdk/undiscriminated-unions/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/undiscriminated-unions/src/Core/Types/Union.php b/seed/php-sdk/undiscriminated-unions/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/undiscriminated-unions/src/Core/Types/Union.php +++ b/seed/php-sdk/undiscriminated-unions/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/undiscriminated-unions/src/Exceptions/SeedApiException.php b/seed/php-sdk/undiscriminated-unions/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/undiscriminated-unions/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/undiscriminated-unions/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/undiscriminated-unions/src/Exceptions/SeedException.php b/seed/php-sdk/undiscriminated-unions/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/undiscriminated-unions/src/Exceptions/SeedException.php +++ b/seed/php-sdk/undiscriminated-unions/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/undiscriminated-unions/src/SeedClient.php b/seed/php-sdk/undiscriminated-unions/src/SeedClient.php index 8ff0b3955c42..211ca1da5c2c 100644 --- a/seed/php-sdk/undiscriminated-unions/src/SeedClient.php +++ b/seed/php-sdk/undiscriminated-unions/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var UnionClient $union @@ -40,26 +40,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->union = new UnionClient($this->client, $this->options); } } diff --git a/seed/php-sdk/undiscriminated-unions/src/Union/Requests/PaymentRequest.php b/seed/php-sdk/undiscriminated-unions/src/Union/Requests/PaymentRequest.php index ba75f3747aca..054664b5a550 100644 --- a/seed/php-sdk/undiscriminated-unions/src/Union/Requests/PaymentRequest.php +++ b/seed/php-sdk/undiscriminated-unions/src/Union/Requests/PaymentRequest.php @@ -16,7 +16,7 @@ class PaymentRequest extends JsonSerializableType * |ConvertToken * ) $paymentMethod */ - #[JsonProperty('paymentMethod'), Union(TokenizeCard::class,ConvertToken::class)] + #[JsonProperty('paymentMethod'), Union(TokenizeCard::class, ConvertToken::class)] public TokenizeCard|ConvertToken $paymentMethod; /** @@ -29,8 +29,7 @@ class PaymentRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->paymentMethod = $values['paymentMethod']; } } diff --git a/seed/php-sdk/undiscriminated-unions/src/Union/Types/ConvertToken.php b/seed/php-sdk/undiscriminated-unions/src/Union/Types/ConvertToken.php index fc718d871f84..e01195467667 100644 --- a/seed/php-sdk/undiscriminated-unions/src/Union/Types/ConvertToken.php +++ b/seed/php-sdk/undiscriminated-unions/src/Union/Types/ConvertToken.php @@ -27,15 +27,16 @@ class ConvertToken extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->method = $values['method'];$this->tokenId = $values['tokenId']; + ) { + $this->method = $values['method']; + $this->tokenId = $values['tokenId']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/undiscriminated-unions/src/Union/Types/KeyType.php b/seed/php-sdk/undiscriminated-unions/src/Union/Types/KeyType.php index be532afcf64b..46c5402bc8f5 100644 --- a/seed/php-sdk/undiscriminated-unions/src/Union/Types/KeyType.php +++ b/seed/php-sdk/undiscriminated-unions/src/Union/Types/KeyType.php @@ -2,8 +2,8 @@ namespace Seed\Union\Types; -enum KeyType - : string { +enum KeyType: string +{ case Name = "name"; case Value = "value"; } diff --git a/seed/php-sdk/undiscriminated-unions/src/Union/Types/NamedMetadata.php b/seed/php-sdk/undiscriminated-unions/src/Union/Types/NamedMetadata.php index 586f4a9d5ee5..bf918ed275d3 100644 --- a/seed/php-sdk/undiscriminated-unions/src/Union/Types/NamedMetadata.php +++ b/seed/php-sdk/undiscriminated-unions/src/Union/Types/NamedMetadata.php @@ -28,15 +28,16 @@ class NamedMetadata extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->value = $values['value']; + ) { + $this->name = $values['name']; + $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/undiscriminated-unions/src/Union/Types/Request.php b/seed/php-sdk/undiscriminated-unions/src/Union/Types/Request.php index 865b98de6b10..774dfc8dcdec 100644 --- a/seed/php-sdk/undiscriminated-unions/src/Union/Types/Request.php +++ b/seed/php-sdk/undiscriminated-unions/src/Union/Types/Request.php @@ -15,7 +15,7 @@ class Request extends JsonSerializableType * |null * ) $union */ - #[JsonProperty('union'), Union(new Union(['string' => 'mixed'], 'null'),NamedMetadata::class,'null')] + #[JsonProperty('union'), Union(new Union(['string' => 'mixed'], 'null'), NamedMetadata::class, 'null')] public array|NamedMetadata|null $union; /** @@ -29,15 +29,15 @@ class Request extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->union = $values['union'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/undiscriminated-unions/src/Union/Types/TokenizeCard.php b/seed/php-sdk/undiscriminated-unions/src/Union/Types/TokenizeCard.php index f99aaf2a1889..d893209bf22b 100644 --- a/seed/php-sdk/undiscriminated-unions/src/Union/Types/TokenizeCard.php +++ b/seed/php-sdk/undiscriminated-unions/src/Union/Types/TokenizeCard.php @@ -27,15 +27,16 @@ class TokenizeCard extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->method = $values['method'];$this->cardNumber = $values['cardNumber']; + ) { + $this->method = $values['method']; + $this->cardNumber = $values['cardNumber']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/undiscriminated-unions/src/Union/Types/TypeWithOptionalUnion.php b/seed/php-sdk/undiscriminated-unions/src/Union/Types/TypeWithOptionalUnion.php index d449273da039..25ee798c4a95 100644 --- a/seed/php-sdk/undiscriminated-unions/src/Union/Types/TypeWithOptionalUnion.php +++ b/seed/php-sdk/undiscriminated-unions/src/Union/Types/TypeWithOptionalUnion.php @@ -17,7 +17,7 @@ class TypeWithOptionalUnion extends JsonSerializableType * |array> * )|null $myUnion */ - #[JsonProperty('myUnion'), Union('string',['string'],'integer',['integer'],[['integer']],'null')] + #[JsonProperty('myUnion'), Union('string', ['string'], 'integer', ['integer'], [['integer']], 'null')] public string|array|int|null $myUnion; /** @@ -33,15 +33,15 @@ class TypeWithOptionalUnion extends JsonSerializableType */ public function __construct( array $values = [], - ) - { + ) { $this->myUnion = $values['myUnion'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/undiscriminated-unions/src/Union/UnionClient.php b/seed/php-sdk/undiscriminated-unions/src/Union/UnionClient.php index 6a716ed53e27..4e1df2b433af 100644 --- a/seed/php-sdk/undiscriminated-unions/src/Union/UnionClient.php +++ b/seed/php-sdk/undiscriminated-unions/src/Union/UnionClient.php @@ -19,7 +19,7 @@ use Seed\Union\Types\Request; use Seed\Union\Requests\PaymentRequest; -class UnionClient +class UnionClient { /** * @var array{ @@ -47,11 +47,10 @@ class UnionClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -82,7 +81,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function get(string|array|int $request, ?array $options = null): string|array|int { + public function get(string|array|int $request, ?array $options = null): string|array|int + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -95,15 +95,15 @@ public function get(string|array|int $request, ?array $options = null): string|a $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeUnion($json, new Union('string', ['string'], 'integer', ['integer'], [['integer']])); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -137,7 +137,8 @@ public function get(string|array|int $request, ?array $options = null): string|a * @throws SeedException * @throws SeedApiException */ - public function getMetadata(?array $options = null): array { + public function getMetadata(?array $options = null): array + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -149,15 +150,15 @@ public function getMetadata(?array $options = null): array { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, ['string' => 'string']); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -193,7 +194,8 @@ public function getMetadata(?array $options = null): array { * @throws SeedException * @throws SeedApiException */ - public function updateMetadata(array|NamedMetadata|null $request, ?array $options = null): bool { + public function updateMetadata(array|NamedMetadata|null $request, ?array $options = null): bool + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -206,15 +208,15 @@ public function updateMetadata(array|NamedMetadata|null $request, ?array $option $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeBool($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -246,7 +248,8 @@ public function updateMetadata(array|NamedMetadata|null $request, ?array $option * @throws SeedException * @throws SeedApiException */ - public function call(Request $request, ?array $options = null): bool { + public function call(Request $request, ?array $options = null): bool + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -259,15 +262,15 @@ public function call(Request $request, ?array $options = null): bool { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeBool($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -307,7 +310,8 @@ public function call(Request $request, ?array $options = null): bool { * @throws SeedException * @throws SeedApiException */ - public function duplicateTypesUnion(string|array|int $request, ?array $options = null): string|array|int { + public function duplicateTypesUnion(string|array|int $request, ?array $options = null): string|array|int + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -320,15 +324,15 @@ public function duplicateTypesUnion(string|array|int $request, ?array $options = $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeUnion($json, new Union('string', ['string'], 'integer')); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -365,7 +369,8 @@ public function duplicateTypesUnion(string|array|int $request, ?array $options = * @throws SeedException * @throws SeedApiException */ - public function nestedUnions(string|array|int|bool $request, ?array $options = null): string { + public function nestedUnions(string|array|int|bool $request, ?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -378,15 +383,15 @@ public function nestedUnions(string|array|int|bool $request, ?array $options = n $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -418,7 +423,8 @@ public function nestedUnions(string|array|int|bool $request, ?array $options = n * @throws SeedException * @throws SeedApiException */ - public function testCamelCaseProperties(PaymentRequest $request, ?array $options = null): string { + public function testCamelCaseProperties(PaymentRequest $request, ?array $options = null): string + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -431,15 +437,15 @@ public function testCamelCaseProperties(PaymentRequest $request, ?array $options $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeString($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/undiscriminated-unions/src/Utils/File.php b/seed/php-sdk/undiscriminated-unions/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/undiscriminated-unions/src/Utils/File.php +++ b/seed/php-sdk/undiscriminated-unions/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/undiscriminated-unions/tests/Core/Client/RawClientTest.php b/seed/php-sdk/undiscriminated-unions/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/undiscriminated-unions/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/undiscriminated-unions/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/undiscriminated-unions/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/undiscriminated-unions/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/undiscriminated-unions/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/undiscriminated-unions/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/undiscriminated-unions/tests/Core/Json/EnumTest.php b/seed/php-sdk/undiscriminated-unions/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/undiscriminated-unions/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/undiscriminated-unions/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/undiscriminated-unions/tests/Core/Json/TraitTest.php b/seed/php-sdk/undiscriminated-unions/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/undiscriminated-unions/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/undiscriminated-unions/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/undiscriminated-unions/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/undiscriminated-unions/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/undiscriminated-unions/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/undiscriminated-unions/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/unions-with-local-date/src/Bigunion/BigunionClient.php b/seed/php-sdk/unions-with-local-date/src/Bigunion/BigunionClient.php index 25224149db51..fd1ab6f28ef3 100644 --- a/seed/php-sdk/unions-with-local-date/src/Bigunion/BigunionClient.php +++ b/seed/php-sdk/unions-with-local-date/src/Bigunion/BigunionClient.php @@ -15,7 +15,7 @@ use Seed\Core\Json\JsonDecoder; use Seed\Core\Json\JsonSerializer; -class BigunionClient +class BigunionClient { /** * @var array{ @@ -43,11 +43,10 @@ class BigunionClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -66,7 +65,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function get(string $id, ?array $options = null): BigUnion { + public function get(string $id, ?array $options = null): BigUnion + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -78,15 +78,15 @@ public function get(string $id, ?array $options = null): BigUnion { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return BigUnion::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -118,7 +118,8 @@ public function get(string $id, ?array $options = null): BigUnion { * @throws SeedException * @throws SeedApiException */ - public function update(BigUnion $request, ?array $options = null): bool { + public function update(BigUnion $request, ?array $options = null): bool + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -131,15 +132,15 @@ public function update(BigUnion $request, ?array $options = null): bool { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeBool($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -171,7 +172,8 @@ public function update(BigUnion $request, ?array $options = null): bool { * @throws SeedException * @throws SeedApiException */ - public function updateMany(array $request, ?array $options = null): array { + public function updateMany(array $request, ?array $options = null): array + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -184,15 +186,15 @@ public function updateMany(array $request, ?array $options = null): array { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, ['string' => 'bool']); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/ActiveDiamond.php b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/ActiveDiamond.php index 41e0adcf275e..d349a8f8c451 100644 --- a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/ActiveDiamond.php +++ b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/ActiveDiamond.php @@ -20,15 +20,15 @@ class ActiveDiamond extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/AttractiveScript.php b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/AttractiveScript.php index 56006b3f8135..e4cae475497a 100644 --- a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/AttractiveScript.php +++ b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/AttractiveScript.php @@ -20,15 +20,15 @@ class AttractiveScript extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/BigUnion.php b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/BigUnion.php index 0b2736d9def0..146fda428ac3 100644 --- a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/BigUnion.php +++ b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/BigUnion.php @@ -174,9 +174,12 @@ class BigUnion extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->id = $values['id'];$this->createdAt = $values['createdAt'];$this->archivedAt = $values['archivedAt'] ?? null;$this->type = $values['type'];$this->value = $values['value']; + ) { + $this->id = $values['id']; + $this->createdAt = $values['createdAt']; + $this->archivedAt = $values['archivedAt'] ?? null; + $this->type = $values['type']; + $this->value = $values['value']; } /** @@ -186,7 +189,8 @@ private function __construct( * @param NormalSweet $normalSweet * @return BigUnion */ - public static function normalSweet(string $id, DateTime $createdAt, NormalSweet $normalSweet, ?DateTime $archivedAt = null): BigUnion { + public static function normalSweet(string $id, DateTime $createdAt, NormalSweet $normalSweet, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -203,7 +207,8 @@ public static function normalSweet(string $id, DateTime $createdAt, NormalSweet * @param ThankfulFactor $thankfulFactor * @return BigUnion */ - public static function thankfulFactor(string $id, DateTime $createdAt, ThankfulFactor $thankfulFactor, ?DateTime $archivedAt = null): BigUnion { + public static function thankfulFactor(string $id, DateTime $createdAt, ThankfulFactor $thankfulFactor, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -220,7 +225,8 @@ public static function thankfulFactor(string $id, DateTime $createdAt, ThankfulF * @param JumboEnd $jumboEnd * @return BigUnion */ - public static function jumboEnd(string $id, DateTime $createdAt, JumboEnd $jumboEnd, ?DateTime $archivedAt = null): BigUnion { + public static function jumboEnd(string $id, DateTime $createdAt, JumboEnd $jumboEnd, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -237,7 +243,8 @@ public static function jumboEnd(string $id, DateTime $createdAt, JumboEnd $jumbo * @param HastyPain $hastyPain * @return BigUnion */ - public static function hastyPain(string $id, DateTime $createdAt, HastyPain $hastyPain, ?DateTime $archivedAt = null): BigUnion { + public static function hastyPain(string $id, DateTime $createdAt, HastyPain $hastyPain, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -254,7 +261,8 @@ public static function hastyPain(string $id, DateTime $createdAt, HastyPain $has * @param MistySnow $mistySnow * @return BigUnion */ - public static function mistySnow(string $id, DateTime $createdAt, MistySnow $mistySnow, ?DateTime $archivedAt = null): BigUnion { + public static function mistySnow(string $id, DateTime $createdAt, MistySnow $mistySnow, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -271,7 +279,8 @@ public static function mistySnow(string $id, DateTime $createdAt, MistySnow $mis * @param DistinctFailure $distinctFailure * @return BigUnion */ - public static function distinctFailure(string $id, DateTime $createdAt, DistinctFailure $distinctFailure, ?DateTime $archivedAt = null): BigUnion { + public static function distinctFailure(string $id, DateTime $createdAt, DistinctFailure $distinctFailure, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -288,7 +297,8 @@ public static function distinctFailure(string $id, DateTime $createdAt, Distinct * @param PracticalPrinciple $practicalPrinciple * @return BigUnion */ - public static function practicalPrinciple(string $id, DateTime $createdAt, PracticalPrinciple $practicalPrinciple, ?DateTime $archivedAt = null): BigUnion { + public static function practicalPrinciple(string $id, DateTime $createdAt, PracticalPrinciple $practicalPrinciple, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -305,7 +315,8 @@ public static function practicalPrinciple(string $id, DateTime $createdAt, Pract * @param LimpingStep $limpingStep * @return BigUnion */ - public static function limpingStep(string $id, DateTime $createdAt, LimpingStep $limpingStep, ?DateTime $archivedAt = null): BigUnion { + public static function limpingStep(string $id, DateTime $createdAt, LimpingStep $limpingStep, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -322,7 +333,8 @@ public static function limpingStep(string $id, DateTime $createdAt, LimpingStep * @param VibrantExcitement $vibrantExcitement * @return BigUnion */ - public static function vibrantExcitement(string $id, DateTime $createdAt, VibrantExcitement $vibrantExcitement, ?DateTime $archivedAt = null): BigUnion { + public static function vibrantExcitement(string $id, DateTime $createdAt, VibrantExcitement $vibrantExcitement, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -339,7 +351,8 @@ public static function vibrantExcitement(string $id, DateTime $createdAt, Vibran * @param ActiveDiamond $activeDiamond * @return BigUnion */ - public static function activeDiamond(string $id, DateTime $createdAt, ActiveDiamond $activeDiamond, ?DateTime $archivedAt = null): BigUnion { + public static function activeDiamond(string $id, DateTime $createdAt, ActiveDiamond $activeDiamond, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -356,7 +369,8 @@ public static function activeDiamond(string $id, DateTime $createdAt, ActiveDiam * @param PopularLimit $popularLimit * @return BigUnion */ - public static function popularLimit(string $id, DateTime $createdAt, PopularLimit $popularLimit, ?DateTime $archivedAt = null): BigUnion { + public static function popularLimit(string $id, DateTime $createdAt, PopularLimit $popularLimit, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -373,7 +387,8 @@ public static function popularLimit(string $id, DateTime $createdAt, PopularLimi * @param FalseMirror $falseMirror * @return BigUnion */ - public static function falseMirror(string $id, DateTime $createdAt, FalseMirror $falseMirror, ?DateTime $archivedAt = null): BigUnion { + public static function falseMirror(string $id, DateTime $createdAt, FalseMirror $falseMirror, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -390,7 +405,8 @@ public static function falseMirror(string $id, DateTime $createdAt, FalseMirror * @param PrimaryBlock $primaryBlock * @return BigUnion */ - public static function primaryBlock(string $id, DateTime $createdAt, PrimaryBlock $primaryBlock, ?DateTime $archivedAt = null): BigUnion { + public static function primaryBlock(string $id, DateTime $createdAt, PrimaryBlock $primaryBlock, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -407,7 +423,8 @@ public static function primaryBlock(string $id, DateTime $createdAt, PrimaryBloc * @param RotatingRatio $rotatingRatio * @return BigUnion */ - public static function rotatingRatio(string $id, DateTime $createdAt, RotatingRatio $rotatingRatio, ?DateTime $archivedAt = null): BigUnion { + public static function rotatingRatio(string $id, DateTime $createdAt, RotatingRatio $rotatingRatio, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -424,7 +441,8 @@ public static function rotatingRatio(string $id, DateTime $createdAt, RotatingRa * @param ColorfulCover $colorfulCover * @return BigUnion */ - public static function colorfulCover(string $id, DateTime $createdAt, ColorfulCover $colorfulCover, ?DateTime $archivedAt = null): BigUnion { + public static function colorfulCover(string $id, DateTime $createdAt, ColorfulCover $colorfulCover, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -441,7 +459,8 @@ public static function colorfulCover(string $id, DateTime $createdAt, ColorfulCo * @param DisloyalValue $disloyalValue * @return BigUnion */ - public static function disloyalValue(string $id, DateTime $createdAt, DisloyalValue $disloyalValue, ?DateTime $archivedAt = null): BigUnion { + public static function disloyalValue(string $id, DateTime $createdAt, DisloyalValue $disloyalValue, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -458,7 +477,8 @@ public static function disloyalValue(string $id, DateTime $createdAt, DisloyalVa * @param GruesomeCoach $gruesomeCoach * @return BigUnion */ - public static function gruesomeCoach(string $id, DateTime $createdAt, GruesomeCoach $gruesomeCoach, ?DateTime $archivedAt = null): BigUnion { + public static function gruesomeCoach(string $id, DateTime $createdAt, GruesomeCoach $gruesomeCoach, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -475,7 +495,8 @@ public static function gruesomeCoach(string $id, DateTime $createdAt, GruesomeCo * @param TotalWork $totalWork * @return BigUnion */ - public static function totalWork(string $id, DateTime $createdAt, TotalWork $totalWork, ?DateTime $archivedAt = null): BigUnion { + public static function totalWork(string $id, DateTime $createdAt, TotalWork $totalWork, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -492,7 +513,8 @@ public static function totalWork(string $id, DateTime $createdAt, TotalWork $tot * @param HarmoniousPlay $harmoniousPlay * @return BigUnion */ - public static function harmoniousPlay(string $id, DateTime $createdAt, HarmoniousPlay $harmoniousPlay, ?DateTime $archivedAt = null): BigUnion { + public static function harmoniousPlay(string $id, DateTime $createdAt, HarmoniousPlay $harmoniousPlay, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -509,7 +531,8 @@ public static function harmoniousPlay(string $id, DateTime $createdAt, Harmoniou * @param UniqueStress $uniqueStress * @return BigUnion */ - public static function uniqueStress(string $id, DateTime $createdAt, UniqueStress $uniqueStress, ?DateTime $archivedAt = null): BigUnion { + public static function uniqueStress(string $id, DateTime $createdAt, UniqueStress $uniqueStress, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -526,7 +549,8 @@ public static function uniqueStress(string $id, DateTime $createdAt, UniqueStres * @param UnwillingSmoke $unwillingSmoke * @return BigUnion */ - public static function unwillingSmoke(string $id, DateTime $createdAt, UnwillingSmoke $unwillingSmoke, ?DateTime $archivedAt = null): BigUnion { + public static function unwillingSmoke(string $id, DateTime $createdAt, UnwillingSmoke $unwillingSmoke, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -543,7 +567,8 @@ public static function unwillingSmoke(string $id, DateTime $createdAt, Unwilling * @param FrozenSleep $frozenSleep * @return BigUnion */ - public static function frozenSleep(string $id, DateTime $createdAt, FrozenSleep $frozenSleep, ?DateTime $archivedAt = null): BigUnion { + public static function frozenSleep(string $id, DateTime $createdAt, FrozenSleep $frozenSleep, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -560,7 +585,8 @@ public static function frozenSleep(string $id, DateTime $createdAt, FrozenSleep * @param DiligentDeal $diligentDeal * @return BigUnion */ - public static function diligentDeal(string $id, DateTime $createdAt, DiligentDeal $diligentDeal, ?DateTime $archivedAt = null): BigUnion { + public static function diligentDeal(string $id, DateTime $createdAt, DiligentDeal $diligentDeal, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -577,7 +603,8 @@ public static function diligentDeal(string $id, DateTime $createdAt, DiligentDea * @param AttractiveScript $attractiveScript * @return BigUnion */ - public static function attractiveScript(string $id, DateTime $createdAt, AttractiveScript $attractiveScript, ?DateTime $archivedAt = null): BigUnion { + public static function attractiveScript(string $id, DateTime $createdAt, AttractiveScript $attractiveScript, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -594,7 +621,8 @@ public static function attractiveScript(string $id, DateTime $createdAt, Attract * @param HoarseMouse $hoarseMouse * @return BigUnion */ - public static function hoarseMouse(string $id, DateTime $createdAt, HoarseMouse $hoarseMouse, ?DateTime $archivedAt = null): BigUnion { + public static function hoarseMouse(string $id, DateTime $createdAt, HoarseMouse $hoarseMouse, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -611,7 +639,8 @@ public static function hoarseMouse(string $id, DateTime $createdAt, HoarseMouse * @param CircularCard $circularCard * @return BigUnion */ - public static function circularCard(string $id, DateTime $createdAt, CircularCard $circularCard, ?DateTime $archivedAt = null): BigUnion { + public static function circularCard(string $id, DateTime $createdAt, CircularCard $circularCard, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -628,7 +657,8 @@ public static function circularCard(string $id, DateTime $createdAt, CircularCar * @param PotableBad $potableBad * @return BigUnion */ - public static function potableBad(string $id, DateTime $createdAt, PotableBad $potableBad, ?DateTime $archivedAt = null): BigUnion { + public static function potableBad(string $id, DateTime $createdAt, PotableBad $potableBad, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -645,7 +675,8 @@ public static function potableBad(string $id, DateTime $createdAt, PotableBad $p * @param TriangularRepair $triangularRepair * @return BigUnion */ - public static function triangularRepair(string $id, DateTime $createdAt, TriangularRepair $triangularRepair, ?DateTime $archivedAt = null): BigUnion { + public static function triangularRepair(string $id, DateTime $createdAt, TriangularRepair $triangularRepair, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -662,7 +693,8 @@ public static function triangularRepair(string $id, DateTime $createdAt, Triangu * @param GaseousRoad $gaseousRoad * @return BigUnion */ - public static function gaseousRoad(string $id, DateTime $createdAt, GaseousRoad $gaseousRoad, ?DateTime $archivedAt = null): BigUnion { + public static function gaseousRoad(string $id, DateTime $createdAt, GaseousRoad $gaseousRoad, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -675,601 +707,661 @@ public static function gaseousRoad(string $id, DateTime $createdAt, GaseousRoad /** * @return bool */ - public function isNormalSweet(): bool { - return $this->value instanceof NormalSweet&& $this->type === 'normalSweet'; + public function isNormalSweet(): bool + { + return $this->value instanceof NormalSweet && $this->type === 'normalSweet'; } /** * @return NormalSweet */ - public function asNormalSweet(): NormalSweet { - if (!($this->value instanceof NormalSweet&& $this->type === 'normalSweet')){ + public function asNormalSweet(): NormalSweet + { + if (!($this->value instanceof NormalSweet && $this->type === 'normalSweet')) { throw new Exception( "Expected normalSweet; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isThankfulFactor(): bool { - return $this->value instanceof ThankfulFactor&& $this->type === 'thankfulFactor'; + public function isThankfulFactor(): bool + { + return $this->value instanceof ThankfulFactor && $this->type === 'thankfulFactor'; } /** * @return ThankfulFactor */ - public function asThankfulFactor(): ThankfulFactor { - if (!($this->value instanceof ThankfulFactor&& $this->type === 'thankfulFactor')){ + public function asThankfulFactor(): ThankfulFactor + { + if (!($this->value instanceof ThankfulFactor && $this->type === 'thankfulFactor')) { throw new Exception( "Expected thankfulFactor; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isJumboEnd(): bool { - return $this->value instanceof JumboEnd&& $this->type === 'jumboEnd'; + public function isJumboEnd(): bool + { + return $this->value instanceof JumboEnd && $this->type === 'jumboEnd'; } /** * @return JumboEnd */ - public function asJumboEnd(): JumboEnd { - if (!($this->value instanceof JumboEnd&& $this->type === 'jumboEnd')){ + public function asJumboEnd(): JumboEnd + { + if (!($this->value instanceof JumboEnd && $this->type === 'jumboEnd')) { throw new Exception( "Expected jumboEnd; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isHastyPain(): bool { - return $this->value instanceof HastyPain&& $this->type === 'hastyPain'; + public function isHastyPain(): bool + { + return $this->value instanceof HastyPain && $this->type === 'hastyPain'; } /** * @return HastyPain */ - public function asHastyPain(): HastyPain { - if (!($this->value instanceof HastyPain&& $this->type === 'hastyPain')){ + public function asHastyPain(): HastyPain + { + if (!($this->value instanceof HastyPain && $this->type === 'hastyPain')) { throw new Exception( "Expected hastyPain; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isMistySnow(): bool { - return $this->value instanceof MistySnow&& $this->type === 'mistySnow'; + public function isMistySnow(): bool + { + return $this->value instanceof MistySnow && $this->type === 'mistySnow'; } /** * @return MistySnow */ - public function asMistySnow(): MistySnow { - if (!($this->value instanceof MistySnow&& $this->type === 'mistySnow')){ + public function asMistySnow(): MistySnow + { + if (!($this->value instanceof MistySnow && $this->type === 'mistySnow')) { throw new Exception( "Expected mistySnow; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isDistinctFailure(): bool { - return $this->value instanceof DistinctFailure&& $this->type === 'distinctFailure'; + public function isDistinctFailure(): bool + { + return $this->value instanceof DistinctFailure && $this->type === 'distinctFailure'; } /** * @return DistinctFailure */ - public function asDistinctFailure(): DistinctFailure { - if (!($this->value instanceof DistinctFailure&& $this->type === 'distinctFailure')){ + public function asDistinctFailure(): DistinctFailure + { + if (!($this->value instanceof DistinctFailure && $this->type === 'distinctFailure')) { throw new Exception( "Expected distinctFailure; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isPracticalPrinciple(): bool { - return $this->value instanceof PracticalPrinciple&& $this->type === 'practicalPrinciple'; + public function isPracticalPrinciple(): bool + { + return $this->value instanceof PracticalPrinciple && $this->type === 'practicalPrinciple'; } /** * @return PracticalPrinciple */ - public function asPracticalPrinciple(): PracticalPrinciple { - if (!($this->value instanceof PracticalPrinciple&& $this->type === 'practicalPrinciple')){ + public function asPracticalPrinciple(): PracticalPrinciple + { + if (!($this->value instanceof PracticalPrinciple && $this->type === 'practicalPrinciple')) { throw new Exception( "Expected practicalPrinciple; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isLimpingStep(): bool { - return $this->value instanceof LimpingStep&& $this->type === 'limpingStep'; + public function isLimpingStep(): bool + { + return $this->value instanceof LimpingStep && $this->type === 'limpingStep'; } /** * @return LimpingStep */ - public function asLimpingStep(): LimpingStep { - if (!($this->value instanceof LimpingStep&& $this->type === 'limpingStep')){ + public function asLimpingStep(): LimpingStep + { + if (!($this->value instanceof LimpingStep && $this->type === 'limpingStep')) { throw new Exception( "Expected limpingStep; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isVibrantExcitement(): bool { - return $this->value instanceof VibrantExcitement&& $this->type === 'vibrantExcitement'; + public function isVibrantExcitement(): bool + { + return $this->value instanceof VibrantExcitement && $this->type === 'vibrantExcitement'; } /** * @return VibrantExcitement */ - public function asVibrantExcitement(): VibrantExcitement { - if (!($this->value instanceof VibrantExcitement&& $this->type === 'vibrantExcitement')){ + public function asVibrantExcitement(): VibrantExcitement + { + if (!($this->value instanceof VibrantExcitement && $this->type === 'vibrantExcitement')) { throw new Exception( "Expected vibrantExcitement; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isActiveDiamond(): bool { - return $this->value instanceof ActiveDiamond&& $this->type === 'activeDiamond'; + public function isActiveDiamond(): bool + { + return $this->value instanceof ActiveDiamond && $this->type === 'activeDiamond'; } /** * @return ActiveDiamond */ - public function asActiveDiamond(): ActiveDiamond { - if (!($this->value instanceof ActiveDiamond&& $this->type === 'activeDiamond')){ + public function asActiveDiamond(): ActiveDiamond + { + if (!($this->value instanceof ActiveDiamond && $this->type === 'activeDiamond')) { throw new Exception( "Expected activeDiamond; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isPopularLimit(): bool { - return $this->value instanceof PopularLimit&& $this->type === 'popularLimit'; + public function isPopularLimit(): bool + { + return $this->value instanceof PopularLimit && $this->type === 'popularLimit'; } /** * @return PopularLimit */ - public function asPopularLimit(): PopularLimit { - if (!($this->value instanceof PopularLimit&& $this->type === 'popularLimit')){ + public function asPopularLimit(): PopularLimit + { + if (!($this->value instanceof PopularLimit && $this->type === 'popularLimit')) { throw new Exception( "Expected popularLimit; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isFalseMirror(): bool { - return $this->value instanceof FalseMirror&& $this->type === 'falseMirror'; + public function isFalseMirror(): bool + { + return $this->value instanceof FalseMirror && $this->type === 'falseMirror'; } /** * @return FalseMirror */ - public function asFalseMirror(): FalseMirror { - if (!($this->value instanceof FalseMirror&& $this->type === 'falseMirror')){ + public function asFalseMirror(): FalseMirror + { + if (!($this->value instanceof FalseMirror && $this->type === 'falseMirror')) { throw new Exception( "Expected falseMirror; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isPrimaryBlock(): bool { - return $this->value instanceof PrimaryBlock&& $this->type === 'primaryBlock'; + public function isPrimaryBlock(): bool + { + return $this->value instanceof PrimaryBlock && $this->type === 'primaryBlock'; } /** * @return PrimaryBlock */ - public function asPrimaryBlock(): PrimaryBlock { - if (!($this->value instanceof PrimaryBlock&& $this->type === 'primaryBlock')){ + public function asPrimaryBlock(): PrimaryBlock + { + if (!($this->value instanceof PrimaryBlock && $this->type === 'primaryBlock')) { throw new Exception( "Expected primaryBlock; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isRotatingRatio(): bool { - return $this->value instanceof RotatingRatio&& $this->type === 'rotatingRatio'; + public function isRotatingRatio(): bool + { + return $this->value instanceof RotatingRatio && $this->type === 'rotatingRatio'; } /** * @return RotatingRatio */ - public function asRotatingRatio(): RotatingRatio { - if (!($this->value instanceof RotatingRatio&& $this->type === 'rotatingRatio')){ + public function asRotatingRatio(): RotatingRatio + { + if (!($this->value instanceof RotatingRatio && $this->type === 'rotatingRatio')) { throw new Exception( "Expected rotatingRatio; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isColorfulCover(): bool { - return $this->value instanceof ColorfulCover&& $this->type === 'colorfulCover'; + public function isColorfulCover(): bool + { + return $this->value instanceof ColorfulCover && $this->type === 'colorfulCover'; } /** * @return ColorfulCover */ - public function asColorfulCover(): ColorfulCover { - if (!($this->value instanceof ColorfulCover&& $this->type === 'colorfulCover')){ + public function asColorfulCover(): ColorfulCover + { + if (!($this->value instanceof ColorfulCover && $this->type === 'colorfulCover')) { throw new Exception( "Expected colorfulCover; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isDisloyalValue(): bool { - return $this->value instanceof DisloyalValue&& $this->type === 'disloyalValue'; + public function isDisloyalValue(): bool + { + return $this->value instanceof DisloyalValue && $this->type === 'disloyalValue'; } /** * @return DisloyalValue */ - public function asDisloyalValue(): DisloyalValue { - if (!($this->value instanceof DisloyalValue&& $this->type === 'disloyalValue')){ + public function asDisloyalValue(): DisloyalValue + { + if (!($this->value instanceof DisloyalValue && $this->type === 'disloyalValue')) { throw new Exception( "Expected disloyalValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isGruesomeCoach(): bool { - return $this->value instanceof GruesomeCoach&& $this->type === 'gruesomeCoach'; + public function isGruesomeCoach(): bool + { + return $this->value instanceof GruesomeCoach && $this->type === 'gruesomeCoach'; } /** * @return GruesomeCoach */ - public function asGruesomeCoach(): GruesomeCoach { - if (!($this->value instanceof GruesomeCoach&& $this->type === 'gruesomeCoach')){ + public function asGruesomeCoach(): GruesomeCoach + { + if (!($this->value instanceof GruesomeCoach && $this->type === 'gruesomeCoach')) { throw new Exception( "Expected gruesomeCoach; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isTotalWork(): bool { - return $this->value instanceof TotalWork&& $this->type === 'totalWork'; + public function isTotalWork(): bool + { + return $this->value instanceof TotalWork && $this->type === 'totalWork'; } /** * @return TotalWork */ - public function asTotalWork(): TotalWork { - if (!($this->value instanceof TotalWork&& $this->type === 'totalWork')){ + public function asTotalWork(): TotalWork + { + if (!($this->value instanceof TotalWork && $this->type === 'totalWork')) { throw new Exception( "Expected totalWork; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isHarmoniousPlay(): bool { - return $this->value instanceof HarmoniousPlay&& $this->type === 'harmoniousPlay'; + public function isHarmoniousPlay(): bool + { + return $this->value instanceof HarmoniousPlay && $this->type === 'harmoniousPlay'; } /** * @return HarmoniousPlay */ - public function asHarmoniousPlay(): HarmoniousPlay { - if (!($this->value instanceof HarmoniousPlay&& $this->type === 'harmoniousPlay')){ + public function asHarmoniousPlay(): HarmoniousPlay + { + if (!($this->value instanceof HarmoniousPlay && $this->type === 'harmoniousPlay')) { throw new Exception( "Expected harmoniousPlay; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isUniqueStress(): bool { - return $this->value instanceof UniqueStress&& $this->type === 'uniqueStress'; + public function isUniqueStress(): bool + { + return $this->value instanceof UniqueStress && $this->type === 'uniqueStress'; } /** * @return UniqueStress */ - public function asUniqueStress(): UniqueStress { - if (!($this->value instanceof UniqueStress&& $this->type === 'uniqueStress')){ + public function asUniqueStress(): UniqueStress + { + if (!($this->value instanceof UniqueStress && $this->type === 'uniqueStress')) { throw new Exception( "Expected uniqueStress; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isUnwillingSmoke(): bool { - return $this->value instanceof UnwillingSmoke&& $this->type === 'unwillingSmoke'; + public function isUnwillingSmoke(): bool + { + return $this->value instanceof UnwillingSmoke && $this->type === 'unwillingSmoke'; } /** * @return UnwillingSmoke */ - public function asUnwillingSmoke(): UnwillingSmoke { - if (!($this->value instanceof UnwillingSmoke&& $this->type === 'unwillingSmoke')){ + public function asUnwillingSmoke(): UnwillingSmoke + { + if (!($this->value instanceof UnwillingSmoke && $this->type === 'unwillingSmoke')) { throw new Exception( "Expected unwillingSmoke; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isFrozenSleep(): bool { - return $this->value instanceof FrozenSleep&& $this->type === 'frozenSleep'; + public function isFrozenSleep(): bool + { + return $this->value instanceof FrozenSleep && $this->type === 'frozenSleep'; } /** * @return FrozenSleep */ - public function asFrozenSleep(): FrozenSleep { - if (!($this->value instanceof FrozenSleep&& $this->type === 'frozenSleep')){ + public function asFrozenSleep(): FrozenSleep + { + if (!($this->value instanceof FrozenSleep && $this->type === 'frozenSleep')) { throw new Exception( "Expected frozenSleep; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isDiligentDeal(): bool { - return $this->value instanceof DiligentDeal&& $this->type === 'diligentDeal'; + public function isDiligentDeal(): bool + { + return $this->value instanceof DiligentDeal && $this->type === 'diligentDeal'; } /** * @return DiligentDeal */ - public function asDiligentDeal(): DiligentDeal { - if (!($this->value instanceof DiligentDeal&& $this->type === 'diligentDeal')){ + public function asDiligentDeal(): DiligentDeal + { + if (!($this->value instanceof DiligentDeal && $this->type === 'diligentDeal')) { throw new Exception( "Expected diligentDeal; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isAttractiveScript(): bool { - return $this->value instanceof AttractiveScript&& $this->type === 'attractiveScript'; + public function isAttractiveScript(): bool + { + return $this->value instanceof AttractiveScript && $this->type === 'attractiveScript'; } /** * @return AttractiveScript */ - public function asAttractiveScript(): AttractiveScript { - if (!($this->value instanceof AttractiveScript&& $this->type === 'attractiveScript')){ + public function asAttractiveScript(): AttractiveScript + { + if (!($this->value instanceof AttractiveScript && $this->type === 'attractiveScript')) { throw new Exception( "Expected attractiveScript; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isHoarseMouse(): bool { - return $this->value instanceof HoarseMouse&& $this->type === 'hoarseMouse'; + public function isHoarseMouse(): bool + { + return $this->value instanceof HoarseMouse && $this->type === 'hoarseMouse'; } /** * @return HoarseMouse */ - public function asHoarseMouse(): HoarseMouse { - if (!($this->value instanceof HoarseMouse&& $this->type === 'hoarseMouse')){ + public function asHoarseMouse(): HoarseMouse + { + if (!($this->value instanceof HoarseMouse && $this->type === 'hoarseMouse')) { throw new Exception( "Expected hoarseMouse; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isCircularCard(): bool { - return $this->value instanceof CircularCard&& $this->type === 'circularCard'; + public function isCircularCard(): bool + { + return $this->value instanceof CircularCard && $this->type === 'circularCard'; } /** * @return CircularCard */ - public function asCircularCard(): CircularCard { - if (!($this->value instanceof CircularCard&& $this->type === 'circularCard')){ + public function asCircularCard(): CircularCard + { + if (!($this->value instanceof CircularCard && $this->type === 'circularCard')) { throw new Exception( "Expected circularCard; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isPotableBad(): bool { - return $this->value instanceof PotableBad&& $this->type === 'potableBad'; + public function isPotableBad(): bool + { + return $this->value instanceof PotableBad && $this->type === 'potableBad'; } /** * @return PotableBad */ - public function asPotableBad(): PotableBad { - if (!($this->value instanceof PotableBad&& $this->type === 'potableBad')){ + public function asPotableBad(): PotableBad + { + if (!($this->value instanceof PotableBad && $this->type === 'potableBad')) { throw new Exception( "Expected potableBad; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isTriangularRepair(): bool { - return $this->value instanceof TriangularRepair&& $this->type === 'triangularRepair'; + public function isTriangularRepair(): bool + { + return $this->value instanceof TriangularRepair && $this->type === 'triangularRepair'; } /** * @return TriangularRepair */ - public function asTriangularRepair(): TriangularRepair { - if (!($this->value instanceof TriangularRepair&& $this->type === 'triangularRepair')){ + public function asTriangularRepair(): TriangularRepair + { + if (!($this->value instanceof TriangularRepair && $this->type === 'triangularRepair')) { throw new Exception( "Expected triangularRepair; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isGaseousRoad(): bool { - return $this->value instanceof GaseousRoad&& $this->type === 'gaseousRoad'; + public function isGaseousRoad(): bool + { + return $this->value instanceof GaseousRoad && $this->type === 'gaseousRoad'; } /** * @return GaseousRoad */ - public function asGaseousRoad(): GaseousRoad { - if (!($this->value instanceof GaseousRoad&& $this->type === 'gaseousRoad')){ + public function asGaseousRoad(): GaseousRoad + { + if (!($this->value instanceof GaseousRoad && $this->type === 'gaseousRoad')) { throw new Exception( "Expected gaseousRoad; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'normalSweet': $value = $this->asNormalSweet()->jsonSerialize(); $result = array_merge($value, $result); @@ -1388,26 +1480,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -1416,58 +1509,59 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('id', $data)){ + if (!array_key_exists('id', $data)) { throw new Exception( "JSON data is missing property 'id'", ); } - if (!(is_string($data['id']))){ + if (!(is_string($data['id']))) { throw new Exception( "Expected property 'id' in JSON data to be string, instead received " . get_debug_type($data['id']), ); } $args['id'] = $data['id']; - - if (!array_key_exists('created-at', $data)){ + + if (!array_key_exists('created-at', $data)) { throw new Exception( "JSON data is missing property 'created-at'", ); } - if (!($data['created-at'] instanceof DateTime)){ + if (!($data['created-at'] instanceof DateTime)) { throw new Exception( "Expected property 'createdAt' in JSON data to be dateTime, instead received " . get_debug_type($data['created-at']), ); } $args['createdAt'] = $data['created-at']; - - if (!array_key_exists('archived-at', $data)){ + + if (!array_key_exists('archived-at', $data)) { throw new Exception( "JSON data is missing property 'archived-at'", ); } - if (!((is_null($data['archived-at']) || $data['archived-at'] instanceof DateTime))){ + if (!((is_null($data['archived-at']) || $data['archived-at'] instanceof DateTime))) { throw new Exception( "Expected property 'archivedAt' in JSON data to be optional, instead received " . get_debug_type($data['archived-at']), ); } $args['archivedAt'] = $data['archived-at']; - - if (!array_key_exists('type', $data)){ + + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'normalSweet': $args['value'] = NormalSweet::jsonDeserialize($data); break; @@ -1560,7 +1654,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/CircularCard.php b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/CircularCard.php index 853f60baa9f6..a6af8e90fd05 100644 --- a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/CircularCard.php +++ b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/CircularCard.php @@ -20,15 +20,15 @@ class CircularCard extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/ColorfulCover.php b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/ColorfulCover.php index bda8960708cf..9b5650f9eef2 100644 --- a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/ColorfulCover.php +++ b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/ColorfulCover.php @@ -20,15 +20,15 @@ class ColorfulCover extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/DiligentDeal.php b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/DiligentDeal.php index a7c1d3016c32..fff5b37687aa 100644 --- a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/DiligentDeal.php +++ b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/DiligentDeal.php @@ -20,15 +20,15 @@ class DiligentDeal extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/DisloyalValue.php b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/DisloyalValue.php index 4e58269addcb..59c4c61d30b0 100644 --- a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/DisloyalValue.php +++ b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/DisloyalValue.php @@ -20,15 +20,15 @@ class DisloyalValue extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/DistinctFailure.php b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/DistinctFailure.php index 1a02d97a0d1b..f1d57e1f0359 100644 --- a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/DistinctFailure.php +++ b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/DistinctFailure.php @@ -20,15 +20,15 @@ class DistinctFailure extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/FalseMirror.php b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/FalseMirror.php index e2ff8265f279..28d94257908f 100644 --- a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/FalseMirror.php +++ b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/FalseMirror.php @@ -20,15 +20,15 @@ class FalseMirror extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/FrozenSleep.php b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/FrozenSleep.php index 8d019157ecb9..a3f655d755c9 100644 --- a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/FrozenSleep.php +++ b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/FrozenSleep.php @@ -20,15 +20,15 @@ class FrozenSleep extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/GaseousRoad.php b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/GaseousRoad.php index 7375c21fc494..de36b4880d3a 100644 --- a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/GaseousRoad.php +++ b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/GaseousRoad.php @@ -20,15 +20,15 @@ class GaseousRoad extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/GruesomeCoach.php b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/GruesomeCoach.php index 98ccff1cccd3..0cb48e1b7297 100644 --- a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/GruesomeCoach.php +++ b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/GruesomeCoach.php @@ -20,15 +20,15 @@ class GruesomeCoach extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/HarmoniousPlay.php b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/HarmoniousPlay.php index c04b11977819..d731330c4796 100644 --- a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/HarmoniousPlay.php +++ b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/HarmoniousPlay.php @@ -20,15 +20,15 @@ class HarmoniousPlay extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/HastyPain.php b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/HastyPain.php index 549b05b5af6a..ffbfb817a3d7 100644 --- a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/HastyPain.php +++ b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/HastyPain.php @@ -20,15 +20,15 @@ class HastyPain extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/HoarseMouse.php b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/HoarseMouse.php index f9b2dbe09510..5aee1829c78f 100644 --- a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/HoarseMouse.php +++ b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/HoarseMouse.php @@ -20,15 +20,15 @@ class HoarseMouse extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/JumboEnd.php b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/JumboEnd.php index 5fe215acd18d..fdb2a7e68e2d 100644 --- a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/JumboEnd.php +++ b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/JumboEnd.php @@ -20,15 +20,15 @@ class JumboEnd extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/LimpingStep.php b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/LimpingStep.php index db44a16c0fde..e401a8a35207 100644 --- a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/LimpingStep.php +++ b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/LimpingStep.php @@ -20,15 +20,15 @@ class LimpingStep extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/MistySnow.php b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/MistySnow.php index b4e72fd8795b..c0a0b8decd7e 100644 --- a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/MistySnow.php +++ b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/MistySnow.php @@ -20,15 +20,15 @@ class MistySnow extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/NormalSweet.php b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/NormalSweet.php index 724c0f2a5e6e..f567738a7f68 100644 --- a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/NormalSweet.php +++ b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/NormalSweet.php @@ -20,15 +20,15 @@ class NormalSweet extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/PopularLimit.php b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/PopularLimit.php index 16c6576f1ca8..2341a8e29200 100644 --- a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/PopularLimit.php +++ b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/PopularLimit.php @@ -20,15 +20,15 @@ class PopularLimit extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/PotableBad.php b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/PotableBad.php index f7acbe29a027..e24feec637b2 100644 --- a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/PotableBad.php +++ b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/PotableBad.php @@ -20,15 +20,15 @@ class PotableBad extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/PracticalPrinciple.php b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/PracticalPrinciple.php index 951a702b6b47..de4e769d98af 100644 --- a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/PracticalPrinciple.php +++ b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/PracticalPrinciple.php @@ -20,15 +20,15 @@ class PracticalPrinciple extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/PrimaryBlock.php b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/PrimaryBlock.php index ac9f697eb970..cd65467325d9 100644 --- a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/PrimaryBlock.php +++ b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/PrimaryBlock.php @@ -20,15 +20,15 @@ class PrimaryBlock extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/RotatingRatio.php b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/RotatingRatio.php index 0e0dede2293e..03c00e20774f 100644 --- a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/RotatingRatio.php +++ b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/RotatingRatio.php @@ -20,15 +20,15 @@ class RotatingRatio extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/ThankfulFactor.php b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/ThankfulFactor.php index 7b5b49c20ece..9bd8db71f821 100644 --- a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/ThankfulFactor.php +++ b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/ThankfulFactor.php @@ -20,15 +20,15 @@ class ThankfulFactor extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/TotalWork.php b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/TotalWork.php index 317ca9b75628..3fec61db6c78 100644 --- a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/TotalWork.php +++ b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/TotalWork.php @@ -20,15 +20,15 @@ class TotalWork extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/TriangularRepair.php b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/TriangularRepair.php index 0b1577053134..b066e4f0d7c2 100644 --- a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/TriangularRepair.php +++ b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/TriangularRepair.php @@ -20,15 +20,15 @@ class TriangularRepair extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/UniqueStress.php b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/UniqueStress.php index f0c638cf16ba..756cd9ae6167 100644 --- a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/UniqueStress.php +++ b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/UniqueStress.php @@ -20,15 +20,15 @@ class UniqueStress extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/UnwillingSmoke.php b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/UnwillingSmoke.php index 68f0d65e1e21..7660031b0a8e 100644 --- a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/UnwillingSmoke.php +++ b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/UnwillingSmoke.php @@ -20,15 +20,15 @@ class UnwillingSmoke extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/VibrantExcitement.php b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/VibrantExcitement.php index e03ebb35a41a..0752343e8d6c 100644 --- a/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/VibrantExcitement.php +++ b/seed/php-sdk/unions-with-local-date/src/Bigunion/Types/VibrantExcitement.php @@ -20,15 +20,15 @@ class VibrantExcitement extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions-with-local-date/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/unions-with-local-date/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/unions-with-local-date/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/unions-with-local-date/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/unions-with-local-date/src/Core/Client/RawClient.php b/seed/php-sdk/unions-with-local-date/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/unions-with-local-date/src/Core/Client/RawClient.php +++ b/seed/php-sdk/unions-with-local-date/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/unions-with-local-date/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/unions-with-local-date/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/unions-with-local-date/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/unions-with-local-date/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/unions-with-local-date/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/unions-with-local-date/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/unions-with-local-date/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/unions-with-local-date/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/unions-with-local-date/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/unions-with-local-date/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/unions-with-local-date/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/unions-with-local-date/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/unions-with-local-date/src/Core/Types/ArrayType.php b/seed/php-sdk/unions-with-local-date/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/unions-with-local-date/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/unions-with-local-date/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/unions-with-local-date/src/Core/Types/Constant.php b/seed/php-sdk/unions-with-local-date/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/unions-with-local-date/src/Core/Types/Constant.php +++ b/seed/php-sdk/unions-with-local-date/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/unions-with-local-date/src/Core/Types/Union.php b/seed/php-sdk/unions-with-local-date/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/unions-with-local-date/src/Core/Types/Union.php +++ b/seed/php-sdk/unions-with-local-date/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/unions-with-local-date/src/Exceptions/SeedApiException.php b/seed/php-sdk/unions-with-local-date/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/unions-with-local-date/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/unions-with-local-date/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/unions-with-local-date/src/Exceptions/SeedException.php b/seed/php-sdk/unions-with-local-date/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/unions-with-local-date/src/Exceptions/SeedException.php +++ b/seed/php-sdk/unions-with-local-date/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/unions-with-local-date/src/SeedClient.php b/seed/php-sdk/unions-with-local-date/src/SeedClient.php index 74b56c29f98e..a6aa27fca2d5 100644 --- a/seed/php-sdk/unions-with-local-date/src/SeedClient.php +++ b/seed/php-sdk/unions-with-local-date/src/SeedClient.php @@ -8,7 +8,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var BigunionClient $bigunion @@ -52,26 +52,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->bigunion = new BigunionClient($this->client, $this->options); $this->types = new TypesClient($this->client, $this->options); $this->union = new UnionClient($this->client, $this->options); diff --git a/seed/php-sdk/unions-with-local-date/src/Types/Traits/Foo.php b/seed/php-sdk/unions-with-local-date/src/Types/Traits/Foo.php index 901f6d1cc180..7b400d71eacd 100644 --- a/seed/php-sdk/unions-with-local-date/src/Types/Traits/Foo.php +++ b/seed/php-sdk/unions-with-local-date/src/Types/Traits/Foo.php @@ -7,7 +7,7 @@ /** * @property string $name */ -trait Foo +trait Foo { /** * @var string $name diff --git a/seed/php-sdk/unions-with-local-date/src/Types/Types/Bar.php b/seed/php-sdk/unions-with-local-date/src/Types/Types/Bar.php index de9118561f94..67fb977f3a74 100644 --- a/seed/php-sdk/unions-with-local-date/src/Types/Types/Bar.php +++ b/seed/php-sdk/unions-with-local-date/src/Types/Types/Bar.php @@ -20,15 +20,15 @@ class Bar extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions-with-local-date/src/Types/Types/Foo.php b/seed/php-sdk/unions-with-local-date/src/Types/Types/Foo.php index 82aff30230a8..bc35f865d077 100644 --- a/seed/php-sdk/unions-with-local-date/src/Types/Types/Foo.php +++ b/seed/php-sdk/unions-with-local-date/src/Types/Types/Foo.php @@ -20,15 +20,15 @@ class Foo extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions-with-local-date/src/Types/Types/FooExtended.php b/seed/php-sdk/unions-with-local-date/src/Types/Types/FooExtended.php index 4b69b86e9858..870a1a1a72b0 100644 --- a/seed/php-sdk/unions-with-local-date/src/Types/Types/FooExtended.php +++ b/seed/php-sdk/unions-with-local-date/src/Types/Types/FooExtended.php @@ -24,15 +24,16 @@ class FooExtended extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->age = $values['age']; + ) { + $this->name = $values['name']; + $this->age = $values['age']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions-with-local-date/src/Types/Types/Union.php b/seed/php-sdk/unions-with-local-date/src/Types/Types/Union.php index 5e8a24ac1855..3e30b4dcf023 100644 --- a/seed/php-sdk/unions-with-local-date/src/Types/Types/Union.php +++ b/seed/php-sdk/unions-with-local-date/src/Types/Types/Union.php @@ -45,16 +45,17 @@ class Union extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param Foo $foo * @return Union */ - public static function foo(Foo $foo): Union { + public static function foo(Foo $foo): Union + { return new Union([ 'type' => 'foo', 'value' => $foo, @@ -65,7 +66,8 @@ public static function foo(Foo $foo): Union { * @param Bar $bar * @return Union */ - public static function bar(Bar $bar): Union { + public static function bar(Bar $bar): Union + { return new Union([ 'type' => 'bar', 'value' => $bar, @@ -75,61 +77,67 @@ public static function bar(Bar $bar): Union { /** * @return bool */ - public function isFoo(): bool { - return $this->value instanceof Foo&& $this->type === 'foo'; + public function isFoo(): bool + { + return $this->value instanceof Foo && $this->type === 'foo'; } /** * @return Foo */ - public function asFoo(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo')){ + public function asFoo(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo')) { throw new Exception( "Expected foo; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isBar(): bool { - return $this->value instanceof Bar&& $this->type === 'bar'; + public function isBar(): bool + { + return $this->value instanceof Bar && $this->type === 'bar'; } /** * @return Bar */ - public function asBar(): Bar { - if (!($this->value instanceof Bar&& $this->type === 'bar')){ + public function asBar(): Bar + { + if (!($this->value instanceof Bar && $this->type === 'bar')) { throw new Exception( "Expected bar; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'foo': $value = $this->asFoo()->jsonSerialize(); $result['foo'] = $value; @@ -140,26 +148,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -168,30 +177,31 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'foo': - if (!array_key_exists('foo', $data)){ + if (!array_key_exists('foo', $data)) { throw new Exception( "JSON data is missing property 'foo'", ); } - - if (!(is_array($data['foo']))){ + + if (!(is_array($data['foo']))) { throw new Exception( "Expected property 'foo' in JSON data to be array, instead received " . get_debug_type($data['foo']), ); @@ -199,13 +209,13 @@ public static function jsonDeserialize(array $data): static { $args['value'] = Foo::jsonDeserialize($data['foo']); break; case 'bar': - if (!array_key_exists('bar', $data)){ + if (!array_key_exists('bar', $data)) { throw new Exception( "JSON data is missing property 'bar'", ); } - - if (!(is_array($data['bar']))){ + + if (!(is_array($data['bar']))) { throw new Exception( "Expected property 'bar' in JSON data to be array, instead received " . get_debug_type($data['bar']), ); @@ -217,7 +227,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithBaseProperties.php b/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithBaseProperties.php index 921d1d8769dc..545f7d16d423 100644 --- a/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithBaseProperties.php +++ b/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithBaseProperties.php @@ -54,9 +54,10 @@ class UnionWithBaseProperties extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->id = $values['id'];$this->type = $values['type'];$this->value = $values['value']; + ) { + $this->id = $values['id']; + $this->type = $values['type']; + $this->value = $values['value']; } /** @@ -64,7 +65,8 @@ private function __construct( * @param int $integer * @return UnionWithBaseProperties */ - public static function integer(string $id, int $integer): UnionWithBaseProperties { + public static function integer(string $id, int $integer): UnionWithBaseProperties + { return new UnionWithBaseProperties([ 'id' => $id, 'type' => 'integer', @@ -77,7 +79,8 @@ public static function integer(string $id, int $integer): UnionWithBasePropertie * @param string $string * @return UnionWithBaseProperties */ - public static function string(string $id, string $string): UnionWithBaseProperties { + public static function string(string $id, string $string): UnionWithBaseProperties + { return new UnionWithBaseProperties([ 'id' => $id, 'type' => 'string', @@ -90,7 +93,8 @@ public static function string(string $id, string $string): UnionWithBaseProperti * @param Foo $foo * @return UnionWithBaseProperties */ - public static function foo(string $id, Foo $foo): UnionWithBaseProperties { + public static function foo(string $id, Foo $foo): UnionWithBaseProperties + { return new UnionWithBaseProperties([ 'id' => $id, 'type' => 'foo', @@ -101,81 +105,89 @@ public static function foo(string $id, Foo $foo): UnionWithBaseProperties { /** * @return bool */ - public function isInteger(): bool { - return is_int($this->value)&& $this->type === 'integer'; + public function isInteger(): bool + { + return is_int($this->value) && $this->type === 'integer'; } /** * @return int */ - public function asInteger(): int { - if (!(is_int($this->value)&& $this->type === 'integer')){ + public function asInteger(): int + { + if (!(is_int($this->value) && $this->type === 'integer')) { throw new Exception( "Expected integer; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isString(): bool { - return is_string($this->value)&& $this->type === 'string'; + public function isString(): bool + { + return is_string($this->value) && $this->type === 'string'; } /** * @return string */ - public function asString(): string { - if (!(is_string($this->value)&& $this->type === 'string')){ + public function asString(): string + { + if (!(is_string($this->value) && $this->type === 'string')) { throw new Exception( "Expected string; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isFoo(): bool { - return $this->value instanceof Foo&& $this->type === 'foo'; + public function isFoo(): bool + { + return $this->value instanceof Foo && $this->type === 'foo'; } /** * @return Foo */ - public function asFoo(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo')){ + public function asFoo(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo')) { throw new Exception( "Expected foo; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'integer': $value = $this->value; $result['integer'] = $value; @@ -190,26 +202,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -218,50 +231,51 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('id', $data)){ + if (!array_key_exists('id', $data)) { throw new Exception( "JSON data is missing property 'id'", ); } - if (!(is_string($data['id']))){ + if (!(is_string($data['id']))) { throw new Exception( "Expected property 'id' in JSON data to be string, instead received " . get_debug_type($data['id']), ); } $args['id'] = $data['id']; - - if (!array_key_exists('type', $data)){ + + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'integer': - if (!array_key_exists('integer', $data)){ + if (!array_key_exists('integer', $data)) { throw new Exception( "JSON data is missing property 'integer'", ); } - + $args['value'] = $data['integer']; break; case 'string': - if (!array_key_exists('string', $data)){ + if (!array_key_exists('string', $data)) { throw new Exception( "JSON data is missing property 'string'", ); } - + $args['value'] = $data['string']; break; case 'foo': @@ -272,7 +286,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithDiscriminant.php b/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithDiscriminant.php index 8a073f33121d..12a0ec0e6c63 100644 --- a/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithDiscriminant.php +++ b/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithDiscriminant.php @@ -42,16 +42,17 @@ class UnionWithDiscriminant extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param Foo $foo * @return UnionWithDiscriminant */ - public static function foo(Foo $foo): UnionWithDiscriminant { + public static function foo(Foo $foo): UnionWithDiscriminant + { return new UnionWithDiscriminant([ 'type' => 'foo', 'value' => $foo, @@ -62,7 +63,8 @@ public static function foo(Foo $foo): UnionWithDiscriminant { * @param Bar $bar * @return UnionWithDiscriminant */ - public static function bar(Bar $bar): UnionWithDiscriminant { + public static function bar(Bar $bar): UnionWithDiscriminant + { return new UnionWithDiscriminant([ 'type' => 'bar', 'value' => $bar, @@ -72,61 +74,67 @@ public static function bar(Bar $bar): UnionWithDiscriminant { /** * @return bool */ - public function isFoo(): bool { - return $this->value instanceof Foo&& $this->type === 'foo'; + public function isFoo(): bool + { + return $this->value instanceof Foo && $this->type === 'foo'; } /** * @return Foo */ - public function asFoo(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo')){ + public function asFoo(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo')) { throw new Exception( "Expected foo; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isBar(): bool { - return $this->value instanceof Bar&& $this->type === 'bar'; + public function isBar(): bool + { + return $this->value instanceof Bar && $this->type === 'bar'; } /** * @return Bar */ - public function asBar(): Bar { - if (!($this->value instanceof Bar&& $this->type === 'bar')){ + public function asBar(): Bar + { + if (!($this->value instanceof Bar && $this->type === 'bar')) { throw new Exception( "Expected bar; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['_type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'foo': $value = $this->asFoo()->jsonSerialize(); $result['foo'] = $value; @@ -137,26 +145,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -165,30 +174,31 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('_type', $data)){ + if (!array_key_exists('_type', $data)) { throw new Exception( "JSON data is missing property '_type'", ); } $type = $data['_type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['_type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'foo': - if (!array_key_exists('foo', $data)){ + if (!array_key_exists('foo', $data)) { throw new Exception( "JSON data is missing property 'foo'", ); } - - if (!(is_array($data['foo']))){ + + if (!(is_array($data['foo']))) { throw new Exception( "Expected property 'foo' in JSON data to be array, instead received " . get_debug_type($data['foo']), ); @@ -196,13 +206,13 @@ public static function jsonDeserialize(array $data): static { $args['value'] = Foo::jsonDeserialize($data['foo']); break; case 'bar': - if (!array_key_exists('bar', $data)){ + if (!array_key_exists('bar', $data)) { throw new Exception( "JSON data is missing property 'bar'", ); } - - if (!(is_array($data['bar']))){ + + if (!(is_array($data['bar']))) { throw new Exception( "Expected property 'bar' in JSON data to be array, instead received " . get_debug_type($data['bar']), ); @@ -214,7 +224,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithDuplicatePrimitive.php b/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithDuplicatePrimitive.php index 8b961b8a5f46..188842b28131 100644 --- a/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithDuplicatePrimitive.php +++ b/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithDuplicatePrimitive.php @@ -46,16 +46,17 @@ class UnionWithDuplicatePrimitive extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param int $integer1 * @return UnionWithDuplicatePrimitive */ - public static function integer1(int $integer1): UnionWithDuplicatePrimitive { + public static function integer1(int $integer1): UnionWithDuplicatePrimitive + { return new UnionWithDuplicatePrimitive([ 'type' => 'integer1', 'value' => $integer1, @@ -66,7 +67,8 @@ public static function integer1(int $integer1): UnionWithDuplicatePrimitive { * @param int $integer2 * @return UnionWithDuplicatePrimitive */ - public static function integer2(int $integer2): UnionWithDuplicatePrimitive { + public static function integer2(int $integer2): UnionWithDuplicatePrimitive + { return new UnionWithDuplicatePrimitive([ 'type' => 'integer2', 'value' => $integer2, @@ -77,7 +79,8 @@ public static function integer2(int $integer2): UnionWithDuplicatePrimitive { * @param string $string1 * @return UnionWithDuplicatePrimitive */ - public static function string1(string $string1): UnionWithDuplicatePrimitive { + public static function string1(string $string1): UnionWithDuplicatePrimitive + { return new UnionWithDuplicatePrimitive([ 'type' => 'string1', 'value' => $string1, @@ -88,7 +91,8 @@ public static function string1(string $string1): UnionWithDuplicatePrimitive { * @param string $string2 * @return UnionWithDuplicatePrimitive */ - public static function string2(string $string2): UnionWithDuplicatePrimitive { + public static function string2(string $string2): UnionWithDuplicatePrimitive + { return new UnionWithDuplicatePrimitive([ 'type' => 'string2', 'value' => $string2, @@ -98,101 +102,111 @@ public static function string2(string $string2): UnionWithDuplicatePrimitive { /** * @return bool */ - public function isInteger1(): bool { - return is_int($this->value)&& $this->type === 'integer1'; + public function isInteger1(): bool + { + return is_int($this->value) && $this->type === 'integer1'; } /** * @return int */ - public function asInteger1(): int { - if (!(is_int($this->value)&& $this->type === 'integer1')){ + public function asInteger1(): int + { + if (!(is_int($this->value) && $this->type === 'integer1')) { throw new Exception( "Expected integer1; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isInteger2(): bool { - return is_int($this->value)&& $this->type === 'integer2'; + public function isInteger2(): bool + { + return is_int($this->value) && $this->type === 'integer2'; } /** * @return int */ - public function asInteger2(): int { - if (!(is_int($this->value)&& $this->type === 'integer2')){ + public function asInteger2(): int + { + if (!(is_int($this->value) && $this->type === 'integer2')) { throw new Exception( "Expected integer2; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isString1(): bool { - return is_string($this->value)&& $this->type === 'string1'; + public function isString1(): bool + { + return is_string($this->value) && $this->type === 'string1'; } /** * @return string */ - public function asString1(): string { - if (!(is_string($this->value)&& $this->type === 'string1')){ + public function asString1(): string + { + if (!(is_string($this->value) && $this->type === 'string1')) { throw new Exception( "Expected string1; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isString2(): bool { - return is_string($this->value)&& $this->type === 'string2'; + public function isString2(): bool + { + return is_string($this->value) && $this->type === 'string2'; } /** * @return string */ - public function asString2(): string { - if (!(is_string($this->value)&& $this->type === 'string2')){ + public function asString2(): string + { + if (!(is_string($this->value) && $this->type === 'string2')) { throw new Exception( "Expected string2; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'integer1': $value = $this->value; $result['integer1'] = $value; @@ -211,26 +225,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -239,56 +254,57 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'integer1': - if (!array_key_exists('integer1', $data)){ + if (!array_key_exists('integer1', $data)) { throw new Exception( "JSON data is missing property 'integer1'", ); } - + $args['value'] = $data['integer1']; break; case 'integer2': - if (!array_key_exists('integer2', $data)){ + if (!array_key_exists('integer2', $data)) { throw new Exception( "JSON data is missing property 'integer2'", ); } - + $args['value'] = $data['integer2']; break; case 'string1': - if (!array_key_exists('string1', $data)){ + if (!array_key_exists('string1', $data)) { throw new Exception( "JSON data is missing property 'string1'", ); } - + $args['value'] = $data['string1']; break; case 'string2': - if (!array_key_exists('string2', $data)){ + if (!array_key_exists('string2', $data)) { throw new Exception( "JSON data is missing property 'string2'", ); } - + $args['value'] = $data['string2']; break; case '_unknown': @@ -296,7 +312,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithDuplicateTypes.php b/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithDuplicateTypes.php index 70b9faeeea5e..84dbbf00e1e5 100644 --- a/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithDuplicateTypes.php +++ b/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithDuplicateTypes.php @@ -40,16 +40,17 @@ class UnionWithDuplicateTypes extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param Foo $foo1 * @return UnionWithDuplicateTypes */ - public static function foo1(Foo $foo1): UnionWithDuplicateTypes { + public static function foo1(Foo $foo1): UnionWithDuplicateTypes + { return new UnionWithDuplicateTypes([ 'type' => 'foo1', 'value' => $foo1, @@ -60,7 +61,8 @@ public static function foo1(Foo $foo1): UnionWithDuplicateTypes { * @param Foo $foo2 * @return UnionWithDuplicateTypes */ - public static function foo2(Foo $foo2): UnionWithDuplicateTypes { + public static function foo2(Foo $foo2): UnionWithDuplicateTypes + { return new UnionWithDuplicateTypes([ 'type' => 'foo2', 'value' => $foo2, @@ -70,61 +72,67 @@ public static function foo2(Foo $foo2): UnionWithDuplicateTypes { /** * @return bool */ - public function isFoo1(): bool { - return $this->value instanceof Foo&& $this->type === 'foo1'; + public function isFoo1(): bool + { + return $this->value instanceof Foo && $this->type === 'foo1'; } /** * @return Foo */ - public function asFoo1(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo1')){ + public function asFoo1(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo1')) { throw new Exception( "Expected foo1; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isFoo2(): bool { - return $this->value instanceof Foo&& $this->type === 'foo2'; + public function isFoo2(): bool + { + return $this->value instanceof Foo && $this->type === 'foo2'; } /** * @return Foo */ - public function asFoo2(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo2')){ + public function asFoo2(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo2')) { throw new Exception( "Expected foo2; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'foo1': $value = $this->asFoo1()->jsonSerialize(); $result = array_merge($value, $result); @@ -135,26 +143,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -163,22 +172,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'foo1': $args['value'] = Foo::jsonDeserialize($data); break; @@ -190,7 +200,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithLiteral.php b/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithLiteral.php index 513941ab20e6..233c78542c0f 100644 --- a/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithLiteral.php +++ b/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithLiteral.php @@ -46,9 +46,10 @@ class UnionWithLiteral extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->base = $values['base'];$this->type = $values['type'];$this->value = $values['value']; + ) { + $this->base = $values['base']; + $this->type = $values['type']; + $this->value = $values['value']; } /** @@ -56,7 +57,8 @@ private function __construct( * @param 'fern' $fern * @return UnionWithLiteral */ - public static function fern(string $base, string $fern): UnionWithLiteral { + public static function fern(string $base, string $fern): UnionWithLiteral + { return new UnionWithLiteral([ 'base' => $base, 'type' => 'fern', @@ -67,67 +69,72 @@ public static function fern(string $base, string $fern): UnionWithLiteral { /** * @return bool */ - public function isFern(): bool { - return $this->value === 'fern'&& $this->type === 'fern'; + public function isFern(): bool + { + return $this->value === 'fern' && $this->type === 'fern'; } /** * @return 'fern' */ - public function asFern(): string { - if (!($this->value === 'fern'&& $this->type === 'fern')){ + public function asFern(): string + { + if (!($this->value === 'fern' && $this->type === 'fern')) { throw new Exception( "Expected fern; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'fern': $value = $this->value; $result['fern'] = $value; break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -136,41 +143,42 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('base', $data)){ + if (!array_key_exists('base', $data)) { throw new Exception( "JSON data is missing property 'base'", ); } - if (!($data['base'] === 'base')){ + if (!($data['base'] === 'base')) { throw new Exception( "Expected property 'base' in JSON data to be 'base', instead received " . get_debug_type($data['base']), ); } $args['base'] = $data['base']; - - if (!array_key_exists('type', $data)){ + + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'fern': - if (!array_key_exists('fern', $data)){ + if (!array_key_exists('fern', $data)) { throw new Exception( "JSON data is missing property 'fern'", ); } - + $args['value'] = $data['fern']; break; case '_unknown': @@ -178,7 +186,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithMultipleNoProperties.php b/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithMultipleNoProperties.php index 2436e0ccdf25..351b56a90bfa 100644 --- a/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithMultipleNoProperties.php +++ b/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithMultipleNoProperties.php @@ -44,16 +44,17 @@ class UnionWithMultipleNoProperties extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param Foo $foo * @return UnionWithMultipleNoProperties */ - public static function foo(Foo $foo): UnionWithMultipleNoProperties { + public static function foo(Foo $foo): UnionWithMultipleNoProperties + { return new UnionWithMultipleNoProperties([ 'type' => 'foo', 'value' => $foo, @@ -63,7 +64,8 @@ public static function foo(Foo $foo): UnionWithMultipleNoProperties { /** * @return UnionWithMultipleNoProperties */ - public static function empty1(): UnionWithMultipleNoProperties { + public static function empty1(): UnionWithMultipleNoProperties + { return new UnionWithMultipleNoProperties([ 'type' => 'empty1', 'value' => null, @@ -73,7 +75,8 @@ public static function empty1(): UnionWithMultipleNoProperties { /** * @return UnionWithMultipleNoProperties */ - public static function empty2(): UnionWithMultipleNoProperties { + public static function empty2(): UnionWithMultipleNoProperties + { return new UnionWithMultipleNoProperties([ 'type' => 'empty2', 'value' => null, @@ -83,55 +86,61 @@ public static function empty2(): UnionWithMultipleNoProperties { /** * @return bool */ - public function isFoo(): bool { - return $this->value instanceof Foo&& $this->type === 'foo'; + public function isFoo(): bool + { + return $this->value instanceof Foo && $this->type === 'foo'; } /** * @return Foo */ - public function asFoo(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo')){ + public function asFoo(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo')) { throw new Exception( "Expected foo; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isEmpty1(): bool { - return is_null($this->value)&& $this->type === 'empty1'; + public function isEmpty1(): bool + { + return is_null($this->value) && $this->type === 'empty1'; } /** * @return bool */ - public function isEmpty2(): bool { - return is_null($this->value)&& $this->type === 'empty2'; + public function isEmpty2(): bool + { + return is_null($this->value) && $this->type === 'empty2'; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'foo': $value = $this->asFoo()->jsonSerialize(); $result = array_merge($value, $result); @@ -144,26 +153,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -172,22 +182,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'foo': $args['value'] = Foo::jsonDeserialize($data); break; @@ -202,7 +213,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithNoProperties.php b/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithNoProperties.php index 86687f6b6919..025656a3dbbf 100644 --- a/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithNoProperties.php +++ b/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithNoProperties.php @@ -42,16 +42,17 @@ class UnionWithNoProperties extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param Foo $foo * @return UnionWithNoProperties */ - public static function foo(Foo $foo): UnionWithNoProperties { + public static function foo(Foo $foo): UnionWithNoProperties + { return new UnionWithNoProperties([ 'type' => 'foo', 'value' => $foo, @@ -61,7 +62,8 @@ public static function foo(Foo $foo): UnionWithNoProperties { /** * @return UnionWithNoProperties */ - public static function empty(): UnionWithNoProperties { + public static function empty(): UnionWithNoProperties + { return new UnionWithNoProperties([ 'type' => 'empty', 'value' => null, @@ -71,48 +73,53 @@ public static function empty(): UnionWithNoProperties { /** * @return bool */ - public function isFoo(): bool { - return $this->value instanceof Foo&& $this->type === 'foo'; + public function isFoo(): bool + { + return $this->value instanceof Foo && $this->type === 'foo'; } /** * @return Foo */ - public function asFoo(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo')){ + public function asFoo(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo')) { throw new Exception( "Expected foo; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isEmpty(): bool { - return is_null($this->value)&& $this->type === 'empty'; + public function isEmpty(): bool + { + return is_null($this->value) && $this->type === 'empty'; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'foo': $value = $this->asFoo()->jsonSerialize(); $result = array_merge($value, $result); @@ -122,26 +129,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -150,22 +158,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'foo': $args['value'] = Foo::jsonDeserialize($data); break; @@ -177,7 +186,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithOptionalTime.php b/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithOptionalTime.php index 0afba7944996..98db6869d002 100644 --- a/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithOptionalTime.php +++ b/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithOptionalTime.php @@ -44,16 +44,17 @@ class UnionWithOptionalTime extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param ?DateTime $date * @return UnionWithOptionalTime */ - public static function date(?DateTime $date = null): UnionWithOptionalTime { + public static function date(?DateTime $date = null): UnionWithOptionalTime + { return new UnionWithOptionalTime([ 'type' => 'date', 'value' => $date, @@ -64,7 +65,8 @@ public static function date(?DateTime $date = null): UnionWithOptionalTime { * @param ?DateTime $datetime * @return UnionWithOptionalTime */ - public static function datetime(?DateTime $datetime = null): UnionWithOptionalTime { + public static function datetime(?DateTime $datetime = null): UnionWithOptionalTime + { return new UnionWithOptionalTime([ 'type' => 'datetime', 'value' => $datetime, @@ -74,97 +76,104 @@ public static function datetime(?DateTime $datetime = null): UnionWithOptionalTi /** * @return bool */ - public function isDate(): bool { - return (is_null($this->value) || $this->value instanceof DateTime)&& $this->type === 'date'; + public function isDate(): bool + { + return (is_null($this->value) || $this->value instanceof DateTime) && $this->type === 'date'; } /** * @return ?DateTime */ - public function asDate(): ?DateTime { - if (!((is_null($this->value) || $this->value instanceof DateTime)&& $this->type === 'date')){ + public function asDate(): ?DateTime + { + if (!((is_null($this->value) || $this->value instanceof DateTime) && $this->type === 'date')) { throw new Exception( "Expected date; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isDatetime(): bool { - return (is_null($this->value) || $this->value instanceof DateTime)&& $this->type === 'datetime'; + public function isDatetime(): bool + { + return (is_null($this->value) || $this->value instanceof DateTime) && $this->type === 'datetime'; } /** * @return ?DateTime */ - public function asDatetime(): ?DateTime { - if (!((is_null($this->value) || $this->value instanceof DateTime)&& $this->type === 'datetime')){ + public function asDatetime(): ?DateTime + { + if (!((is_null($this->value) || $this->value instanceof DateTime) && $this->type === 'datetime')) { throw new Exception( "Expected datetime; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'date': $value = $this->asDate(); - if (!is_null($value)){ + if (!is_null($value)) { $value = JsonSerializer::serializeDate($value); } $result['date'] = $value; break; case 'datetime': $value = $this->asDatetime(); - if (!is_null($value)){ + if (!is_null($value)) { $value = JsonSerializer::serializeDateTime($value); } $result['datetime'] = $value; break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -173,38 +182,39 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'date': - if (!array_key_exists('date', $data)){ + if (!array_key_exists('date', $data)) { throw new Exception( "JSON data is missing property 'date'", ); } - + $args['value'] = $data['date']; break; case 'datetime': - if (!array_key_exists('datetime', $data)){ + if (!array_key_exists('datetime', $data)) { throw new Exception( "JSON data is missing property 'datetime'", ); } - + $args['value'] = $data['datetime']; break; case '_unknown': @@ -212,7 +222,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithPrimitive.php b/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithPrimitive.php index 9a6c3bd8a564..290460149239 100644 --- a/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithPrimitive.php +++ b/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithPrimitive.php @@ -42,16 +42,17 @@ class UnionWithPrimitive extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param int $integer * @return UnionWithPrimitive */ - public static function integer(int $integer): UnionWithPrimitive { + public static function integer(int $integer): UnionWithPrimitive + { return new UnionWithPrimitive([ 'type' => 'integer', 'value' => $integer, @@ -62,7 +63,8 @@ public static function integer(int $integer): UnionWithPrimitive { * @param string $string * @return UnionWithPrimitive */ - public static function string(string $string): UnionWithPrimitive { + public static function string(string $string): UnionWithPrimitive + { return new UnionWithPrimitive([ 'type' => 'string', 'value' => $string, @@ -72,61 +74,67 @@ public static function string(string $string): UnionWithPrimitive { /** * @return bool */ - public function isInteger(): bool { - return is_int($this->value)&& $this->type === 'integer'; + public function isInteger(): bool + { + return is_int($this->value) && $this->type === 'integer'; } /** * @return int */ - public function asInteger(): int { - if (!(is_int($this->value)&& $this->type === 'integer')){ + public function asInteger(): int + { + if (!(is_int($this->value) && $this->type === 'integer')) { throw new Exception( "Expected integer; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isString(): bool { - return is_string($this->value)&& $this->type === 'string'; + public function isString(): bool + { + return is_string($this->value) && $this->type === 'string'; } /** * @return string */ - public function asString(): string { - if (!(is_string($this->value)&& $this->type === 'string')){ + public function asString(): string + { + if (!(is_string($this->value) && $this->type === 'string')) { throw new Exception( "Expected string; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'integer': $value = $this->value; $result['integer'] = $value; @@ -137,26 +145,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -165,38 +174,39 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'integer': - if (!array_key_exists('integer', $data)){ + if (!array_key_exists('integer', $data)) { throw new Exception( "JSON data is missing property 'integer'", ); } - + $args['value'] = $data['integer']; break; case 'string': - if (!array_key_exists('string', $data)){ + if (!array_key_exists('string', $data)) { throw new Exception( "JSON data is missing property 'string'", ); } - + $args['value'] = $data['string']; break; case '_unknown': @@ -204,7 +214,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithSameNumberTypes.php b/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithSameNumberTypes.php index 59337d67d063..6deb07c36c12 100644 --- a/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithSameNumberTypes.php +++ b/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithSameNumberTypes.php @@ -44,16 +44,17 @@ class UnionWithSameNumberTypes extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param int $positiveInt * @return UnionWithSameNumberTypes */ - public static function positiveInt(int $positiveInt): UnionWithSameNumberTypes { + public static function positiveInt(int $positiveInt): UnionWithSameNumberTypes + { return new UnionWithSameNumberTypes([ 'type' => 'positiveInt', 'value' => $positiveInt, @@ -64,7 +65,8 @@ public static function positiveInt(int $positiveInt): UnionWithSameNumberTypes { * @param int $negativeInt * @return UnionWithSameNumberTypes */ - public static function negativeInt(int $negativeInt): UnionWithSameNumberTypes { + public static function negativeInt(int $negativeInt): UnionWithSameNumberTypes + { return new UnionWithSameNumberTypes([ 'type' => 'negativeInt', 'value' => $negativeInt, @@ -75,7 +77,8 @@ public static function negativeInt(int $negativeInt): UnionWithSameNumberTypes { * @param float $anyNumber * @return UnionWithSameNumberTypes */ - public static function anyNumber(float $anyNumber): UnionWithSameNumberTypes { + public static function anyNumber(float $anyNumber): UnionWithSameNumberTypes + { return new UnionWithSameNumberTypes([ 'type' => 'anyNumber', 'value' => $anyNumber, @@ -85,81 +88,89 @@ public static function anyNumber(float $anyNumber): UnionWithSameNumberTypes { /** * @return bool */ - public function isPositiveInt(): bool { - return is_int($this->value)&& $this->type === 'positiveInt'; + public function isPositiveInt(): bool + { + return is_int($this->value) && $this->type === 'positiveInt'; } /** * @return int */ - public function asPositiveInt(): int { - if (!(is_int($this->value)&& $this->type === 'positiveInt')){ + public function asPositiveInt(): int + { + if (!(is_int($this->value) && $this->type === 'positiveInt')) { throw new Exception( "Expected positiveInt; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isNegativeInt(): bool { - return is_int($this->value)&& $this->type === 'negativeInt'; + public function isNegativeInt(): bool + { + return is_int($this->value) && $this->type === 'negativeInt'; } /** * @return int */ - public function asNegativeInt(): int { - if (!(is_int($this->value)&& $this->type === 'negativeInt')){ + public function asNegativeInt(): int + { + if (!(is_int($this->value) && $this->type === 'negativeInt')) { throw new Exception( "Expected negativeInt; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isAnyNumber(): bool { - return is_float($this->value)&& $this->type === 'anyNumber'; + public function isAnyNumber(): bool + { + return is_float($this->value) && $this->type === 'anyNumber'; } /** * @return float */ - public function asAnyNumber(): float { - if (!(is_float($this->value)&& $this->type === 'anyNumber')){ + public function asAnyNumber(): float + { + if (!(is_float($this->value) && $this->type === 'anyNumber')) { throw new Exception( "Expected anyNumber; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'positiveInt': $value = $this->value; $result['positiveInt'] = $value; @@ -174,26 +185,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -202,47 +214,48 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'positiveInt': - if (!array_key_exists('positiveInt', $data)){ + if (!array_key_exists('positiveInt', $data)) { throw new Exception( "JSON data is missing property 'positiveInt'", ); } - + $args['value'] = $data['positiveInt']; break; case 'negativeInt': - if (!array_key_exists('negativeInt', $data)){ + if (!array_key_exists('negativeInt', $data)) { throw new Exception( "JSON data is missing property 'negativeInt'", ); } - + $args['value'] = $data['negativeInt']; break; case 'anyNumber': - if (!array_key_exists('anyNumber', $data)){ + if (!array_key_exists('anyNumber', $data)) { throw new Exception( "JSON data is missing property 'anyNumber'", ); } - + $args['value'] = $data['anyNumber']; break; case '_unknown': @@ -250,7 +263,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithSameStringTypes.php b/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithSameStringTypes.php index abd175a25929..268ca8d3c7e9 100644 --- a/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithSameStringTypes.php +++ b/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithSameStringTypes.php @@ -42,16 +42,17 @@ class UnionWithSameStringTypes extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param string $customFormat * @return UnionWithSameStringTypes */ - public static function customFormat(string $customFormat): UnionWithSameStringTypes { + public static function customFormat(string $customFormat): UnionWithSameStringTypes + { return new UnionWithSameStringTypes([ 'type' => 'customFormat', 'value' => $customFormat, @@ -62,7 +63,8 @@ public static function customFormat(string $customFormat): UnionWithSameStringTy * @param string $regularString * @return UnionWithSameStringTypes */ - public static function regularString(string $regularString): UnionWithSameStringTypes { + public static function regularString(string $regularString): UnionWithSameStringTypes + { return new UnionWithSameStringTypes([ 'type' => 'regularString', 'value' => $regularString, @@ -73,7 +75,8 @@ public static function regularString(string $regularString): UnionWithSameString * @param string $patternString * @return UnionWithSameStringTypes */ - public static function patternString(string $patternString): UnionWithSameStringTypes { + public static function patternString(string $patternString): UnionWithSameStringTypes + { return new UnionWithSameStringTypes([ 'type' => 'patternString', 'value' => $patternString, @@ -83,81 +86,89 @@ public static function patternString(string $patternString): UnionWithSameString /** * @return bool */ - public function isCustomFormat(): bool { - return is_string($this->value)&& $this->type === 'customFormat'; + public function isCustomFormat(): bool + { + return is_string($this->value) && $this->type === 'customFormat'; } /** * @return string */ - public function asCustomFormat(): string { - if (!(is_string($this->value)&& $this->type === 'customFormat')){ + public function asCustomFormat(): string + { + if (!(is_string($this->value) && $this->type === 'customFormat')) { throw new Exception( "Expected customFormat; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isRegularString(): bool { - return is_string($this->value)&& $this->type === 'regularString'; + public function isRegularString(): bool + { + return is_string($this->value) && $this->type === 'regularString'; } /** * @return string */ - public function asRegularString(): string { - if (!(is_string($this->value)&& $this->type === 'regularString')){ + public function asRegularString(): string + { + if (!(is_string($this->value) && $this->type === 'regularString')) { throw new Exception( "Expected regularString; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isPatternString(): bool { - return is_string($this->value)&& $this->type === 'patternString'; + public function isPatternString(): bool + { + return is_string($this->value) && $this->type === 'patternString'; } /** * @return string */ - public function asPatternString(): string { - if (!(is_string($this->value)&& $this->type === 'patternString')){ + public function asPatternString(): string + { + if (!(is_string($this->value) && $this->type === 'patternString')) { throw new Exception( "Expected patternString; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'customFormat': $value = $this->value; $result['customFormat'] = $value; @@ -172,26 +183,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -200,47 +212,48 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'customFormat': - if (!array_key_exists('customFormat', $data)){ + if (!array_key_exists('customFormat', $data)) { throw new Exception( "JSON data is missing property 'customFormat'", ); } - + $args['value'] = $data['customFormat']; break; case 'regularString': - if (!array_key_exists('regularString', $data)){ + if (!array_key_exists('regularString', $data)) { throw new Exception( "JSON data is missing property 'regularString'", ); } - + $args['value'] = $data['regularString']; break; case 'patternString': - if (!array_key_exists('patternString', $data)){ + if (!array_key_exists('patternString', $data)) { throw new Exception( "JSON data is missing property 'patternString'", ); } - + $args['value'] = $data['patternString']; break; case '_unknown': @@ -248,7 +261,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithSingleElement.php b/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithSingleElement.php index 086b28c04ba8..63cb0e427d92 100644 --- a/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithSingleElement.php +++ b/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithSingleElement.php @@ -38,16 +38,17 @@ class UnionWithSingleElement extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param Foo $foo * @return UnionWithSingleElement */ - public static function foo(Foo $foo): UnionWithSingleElement { + public static function foo(Foo $foo): UnionWithSingleElement + { return new UnionWithSingleElement([ 'type' => 'foo', 'value' => $foo, @@ -57,67 +58,72 @@ public static function foo(Foo $foo): UnionWithSingleElement { /** * @return bool */ - public function isFoo(): bool { - return $this->value instanceof Foo&& $this->type === 'foo'; + public function isFoo(): bool + { + return $this->value instanceof Foo && $this->type === 'foo'; } /** * @return Foo */ - public function asFoo(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo')){ + public function asFoo(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo')) { throw new Exception( "Expected foo; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'foo': $value = $this->asFoo()->jsonSerialize(); $result = array_merge($value, $result); break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -126,22 +132,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'foo': $args['value'] = Foo::jsonDeserialize($data); break; @@ -150,7 +157,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithSubTypes.php b/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithSubTypes.php index 09211c87b0db..f3b99d8e2bcb 100644 --- a/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithSubTypes.php +++ b/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithSubTypes.php @@ -42,16 +42,17 @@ class UnionWithSubTypes extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param Foo $foo * @return UnionWithSubTypes */ - public static function foo(Foo $foo): UnionWithSubTypes { + public static function foo(Foo $foo): UnionWithSubTypes + { return new UnionWithSubTypes([ 'type' => 'foo', 'value' => $foo, @@ -62,7 +63,8 @@ public static function foo(Foo $foo): UnionWithSubTypes { * @param FooExtended $fooExtended * @return UnionWithSubTypes */ - public static function fooExtended(FooExtended $fooExtended): UnionWithSubTypes { + public static function fooExtended(FooExtended $fooExtended): UnionWithSubTypes + { return new UnionWithSubTypes([ 'type' => 'fooExtended', 'value' => $fooExtended, @@ -72,61 +74,67 @@ public static function fooExtended(FooExtended $fooExtended): UnionWithSubTypes /** * @return bool */ - public function isFoo(): bool { - return $this->value instanceof Foo&& $this->type === 'foo'; + public function isFoo(): bool + { + return $this->value instanceof Foo && $this->type === 'foo'; } /** * @return Foo */ - public function asFoo(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo')){ + public function asFoo(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo')) { throw new Exception( "Expected foo; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isFooExtended(): bool { - return $this->value instanceof FooExtended&& $this->type === 'fooExtended'; + public function isFooExtended(): bool + { + return $this->value instanceof FooExtended && $this->type === 'fooExtended'; } /** * @return FooExtended */ - public function asFooExtended(): FooExtended { - if (!($this->value instanceof FooExtended&& $this->type === 'fooExtended')){ + public function asFooExtended(): FooExtended + { + if (!($this->value instanceof FooExtended && $this->type === 'fooExtended')) { throw new Exception( "Expected fooExtended; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'foo': $value = $this->asFoo()->jsonSerialize(); $result = array_merge($value, $result); @@ -137,26 +145,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -165,22 +174,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'foo': $args['value'] = Foo::jsonDeserialize($data); break; @@ -192,7 +202,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithTime.php b/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithTime.php index e91e58c6c3ee..7f6376e49553 100644 --- a/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithTime.php +++ b/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithTime.php @@ -46,16 +46,17 @@ class UnionWithTime extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param int $value * @return UnionWithTime */ - public static function value(int $value): UnionWithTime { + public static function value(int $value): UnionWithTime + { return new UnionWithTime([ 'type' => 'value', 'value' => $value, @@ -66,7 +67,8 @@ public static function value(int $value): UnionWithTime { * @param DateTime $date * @return UnionWithTime */ - public static function date(DateTime $date): UnionWithTime { + public static function date(DateTime $date): UnionWithTime + { return new UnionWithTime([ 'type' => 'date', 'value' => $date, @@ -77,7 +79,8 @@ public static function date(DateTime $date): UnionWithTime { * @param DateTime $datetime * @return UnionWithTime */ - public static function datetime(DateTime $datetime): UnionWithTime { + public static function datetime(DateTime $datetime): UnionWithTime + { return new UnionWithTime([ 'type' => 'datetime', 'value' => $datetime, @@ -87,81 +90,89 @@ public static function datetime(DateTime $datetime): UnionWithTime { /** * @return bool */ - public function isValue(): bool { - return is_int($this->value)&& $this->type === 'value'; + public function isValue(): bool + { + return is_int($this->value) && $this->type === 'value'; } /** * @return int */ - public function asValue(): int { - if (!(is_int($this->value)&& $this->type === 'value')){ + public function asValue(): int + { + if (!(is_int($this->value) && $this->type === 'value')) { throw new Exception( "Expected value; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isDate(): bool { - return $this->value instanceof DateTime&& $this->type === 'date'; + public function isDate(): bool + { + return $this->value instanceof DateTime && $this->type === 'date'; } /** * @return DateTime */ - public function asDate(): DateTime { - if (!($this->value instanceof DateTime&& $this->type === 'date')){ + public function asDate(): DateTime + { + if (!($this->value instanceof DateTime && $this->type === 'date')) { throw new Exception( "Expected date; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isDatetime(): bool { - return $this->value instanceof DateTime&& $this->type === 'datetime'; + public function isDatetime(): bool + { + return $this->value instanceof DateTime && $this->type === 'datetime'; } /** * @return DateTime */ - public function asDatetime(): DateTime { - if (!($this->value instanceof DateTime&& $this->type === 'datetime')){ + public function asDatetime(): DateTime + { + if (!($this->value instanceof DateTime && $this->type === 'datetime')) { throw new Exception( "Expected datetime; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'value': $value = $this->value; $result['value'] = $value; @@ -176,26 +187,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -204,47 +216,48 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'value': - if (!array_key_exists('value', $data)){ + if (!array_key_exists('value', $data)) { throw new Exception( "JSON data is missing property 'value'", ); } - + $args['value'] = $data['value']; break; case 'date': - if (!array_key_exists('date', $data)){ + if (!array_key_exists('date', $data)) { throw new Exception( "JSON data is missing property 'date'", ); } - + $args['value'] = $data['date']; break; case 'datetime': - if (!array_key_exists('datetime', $data)){ + if (!array_key_exists('datetime', $data)) { throw new Exception( "JSON data is missing property 'datetime'", ); } - + $args['value'] = $data['datetime']; break; case '_unknown': @@ -252,7 +265,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithoutKey.php b/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithoutKey.php index 1fb2cf1a5032..a91c9824095e 100644 --- a/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithoutKey.php +++ b/seed/php-sdk/unions-with-local-date/src/Types/Types/UnionWithoutKey.php @@ -42,16 +42,17 @@ class UnionWithoutKey extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param Foo $foo * @return UnionWithoutKey */ - public static function foo(Foo $foo): UnionWithoutKey { + public static function foo(Foo $foo): UnionWithoutKey + { return new UnionWithoutKey([ 'type' => 'foo', 'value' => $foo, @@ -62,7 +63,8 @@ public static function foo(Foo $foo): UnionWithoutKey { * @param Bar $bar * @return UnionWithoutKey */ - public static function bar(Bar $bar): UnionWithoutKey { + public static function bar(Bar $bar): UnionWithoutKey + { return new UnionWithoutKey([ 'type' => 'bar', 'value' => $bar, @@ -72,61 +74,67 @@ public static function bar(Bar $bar): UnionWithoutKey { /** * @return bool */ - public function isFoo(): bool { - return $this->value instanceof Foo&& $this->type === 'foo'; + public function isFoo(): bool + { + return $this->value instanceof Foo && $this->type === 'foo'; } /** * @return Foo */ - public function asFoo(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo')){ + public function asFoo(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo')) { throw new Exception( "Expected foo; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isBar(): bool { - return $this->value instanceof Bar&& $this->type === 'bar'; + public function isBar(): bool + { + return $this->value instanceof Bar && $this->type === 'bar'; } /** * @return Bar */ - public function asBar(): Bar { - if (!($this->value instanceof Bar&& $this->type === 'bar')){ + public function asBar(): Bar + { + if (!($this->value instanceof Bar && $this->type === 'bar')) { throw new Exception( "Expected bar; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'foo': $value = $this->asFoo()->jsonSerialize(); $result = array_merge($value, $result); @@ -137,26 +145,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -165,22 +174,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'foo': $args['value'] = Foo::jsonDeserialize($data); break; @@ -192,7 +202,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions-with-local-date/src/Types/TypesClient.php b/seed/php-sdk/unions-with-local-date/src/Types/TypesClient.php index 95e85127ddfb..a1e591545e7b 100644 --- a/seed/php-sdk/unions-with-local-date/src/Types/TypesClient.php +++ b/seed/php-sdk/unions-with-local-date/src/Types/TypesClient.php @@ -14,7 +14,7 @@ use Psr\Http\Client\ClientExceptionInterface; use Seed\Core\Json\JsonDecoder; -class TypesClient +class TypesClient { /** * @var array{ @@ -42,11 +42,10 @@ class TypesClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -65,7 +64,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function get(string $id, ?array $options = null): UnionWithTime { + public function get(string $id, ?array $options = null): UnionWithTime + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -77,15 +77,15 @@ public function get(string $id, ?array $options = null): UnionWithTime { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return UnionWithTime::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -117,7 +117,8 @@ public function get(string $id, ?array $options = null): UnionWithTime { * @throws SeedException * @throws SeedApiException */ - public function update(UnionWithTime $request, ?array $options = null): bool { + public function update(UnionWithTime $request, ?array $options = null): bool + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -130,15 +131,15 @@ public function update(UnionWithTime $request, ?array $options = null): bool { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeBool($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/unions-with-local-date/src/Union/Types/Circle.php b/seed/php-sdk/unions-with-local-date/src/Union/Types/Circle.php index c25a01408fa1..ee064d222ca2 100644 --- a/seed/php-sdk/unions-with-local-date/src/Union/Types/Circle.php +++ b/seed/php-sdk/unions-with-local-date/src/Union/Types/Circle.php @@ -20,15 +20,15 @@ class Circle extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->radius = $values['radius']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions-with-local-date/src/Union/Types/GetShapeRequest.php b/seed/php-sdk/unions-with-local-date/src/Union/Types/GetShapeRequest.php index a4ca1c0cdc41..5936f8fc45c1 100644 --- a/seed/php-sdk/unions-with-local-date/src/Union/Types/GetShapeRequest.php +++ b/seed/php-sdk/unions-with-local-date/src/Union/Types/GetShapeRequest.php @@ -20,15 +20,15 @@ class GetShapeRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->id = $values['id']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions-with-local-date/src/Union/Types/Shape.php b/seed/php-sdk/unions-with-local-date/src/Union/Types/Shape.php index cb7668687112..b02c60769705 100644 --- a/seed/php-sdk/unions-with-local-date/src/Union/Types/Shape.php +++ b/seed/php-sdk/unions-with-local-date/src/Union/Types/Shape.php @@ -50,9 +50,10 @@ class Shape extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->id = $values['id'];$this->type = $values['type'];$this->value = $values['value']; + ) { + $this->id = $values['id']; + $this->type = $values['type']; + $this->value = $values['value']; } /** @@ -60,7 +61,8 @@ private function __construct( * @param Circle $circle * @return Shape */ - public static function circle(string $id, Circle $circle): Shape { + public static function circle(string $id, Circle $circle): Shape + { return new Shape([ 'id' => $id, 'type' => 'circle', @@ -73,7 +75,8 @@ public static function circle(string $id, Circle $circle): Shape { * @param Square $square * @return Shape */ - public static function square(string $id, Square $square): Shape { + public static function square(string $id, Square $square): Shape + { return new Shape([ 'id' => $id, 'type' => 'square', @@ -84,61 +87,67 @@ public static function square(string $id, Square $square): Shape { /** * @return bool */ - public function isCircle(): bool { - return $this->value instanceof Circle&& $this->type === 'circle'; + public function isCircle(): bool + { + return $this->value instanceof Circle && $this->type === 'circle'; } /** * @return Circle */ - public function asCircle(): Circle { - if (!($this->value instanceof Circle&& $this->type === 'circle')){ + public function asCircle(): Circle + { + if (!($this->value instanceof Circle && $this->type === 'circle')) { throw new Exception( "Expected circle; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isSquare(): bool { - return $this->value instanceof Square&& $this->type === 'square'; + public function isSquare(): bool + { + return $this->value instanceof Square && $this->type === 'square'; } /** * @return Square */ - public function asSquare(): Square { - if (!($this->value instanceof Square&& $this->type === 'square')){ + public function asSquare(): Square + { + if (!($this->value instanceof Square && $this->type === 'square')) { throw new Exception( "Expected square; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'circle': $value = $this->asCircle()->jsonSerialize(); $result = array_merge($value, $result); @@ -149,26 +158,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -177,34 +187,35 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('id', $data)){ + if (!array_key_exists('id', $data)) { throw new Exception( "JSON data is missing property 'id'", ); } - if (!(is_string($data['id']))){ + if (!(is_string($data['id']))) { throw new Exception( "Expected property 'id' in JSON data to be string, instead received " . get_debug_type($data['id']), ); } $args['id'] = $data['id']; - - if (!array_key_exists('type', $data)){ + + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'circle': $args['value'] = Circle::jsonDeserialize($data); break; @@ -216,7 +227,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions-with-local-date/src/Union/Types/Square.php b/seed/php-sdk/unions-with-local-date/src/Union/Types/Square.php index b7dd2a8de985..0385f38f0143 100644 --- a/seed/php-sdk/unions-with-local-date/src/Union/Types/Square.php +++ b/seed/php-sdk/unions-with-local-date/src/Union/Types/Square.php @@ -20,15 +20,15 @@ class Square extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->length = $values['length']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions-with-local-date/src/Union/Types/WithName.php b/seed/php-sdk/unions-with-local-date/src/Union/Types/WithName.php index b1629d2b71ad..109ff5d2952f 100644 --- a/seed/php-sdk/unions-with-local-date/src/Union/Types/WithName.php +++ b/seed/php-sdk/unions-with-local-date/src/Union/Types/WithName.php @@ -20,15 +20,15 @@ class WithName extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions-with-local-date/src/Union/UnionClient.php b/seed/php-sdk/unions-with-local-date/src/Union/UnionClient.php index 4f27c965e752..baacf1c699f3 100644 --- a/seed/php-sdk/unions-with-local-date/src/Union/UnionClient.php +++ b/seed/php-sdk/unions-with-local-date/src/Union/UnionClient.php @@ -14,7 +14,7 @@ use Psr\Http\Client\ClientExceptionInterface; use Seed\Core\Json\JsonDecoder; -class UnionClient +class UnionClient { /** * @var array{ @@ -42,11 +42,10 @@ class UnionClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -65,7 +64,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function get(string $id, ?array $options = null): Shape { + public function get(string $id, ?array $options = null): Shape + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -77,15 +77,15 @@ public function get(string $id, ?array $options = null): Shape { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return Shape::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -117,7 +117,8 @@ public function get(string $id, ?array $options = null): Shape { * @throws SeedException * @throws SeedApiException */ - public function update(Shape $request, ?array $options = null): bool { + public function update(Shape $request, ?array $options = null): bool + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -130,15 +131,15 @@ public function update(Shape $request, ?array $options = null): bool { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeBool($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/unions-with-local-date/src/Utils/File.php b/seed/php-sdk/unions-with-local-date/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/unions-with-local-date/src/Utils/File.php +++ b/seed/php-sdk/unions-with-local-date/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/unions-with-local-date/tests/Core/Client/RawClientTest.php b/seed/php-sdk/unions-with-local-date/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/unions-with-local-date/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/unions-with-local-date/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/unions-with-local-date/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/unions-with-local-date/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/unions-with-local-date/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/unions-with-local-date/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/unions-with-local-date/tests/Core/Json/EnumTest.php b/seed/php-sdk/unions-with-local-date/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/unions-with-local-date/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/unions-with-local-date/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/unions-with-local-date/tests/Core/Json/TraitTest.php b/seed/php-sdk/unions-with-local-date/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/unions-with-local-date/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/unions-with-local-date/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/unions-with-local-date/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/unions-with-local-date/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/unions-with-local-date/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/unions-with-local-date/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/unions/no-custom-config/src/Bigunion/BigunionClient.php b/seed/php-sdk/unions/no-custom-config/src/Bigunion/BigunionClient.php index 25224149db51..fd1ab6f28ef3 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Bigunion/BigunionClient.php +++ b/seed/php-sdk/unions/no-custom-config/src/Bigunion/BigunionClient.php @@ -15,7 +15,7 @@ use Seed\Core\Json\JsonDecoder; use Seed\Core\Json\JsonSerializer; -class BigunionClient +class BigunionClient { /** * @var array{ @@ -43,11 +43,10 @@ class BigunionClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -66,7 +65,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function get(string $id, ?array $options = null): BigUnion { + public function get(string $id, ?array $options = null): BigUnion + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -78,15 +78,15 @@ public function get(string $id, ?array $options = null): BigUnion { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return BigUnion::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -118,7 +118,8 @@ public function get(string $id, ?array $options = null): BigUnion { * @throws SeedException * @throws SeedApiException */ - public function update(BigUnion $request, ?array $options = null): bool { + public function update(BigUnion $request, ?array $options = null): bool + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -131,15 +132,15 @@ public function update(BigUnion $request, ?array $options = null): bool { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeBool($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -171,7 +172,8 @@ public function update(BigUnion $request, ?array $options = null): bool { * @throws SeedException * @throws SeedApiException */ - public function updateMany(array $request, ?array $options = null): array { + public function updateMany(array $request, ?array $options = null): array + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -184,15 +186,15 @@ public function updateMany(array $request, ?array $options = null): array { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, ['string' => 'bool']); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/ActiveDiamond.php b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/ActiveDiamond.php index 41e0adcf275e..d349a8f8c451 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/ActiveDiamond.php +++ b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/ActiveDiamond.php @@ -20,15 +20,15 @@ class ActiveDiamond extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/AttractiveScript.php b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/AttractiveScript.php index 56006b3f8135..e4cae475497a 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/AttractiveScript.php +++ b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/AttractiveScript.php @@ -20,15 +20,15 @@ class AttractiveScript extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/BigUnion.php b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/BigUnion.php index 0b2736d9def0..146fda428ac3 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/BigUnion.php +++ b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/BigUnion.php @@ -174,9 +174,12 @@ class BigUnion extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->id = $values['id'];$this->createdAt = $values['createdAt'];$this->archivedAt = $values['archivedAt'] ?? null;$this->type = $values['type'];$this->value = $values['value']; + ) { + $this->id = $values['id']; + $this->createdAt = $values['createdAt']; + $this->archivedAt = $values['archivedAt'] ?? null; + $this->type = $values['type']; + $this->value = $values['value']; } /** @@ -186,7 +189,8 @@ private function __construct( * @param NormalSweet $normalSweet * @return BigUnion */ - public static function normalSweet(string $id, DateTime $createdAt, NormalSweet $normalSweet, ?DateTime $archivedAt = null): BigUnion { + public static function normalSweet(string $id, DateTime $createdAt, NormalSweet $normalSweet, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -203,7 +207,8 @@ public static function normalSweet(string $id, DateTime $createdAt, NormalSweet * @param ThankfulFactor $thankfulFactor * @return BigUnion */ - public static function thankfulFactor(string $id, DateTime $createdAt, ThankfulFactor $thankfulFactor, ?DateTime $archivedAt = null): BigUnion { + public static function thankfulFactor(string $id, DateTime $createdAt, ThankfulFactor $thankfulFactor, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -220,7 +225,8 @@ public static function thankfulFactor(string $id, DateTime $createdAt, ThankfulF * @param JumboEnd $jumboEnd * @return BigUnion */ - public static function jumboEnd(string $id, DateTime $createdAt, JumboEnd $jumboEnd, ?DateTime $archivedAt = null): BigUnion { + public static function jumboEnd(string $id, DateTime $createdAt, JumboEnd $jumboEnd, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -237,7 +243,8 @@ public static function jumboEnd(string $id, DateTime $createdAt, JumboEnd $jumbo * @param HastyPain $hastyPain * @return BigUnion */ - public static function hastyPain(string $id, DateTime $createdAt, HastyPain $hastyPain, ?DateTime $archivedAt = null): BigUnion { + public static function hastyPain(string $id, DateTime $createdAt, HastyPain $hastyPain, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -254,7 +261,8 @@ public static function hastyPain(string $id, DateTime $createdAt, HastyPain $has * @param MistySnow $mistySnow * @return BigUnion */ - public static function mistySnow(string $id, DateTime $createdAt, MistySnow $mistySnow, ?DateTime $archivedAt = null): BigUnion { + public static function mistySnow(string $id, DateTime $createdAt, MistySnow $mistySnow, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -271,7 +279,8 @@ public static function mistySnow(string $id, DateTime $createdAt, MistySnow $mis * @param DistinctFailure $distinctFailure * @return BigUnion */ - public static function distinctFailure(string $id, DateTime $createdAt, DistinctFailure $distinctFailure, ?DateTime $archivedAt = null): BigUnion { + public static function distinctFailure(string $id, DateTime $createdAt, DistinctFailure $distinctFailure, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -288,7 +297,8 @@ public static function distinctFailure(string $id, DateTime $createdAt, Distinct * @param PracticalPrinciple $practicalPrinciple * @return BigUnion */ - public static function practicalPrinciple(string $id, DateTime $createdAt, PracticalPrinciple $practicalPrinciple, ?DateTime $archivedAt = null): BigUnion { + public static function practicalPrinciple(string $id, DateTime $createdAt, PracticalPrinciple $practicalPrinciple, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -305,7 +315,8 @@ public static function practicalPrinciple(string $id, DateTime $createdAt, Pract * @param LimpingStep $limpingStep * @return BigUnion */ - public static function limpingStep(string $id, DateTime $createdAt, LimpingStep $limpingStep, ?DateTime $archivedAt = null): BigUnion { + public static function limpingStep(string $id, DateTime $createdAt, LimpingStep $limpingStep, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -322,7 +333,8 @@ public static function limpingStep(string $id, DateTime $createdAt, LimpingStep * @param VibrantExcitement $vibrantExcitement * @return BigUnion */ - public static function vibrantExcitement(string $id, DateTime $createdAt, VibrantExcitement $vibrantExcitement, ?DateTime $archivedAt = null): BigUnion { + public static function vibrantExcitement(string $id, DateTime $createdAt, VibrantExcitement $vibrantExcitement, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -339,7 +351,8 @@ public static function vibrantExcitement(string $id, DateTime $createdAt, Vibran * @param ActiveDiamond $activeDiamond * @return BigUnion */ - public static function activeDiamond(string $id, DateTime $createdAt, ActiveDiamond $activeDiamond, ?DateTime $archivedAt = null): BigUnion { + public static function activeDiamond(string $id, DateTime $createdAt, ActiveDiamond $activeDiamond, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -356,7 +369,8 @@ public static function activeDiamond(string $id, DateTime $createdAt, ActiveDiam * @param PopularLimit $popularLimit * @return BigUnion */ - public static function popularLimit(string $id, DateTime $createdAt, PopularLimit $popularLimit, ?DateTime $archivedAt = null): BigUnion { + public static function popularLimit(string $id, DateTime $createdAt, PopularLimit $popularLimit, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -373,7 +387,8 @@ public static function popularLimit(string $id, DateTime $createdAt, PopularLimi * @param FalseMirror $falseMirror * @return BigUnion */ - public static function falseMirror(string $id, DateTime $createdAt, FalseMirror $falseMirror, ?DateTime $archivedAt = null): BigUnion { + public static function falseMirror(string $id, DateTime $createdAt, FalseMirror $falseMirror, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -390,7 +405,8 @@ public static function falseMirror(string $id, DateTime $createdAt, FalseMirror * @param PrimaryBlock $primaryBlock * @return BigUnion */ - public static function primaryBlock(string $id, DateTime $createdAt, PrimaryBlock $primaryBlock, ?DateTime $archivedAt = null): BigUnion { + public static function primaryBlock(string $id, DateTime $createdAt, PrimaryBlock $primaryBlock, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -407,7 +423,8 @@ public static function primaryBlock(string $id, DateTime $createdAt, PrimaryBloc * @param RotatingRatio $rotatingRatio * @return BigUnion */ - public static function rotatingRatio(string $id, DateTime $createdAt, RotatingRatio $rotatingRatio, ?DateTime $archivedAt = null): BigUnion { + public static function rotatingRatio(string $id, DateTime $createdAt, RotatingRatio $rotatingRatio, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -424,7 +441,8 @@ public static function rotatingRatio(string $id, DateTime $createdAt, RotatingRa * @param ColorfulCover $colorfulCover * @return BigUnion */ - public static function colorfulCover(string $id, DateTime $createdAt, ColorfulCover $colorfulCover, ?DateTime $archivedAt = null): BigUnion { + public static function colorfulCover(string $id, DateTime $createdAt, ColorfulCover $colorfulCover, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -441,7 +459,8 @@ public static function colorfulCover(string $id, DateTime $createdAt, ColorfulCo * @param DisloyalValue $disloyalValue * @return BigUnion */ - public static function disloyalValue(string $id, DateTime $createdAt, DisloyalValue $disloyalValue, ?DateTime $archivedAt = null): BigUnion { + public static function disloyalValue(string $id, DateTime $createdAt, DisloyalValue $disloyalValue, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -458,7 +477,8 @@ public static function disloyalValue(string $id, DateTime $createdAt, DisloyalVa * @param GruesomeCoach $gruesomeCoach * @return BigUnion */ - public static function gruesomeCoach(string $id, DateTime $createdAt, GruesomeCoach $gruesomeCoach, ?DateTime $archivedAt = null): BigUnion { + public static function gruesomeCoach(string $id, DateTime $createdAt, GruesomeCoach $gruesomeCoach, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -475,7 +495,8 @@ public static function gruesomeCoach(string $id, DateTime $createdAt, GruesomeCo * @param TotalWork $totalWork * @return BigUnion */ - public static function totalWork(string $id, DateTime $createdAt, TotalWork $totalWork, ?DateTime $archivedAt = null): BigUnion { + public static function totalWork(string $id, DateTime $createdAt, TotalWork $totalWork, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -492,7 +513,8 @@ public static function totalWork(string $id, DateTime $createdAt, TotalWork $tot * @param HarmoniousPlay $harmoniousPlay * @return BigUnion */ - public static function harmoniousPlay(string $id, DateTime $createdAt, HarmoniousPlay $harmoniousPlay, ?DateTime $archivedAt = null): BigUnion { + public static function harmoniousPlay(string $id, DateTime $createdAt, HarmoniousPlay $harmoniousPlay, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -509,7 +531,8 @@ public static function harmoniousPlay(string $id, DateTime $createdAt, Harmoniou * @param UniqueStress $uniqueStress * @return BigUnion */ - public static function uniqueStress(string $id, DateTime $createdAt, UniqueStress $uniqueStress, ?DateTime $archivedAt = null): BigUnion { + public static function uniqueStress(string $id, DateTime $createdAt, UniqueStress $uniqueStress, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -526,7 +549,8 @@ public static function uniqueStress(string $id, DateTime $createdAt, UniqueStres * @param UnwillingSmoke $unwillingSmoke * @return BigUnion */ - public static function unwillingSmoke(string $id, DateTime $createdAt, UnwillingSmoke $unwillingSmoke, ?DateTime $archivedAt = null): BigUnion { + public static function unwillingSmoke(string $id, DateTime $createdAt, UnwillingSmoke $unwillingSmoke, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -543,7 +567,8 @@ public static function unwillingSmoke(string $id, DateTime $createdAt, Unwilling * @param FrozenSleep $frozenSleep * @return BigUnion */ - public static function frozenSleep(string $id, DateTime $createdAt, FrozenSleep $frozenSleep, ?DateTime $archivedAt = null): BigUnion { + public static function frozenSleep(string $id, DateTime $createdAt, FrozenSleep $frozenSleep, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -560,7 +585,8 @@ public static function frozenSleep(string $id, DateTime $createdAt, FrozenSleep * @param DiligentDeal $diligentDeal * @return BigUnion */ - public static function diligentDeal(string $id, DateTime $createdAt, DiligentDeal $diligentDeal, ?DateTime $archivedAt = null): BigUnion { + public static function diligentDeal(string $id, DateTime $createdAt, DiligentDeal $diligentDeal, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -577,7 +603,8 @@ public static function diligentDeal(string $id, DateTime $createdAt, DiligentDea * @param AttractiveScript $attractiveScript * @return BigUnion */ - public static function attractiveScript(string $id, DateTime $createdAt, AttractiveScript $attractiveScript, ?DateTime $archivedAt = null): BigUnion { + public static function attractiveScript(string $id, DateTime $createdAt, AttractiveScript $attractiveScript, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -594,7 +621,8 @@ public static function attractiveScript(string $id, DateTime $createdAt, Attract * @param HoarseMouse $hoarseMouse * @return BigUnion */ - public static function hoarseMouse(string $id, DateTime $createdAt, HoarseMouse $hoarseMouse, ?DateTime $archivedAt = null): BigUnion { + public static function hoarseMouse(string $id, DateTime $createdAt, HoarseMouse $hoarseMouse, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -611,7 +639,8 @@ public static function hoarseMouse(string $id, DateTime $createdAt, HoarseMouse * @param CircularCard $circularCard * @return BigUnion */ - public static function circularCard(string $id, DateTime $createdAt, CircularCard $circularCard, ?DateTime $archivedAt = null): BigUnion { + public static function circularCard(string $id, DateTime $createdAt, CircularCard $circularCard, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -628,7 +657,8 @@ public static function circularCard(string $id, DateTime $createdAt, CircularCar * @param PotableBad $potableBad * @return BigUnion */ - public static function potableBad(string $id, DateTime $createdAt, PotableBad $potableBad, ?DateTime $archivedAt = null): BigUnion { + public static function potableBad(string $id, DateTime $createdAt, PotableBad $potableBad, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -645,7 +675,8 @@ public static function potableBad(string $id, DateTime $createdAt, PotableBad $p * @param TriangularRepair $triangularRepair * @return BigUnion */ - public static function triangularRepair(string $id, DateTime $createdAt, TriangularRepair $triangularRepair, ?DateTime $archivedAt = null): BigUnion { + public static function triangularRepair(string $id, DateTime $createdAt, TriangularRepair $triangularRepair, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -662,7 +693,8 @@ public static function triangularRepair(string $id, DateTime $createdAt, Triangu * @param GaseousRoad $gaseousRoad * @return BigUnion */ - public static function gaseousRoad(string $id, DateTime $createdAt, GaseousRoad $gaseousRoad, ?DateTime $archivedAt = null): BigUnion { + public static function gaseousRoad(string $id, DateTime $createdAt, GaseousRoad $gaseousRoad, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -675,601 +707,661 @@ public static function gaseousRoad(string $id, DateTime $createdAt, GaseousRoad /** * @return bool */ - public function isNormalSweet(): bool { - return $this->value instanceof NormalSweet&& $this->type === 'normalSweet'; + public function isNormalSweet(): bool + { + return $this->value instanceof NormalSweet && $this->type === 'normalSweet'; } /** * @return NormalSweet */ - public function asNormalSweet(): NormalSweet { - if (!($this->value instanceof NormalSweet&& $this->type === 'normalSweet')){ + public function asNormalSweet(): NormalSweet + { + if (!($this->value instanceof NormalSweet && $this->type === 'normalSweet')) { throw new Exception( "Expected normalSweet; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isThankfulFactor(): bool { - return $this->value instanceof ThankfulFactor&& $this->type === 'thankfulFactor'; + public function isThankfulFactor(): bool + { + return $this->value instanceof ThankfulFactor && $this->type === 'thankfulFactor'; } /** * @return ThankfulFactor */ - public function asThankfulFactor(): ThankfulFactor { - if (!($this->value instanceof ThankfulFactor&& $this->type === 'thankfulFactor')){ + public function asThankfulFactor(): ThankfulFactor + { + if (!($this->value instanceof ThankfulFactor && $this->type === 'thankfulFactor')) { throw new Exception( "Expected thankfulFactor; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isJumboEnd(): bool { - return $this->value instanceof JumboEnd&& $this->type === 'jumboEnd'; + public function isJumboEnd(): bool + { + return $this->value instanceof JumboEnd && $this->type === 'jumboEnd'; } /** * @return JumboEnd */ - public function asJumboEnd(): JumboEnd { - if (!($this->value instanceof JumboEnd&& $this->type === 'jumboEnd')){ + public function asJumboEnd(): JumboEnd + { + if (!($this->value instanceof JumboEnd && $this->type === 'jumboEnd')) { throw new Exception( "Expected jumboEnd; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isHastyPain(): bool { - return $this->value instanceof HastyPain&& $this->type === 'hastyPain'; + public function isHastyPain(): bool + { + return $this->value instanceof HastyPain && $this->type === 'hastyPain'; } /** * @return HastyPain */ - public function asHastyPain(): HastyPain { - if (!($this->value instanceof HastyPain&& $this->type === 'hastyPain')){ + public function asHastyPain(): HastyPain + { + if (!($this->value instanceof HastyPain && $this->type === 'hastyPain')) { throw new Exception( "Expected hastyPain; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isMistySnow(): bool { - return $this->value instanceof MistySnow&& $this->type === 'mistySnow'; + public function isMistySnow(): bool + { + return $this->value instanceof MistySnow && $this->type === 'mistySnow'; } /** * @return MistySnow */ - public function asMistySnow(): MistySnow { - if (!($this->value instanceof MistySnow&& $this->type === 'mistySnow')){ + public function asMistySnow(): MistySnow + { + if (!($this->value instanceof MistySnow && $this->type === 'mistySnow')) { throw new Exception( "Expected mistySnow; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isDistinctFailure(): bool { - return $this->value instanceof DistinctFailure&& $this->type === 'distinctFailure'; + public function isDistinctFailure(): bool + { + return $this->value instanceof DistinctFailure && $this->type === 'distinctFailure'; } /** * @return DistinctFailure */ - public function asDistinctFailure(): DistinctFailure { - if (!($this->value instanceof DistinctFailure&& $this->type === 'distinctFailure')){ + public function asDistinctFailure(): DistinctFailure + { + if (!($this->value instanceof DistinctFailure && $this->type === 'distinctFailure')) { throw new Exception( "Expected distinctFailure; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isPracticalPrinciple(): bool { - return $this->value instanceof PracticalPrinciple&& $this->type === 'practicalPrinciple'; + public function isPracticalPrinciple(): bool + { + return $this->value instanceof PracticalPrinciple && $this->type === 'practicalPrinciple'; } /** * @return PracticalPrinciple */ - public function asPracticalPrinciple(): PracticalPrinciple { - if (!($this->value instanceof PracticalPrinciple&& $this->type === 'practicalPrinciple')){ + public function asPracticalPrinciple(): PracticalPrinciple + { + if (!($this->value instanceof PracticalPrinciple && $this->type === 'practicalPrinciple')) { throw new Exception( "Expected practicalPrinciple; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isLimpingStep(): bool { - return $this->value instanceof LimpingStep&& $this->type === 'limpingStep'; + public function isLimpingStep(): bool + { + return $this->value instanceof LimpingStep && $this->type === 'limpingStep'; } /** * @return LimpingStep */ - public function asLimpingStep(): LimpingStep { - if (!($this->value instanceof LimpingStep&& $this->type === 'limpingStep')){ + public function asLimpingStep(): LimpingStep + { + if (!($this->value instanceof LimpingStep && $this->type === 'limpingStep')) { throw new Exception( "Expected limpingStep; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isVibrantExcitement(): bool { - return $this->value instanceof VibrantExcitement&& $this->type === 'vibrantExcitement'; + public function isVibrantExcitement(): bool + { + return $this->value instanceof VibrantExcitement && $this->type === 'vibrantExcitement'; } /** * @return VibrantExcitement */ - public function asVibrantExcitement(): VibrantExcitement { - if (!($this->value instanceof VibrantExcitement&& $this->type === 'vibrantExcitement')){ + public function asVibrantExcitement(): VibrantExcitement + { + if (!($this->value instanceof VibrantExcitement && $this->type === 'vibrantExcitement')) { throw new Exception( "Expected vibrantExcitement; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isActiveDiamond(): bool { - return $this->value instanceof ActiveDiamond&& $this->type === 'activeDiamond'; + public function isActiveDiamond(): bool + { + return $this->value instanceof ActiveDiamond && $this->type === 'activeDiamond'; } /** * @return ActiveDiamond */ - public function asActiveDiamond(): ActiveDiamond { - if (!($this->value instanceof ActiveDiamond&& $this->type === 'activeDiamond')){ + public function asActiveDiamond(): ActiveDiamond + { + if (!($this->value instanceof ActiveDiamond && $this->type === 'activeDiamond')) { throw new Exception( "Expected activeDiamond; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isPopularLimit(): bool { - return $this->value instanceof PopularLimit&& $this->type === 'popularLimit'; + public function isPopularLimit(): bool + { + return $this->value instanceof PopularLimit && $this->type === 'popularLimit'; } /** * @return PopularLimit */ - public function asPopularLimit(): PopularLimit { - if (!($this->value instanceof PopularLimit&& $this->type === 'popularLimit')){ + public function asPopularLimit(): PopularLimit + { + if (!($this->value instanceof PopularLimit && $this->type === 'popularLimit')) { throw new Exception( "Expected popularLimit; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isFalseMirror(): bool { - return $this->value instanceof FalseMirror&& $this->type === 'falseMirror'; + public function isFalseMirror(): bool + { + return $this->value instanceof FalseMirror && $this->type === 'falseMirror'; } /** * @return FalseMirror */ - public function asFalseMirror(): FalseMirror { - if (!($this->value instanceof FalseMirror&& $this->type === 'falseMirror')){ + public function asFalseMirror(): FalseMirror + { + if (!($this->value instanceof FalseMirror && $this->type === 'falseMirror')) { throw new Exception( "Expected falseMirror; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isPrimaryBlock(): bool { - return $this->value instanceof PrimaryBlock&& $this->type === 'primaryBlock'; + public function isPrimaryBlock(): bool + { + return $this->value instanceof PrimaryBlock && $this->type === 'primaryBlock'; } /** * @return PrimaryBlock */ - public function asPrimaryBlock(): PrimaryBlock { - if (!($this->value instanceof PrimaryBlock&& $this->type === 'primaryBlock')){ + public function asPrimaryBlock(): PrimaryBlock + { + if (!($this->value instanceof PrimaryBlock && $this->type === 'primaryBlock')) { throw new Exception( "Expected primaryBlock; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isRotatingRatio(): bool { - return $this->value instanceof RotatingRatio&& $this->type === 'rotatingRatio'; + public function isRotatingRatio(): bool + { + return $this->value instanceof RotatingRatio && $this->type === 'rotatingRatio'; } /** * @return RotatingRatio */ - public function asRotatingRatio(): RotatingRatio { - if (!($this->value instanceof RotatingRatio&& $this->type === 'rotatingRatio')){ + public function asRotatingRatio(): RotatingRatio + { + if (!($this->value instanceof RotatingRatio && $this->type === 'rotatingRatio')) { throw new Exception( "Expected rotatingRatio; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isColorfulCover(): bool { - return $this->value instanceof ColorfulCover&& $this->type === 'colorfulCover'; + public function isColorfulCover(): bool + { + return $this->value instanceof ColorfulCover && $this->type === 'colorfulCover'; } /** * @return ColorfulCover */ - public function asColorfulCover(): ColorfulCover { - if (!($this->value instanceof ColorfulCover&& $this->type === 'colorfulCover')){ + public function asColorfulCover(): ColorfulCover + { + if (!($this->value instanceof ColorfulCover && $this->type === 'colorfulCover')) { throw new Exception( "Expected colorfulCover; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isDisloyalValue(): bool { - return $this->value instanceof DisloyalValue&& $this->type === 'disloyalValue'; + public function isDisloyalValue(): bool + { + return $this->value instanceof DisloyalValue && $this->type === 'disloyalValue'; } /** * @return DisloyalValue */ - public function asDisloyalValue(): DisloyalValue { - if (!($this->value instanceof DisloyalValue&& $this->type === 'disloyalValue')){ + public function asDisloyalValue(): DisloyalValue + { + if (!($this->value instanceof DisloyalValue && $this->type === 'disloyalValue')) { throw new Exception( "Expected disloyalValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isGruesomeCoach(): bool { - return $this->value instanceof GruesomeCoach&& $this->type === 'gruesomeCoach'; + public function isGruesomeCoach(): bool + { + return $this->value instanceof GruesomeCoach && $this->type === 'gruesomeCoach'; } /** * @return GruesomeCoach */ - public function asGruesomeCoach(): GruesomeCoach { - if (!($this->value instanceof GruesomeCoach&& $this->type === 'gruesomeCoach')){ + public function asGruesomeCoach(): GruesomeCoach + { + if (!($this->value instanceof GruesomeCoach && $this->type === 'gruesomeCoach')) { throw new Exception( "Expected gruesomeCoach; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isTotalWork(): bool { - return $this->value instanceof TotalWork&& $this->type === 'totalWork'; + public function isTotalWork(): bool + { + return $this->value instanceof TotalWork && $this->type === 'totalWork'; } /** * @return TotalWork */ - public function asTotalWork(): TotalWork { - if (!($this->value instanceof TotalWork&& $this->type === 'totalWork')){ + public function asTotalWork(): TotalWork + { + if (!($this->value instanceof TotalWork && $this->type === 'totalWork')) { throw new Exception( "Expected totalWork; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isHarmoniousPlay(): bool { - return $this->value instanceof HarmoniousPlay&& $this->type === 'harmoniousPlay'; + public function isHarmoniousPlay(): bool + { + return $this->value instanceof HarmoniousPlay && $this->type === 'harmoniousPlay'; } /** * @return HarmoniousPlay */ - public function asHarmoniousPlay(): HarmoniousPlay { - if (!($this->value instanceof HarmoniousPlay&& $this->type === 'harmoniousPlay')){ + public function asHarmoniousPlay(): HarmoniousPlay + { + if (!($this->value instanceof HarmoniousPlay && $this->type === 'harmoniousPlay')) { throw new Exception( "Expected harmoniousPlay; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isUniqueStress(): bool { - return $this->value instanceof UniqueStress&& $this->type === 'uniqueStress'; + public function isUniqueStress(): bool + { + return $this->value instanceof UniqueStress && $this->type === 'uniqueStress'; } /** * @return UniqueStress */ - public function asUniqueStress(): UniqueStress { - if (!($this->value instanceof UniqueStress&& $this->type === 'uniqueStress')){ + public function asUniqueStress(): UniqueStress + { + if (!($this->value instanceof UniqueStress && $this->type === 'uniqueStress')) { throw new Exception( "Expected uniqueStress; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isUnwillingSmoke(): bool { - return $this->value instanceof UnwillingSmoke&& $this->type === 'unwillingSmoke'; + public function isUnwillingSmoke(): bool + { + return $this->value instanceof UnwillingSmoke && $this->type === 'unwillingSmoke'; } /** * @return UnwillingSmoke */ - public function asUnwillingSmoke(): UnwillingSmoke { - if (!($this->value instanceof UnwillingSmoke&& $this->type === 'unwillingSmoke')){ + public function asUnwillingSmoke(): UnwillingSmoke + { + if (!($this->value instanceof UnwillingSmoke && $this->type === 'unwillingSmoke')) { throw new Exception( "Expected unwillingSmoke; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isFrozenSleep(): bool { - return $this->value instanceof FrozenSleep&& $this->type === 'frozenSleep'; + public function isFrozenSleep(): bool + { + return $this->value instanceof FrozenSleep && $this->type === 'frozenSleep'; } /** * @return FrozenSleep */ - public function asFrozenSleep(): FrozenSleep { - if (!($this->value instanceof FrozenSleep&& $this->type === 'frozenSleep')){ + public function asFrozenSleep(): FrozenSleep + { + if (!($this->value instanceof FrozenSleep && $this->type === 'frozenSleep')) { throw new Exception( "Expected frozenSleep; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isDiligentDeal(): bool { - return $this->value instanceof DiligentDeal&& $this->type === 'diligentDeal'; + public function isDiligentDeal(): bool + { + return $this->value instanceof DiligentDeal && $this->type === 'diligentDeal'; } /** * @return DiligentDeal */ - public function asDiligentDeal(): DiligentDeal { - if (!($this->value instanceof DiligentDeal&& $this->type === 'diligentDeal')){ + public function asDiligentDeal(): DiligentDeal + { + if (!($this->value instanceof DiligentDeal && $this->type === 'diligentDeal')) { throw new Exception( "Expected diligentDeal; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isAttractiveScript(): bool { - return $this->value instanceof AttractiveScript&& $this->type === 'attractiveScript'; + public function isAttractiveScript(): bool + { + return $this->value instanceof AttractiveScript && $this->type === 'attractiveScript'; } /** * @return AttractiveScript */ - public function asAttractiveScript(): AttractiveScript { - if (!($this->value instanceof AttractiveScript&& $this->type === 'attractiveScript')){ + public function asAttractiveScript(): AttractiveScript + { + if (!($this->value instanceof AttractiveScript && $this->type === 'attractiveScript')) { throw new Exception( "Expected attractiveScript; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isHoarseMouse(): bool { - return $this->value instanceof HoarseMouse&& $this->type === 'hoarseMouse'; + public function isHoarseMouse(): bool + { + return $this->value instanceof HoarseMouse && $this->type === 'hoarseMouse'; } /** * @return HoarseMouse */ - public function asHoarseMouse(): HoarseMouse { - if (!($this->value instanceof HoarseMouse&& $this->type === 'hoarseMouse')){ + public function asHoarseMouse(): HoarseMouse + { + if (!($this->value instanceof HoarseMouse && $this->type === 'hoarseMouse')) { throw new Exception( "Expected hoarseMouse; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isCircularCard(): bool { - return $this->value instanceof CircularCard&& $this->type === 'circularCard'; + public function isCircularCard(): bool + { + return $this->value instanceof CircularCard && $this->type === 'circularCard'; } /** * @return CircularCard */ - public function asCircularCard(): CircularCard { - if (!($this->value instanceof CircularCard&& $this->type === 'circularCard')){ + public function asCircularCard(): CircularCard + { + if (!($this->value instanceof CircularCard && $this->type === 'circularCard')) { throw new Exception( "Expected circularCard; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isPotableBad(): bool { - return $this->value instanceof PotableBad&& $this->type === 'potableBad'; + public function isPotableBad(): bool + { + return $this->value instanceof PotableBad && $this->type === 'potableBad'; } /** * @return PotableBad */ - public function asPotableBad(): PotableBad { - if (!($this->value instanceof PotableBad&& $this->type === 'potableBad')){ + public function asPotableBad(): PotableBad + { + if (!($this->value instanceof PotableBad && $this->type === 'potableBad')) { throw new Exception( "Expected potableBad; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isTriangularRepair(): bool { - return $this->value instanceof TriangularRepair&& $this->type === 'triangularRepair'; + public function isTriangularRepair(): bool + { + return $this->value instanceof TriangularRepair && $this->type === 'triangularRepair'; } /** * @return TriangularRepair */ - public function asTriangularRepair(): TriangularRepair { - if (!($this->value instanceof TriangularRepair&& $this->type === 'triangularRepair')){ + public function asTriangularRepair(): TriangularRepair + { + if (!($this->value instanceof TriangularRepair && $this->type === 'triangularRepair')) { throw new Exception( "Expected triangularRepair; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isGaseousRoad(): bool { - return $this->value instanceof GaseousRoad&& $this->type === 'gaseousRoad'; + public function isGaseousRoad(): bool + { + return $this->value instanceof GaseousRoad && $this->type === 'gaseousRoad'; } /** * @return GaseousRoad */ - public function asGaseousRoad(): GaseousRoad { - if (!($this->value instanceof GaseousRoad&& $this->type === 'gaseousRoad')){ + public function asGaseousRoad(): GaseousRoad + { + if (!($this->value instanceof GaseousRoad && $this->type === 'gaseousRoad')) { throw new Exception( "Expected gaseousRoad; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'normalSweet': $value = $this->asNormalSweet()->jsonSerialize(); $result = array_merge($value, $result); @@ -1388,26 +1480,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -1416,58 +1509,59 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('id', $data)){ + if (!array_key_exists('id', $data)) { throw new Exception( "JSON data is missing property 'id'", ); } - if (!(is_string($data['id']))){ + if (!(is_string($data['id']))) { throw new Exception( "Expected property 'id' in JSON data to be string, instead received " . get_debug_type($data['id']), ); } $args['id'] = $data['id']; - - if (!array_key_exists('created-at', $data)){ + + if (!array_key_exists('created-at', $data)) { throw new Exception( "JSON data is missing property 'created-at'", ); } - if (!($data['created-at'] instanceof DateTime)){ + if (!($data['created-at'] instanceof DateTime)) { throw new Exception( "Expected property 'createdAt' in JSON data to be dateTime, instead received " . get_debug_type($data['created-at']), ); } $args['createdAt'] = $data['created-at']; - - if (!array_key_exists('archived-at', $data)){ + + if (!array_key_exists('archived-at', $data)) { throw new Exception( "JSON data is missing property 'archived-at'", ); } - if (!((is_null($data['archived-at']) || $data['archived-at'] instanceof DateTime))){ + if (!((is_null($data['archived-at']) || $data['archived-at'] instanceof DateTime))) { throw new Exception( "Expected property 'archivedAt' in JSON data to be optional, instead received " . get_debug_type($data['archived-at']), ); } $args['archivedAt'] = $data['archived-at']; - - if (!array_key_exists('type', $data)){ + + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'normalSweet': $args['value'] = NormalSweet::jsonDeserialize($data); break; @@ -1560,7 +1654,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/CircularCard.php b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/CircularCard.php index 853f60baa9f6..a6af8e90fd05 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/CircularCard.php +++ b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/CircularCard.php @@ -20,15 +20,15 @@ class CircularCard extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/ColorfulCover.php b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/ColorfulCover.php index bda8960708cf..9b5650f9eef2 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/ColorfulCover.php +++ b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/ColorfulCover.php @@ -20,15 +20,15 @@ class ColorfulCover extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/DiligentDeal.php b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/DiligentDeal.php index a7c1d3016c32..fff5b37687aa 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/DiligentDeal.php +++ b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/DiligentDeal.php @@ -20,15 +20,15 @@ class DiligentDeal extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/DisloyalValue.php b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/DisloyalValue.php index 4e58269addcb..59c4c61d30b0 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/DisloyalValue.php +++ b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/DisloyalValue.php @@ -20,15 +20,15 @@ class DisloyalValue extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/DistinctFailure.php b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/DistinctFailure.php index 1a02d97a0d1b..f1d57e1f0359 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/DistinctFailure.php +++ b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/DistinctFailure.php @@ -20,15 +20,15 @@ class DistinctFailure extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/FalseMirror.php b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/FalseMirror.php index e2ff8265f279..28d94257908f 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/FalseMirror.php +++ b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/FalseMirror.php @@ -20,15 +20,15 @@ class FalseMirror extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/FrozenSleep.php b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/FrozenSleep.php index 8d019157ecb9..a3f655d755c9 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/FrozenSleep.php +++ b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/FrozenSleep.php @@ -20,15 +20,15 @@ class FrozenSleep extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/GaseousRoad.php b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/GaseousRoad.php index 7375c21fc494..de36b4880d3a 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/GaseousRoad.php +++ b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/GaseousRoad.php @@ -20,15 +20,15 @@ class GaseousRoad extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/GruesomeCoach.php b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/GruesomeCoach.php index 98ccff1cccd3..0cb48e1b7297 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/GruesomeCoach.php +++ b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/GruesomeCoach.php @@ -20,15 +20,15 @@ class GruesomeCoach extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/HarmoniousPlay.php b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/HarmoniousPlay.php index c04b11977819..d731330c4796 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/HarmoniousPlay.php +++ b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/HarmoniousPlay.php @@ -20,15 +20,15 @@ class HarmoniousPlay extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/HastyPain.php b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/HastyPain.php index 549b05b5af6a..ffbfb817a3d7 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/HastyPain.php +++ b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/HastyPain.php @@ -20,15 +20,15 @@ class HastyPain extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/HoarseMouse.php b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/HoarseMouse.php index f9b2dbe09510..5aee1829c78f 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/HoarseMouse.php +++ b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/HoarseMouse.php @@ -20,15 +20,15 @@ class HoarseMouse extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/JumboEnd.php b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/JumboEnd.php index 5fe215acd18d..fdb2a7e68e2d 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/JumboEnd.php +++ b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/JumboEnd.php @@ -20,15 +20,15 @@ class JumboEnd extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/LimpingStep.php b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/LimpingStep.php index db44a16c0fde..e401a8a35207 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/LimpingStep.php +++ b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/LimpingStep.php @@ -20,15 +20,15 @@ class LimpingStep extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/MistySnow.php b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/MistySnow.php index b4e72fd8795b..c0a0b8decd7e 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/MistySnow.php +++ b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/MistySnow.php @@ -20,15 +20,15 @@ class MistySnow extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/NormalSweet.php b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/NormalSweet.php index 724c0f2a5e6e..f567738a7f68 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/NormalSweet.php +++ b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/NormalSweet.php @@ -20,15 +20,15 @@ class NormalSweet extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/PopularLimit.php b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/PopularLimit.php index 16c6576f1ca8..2341a8e29200 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/PopularLimit.php +++ b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/PopularLimit.php @@ -20,15 +20,15 @@ class PopularLimit extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/PotableBad.php b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/PotableBad.php index f7acbe29a027..e24feec637b2 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/PotableBad.php +++ b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/PotableBad.php @@ -20,15 +20,15 @@ class PotableBad extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/PracticalPrinciple.php b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/PracticalPrinciple.php index 951a702b6b47..de4e769d98af 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/PracticalPrinciple.php +++ b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/PracticalPrinciple.php @@ -20,15 +20,15 @@ class PracticalPrinciple extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/PrimaryBlock.php b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/PrimaryBlock.php index ac9f697eb970..cd65467325d9 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/PrimaryBlock.php +++ b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/PrimaryBlock.php @@ -20,15 +20,15 @@ class PrimaryBlock extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/RotatingRatio.php b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/RotatingRatio.php index 0e0dede2293e..03c00e20774f 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/RotatingRatio.php +++ b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/RotatingRatio.php @@ -20,15 +20,15 @@ class RotatingRatio extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/ThankfulFactor.php b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/ThankfulFactor.php index 7b5b49c20ece..9bd8db71f821 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/ThankfulFactor.php +++ b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/ThankfulFactor.php @@ -20,15 +20,15 @@ class ThankfulFactor extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/TotalWork.php b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/TotalWork.php index 317ca9b75628..3fec61db6c78 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/TotalWork.php +++ b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/TotalWork.php @@ -20,15 +20,15 @@ class TotalWork extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/TriangularRepair.php b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/TriangularRepair.php index 0b1577053134..b066e4f0d7c2 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/TriangularRepair.php +++ b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/TriangularRepair.php @@ -20,15 +20,15 @@ class TriangularRepair extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/UniqueStress.php b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/UniqueStress.php index f0c638cf16ba..756cd9ae6167 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/UniqueStress.php +++ b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/UniqueStress.php @@ -20,15 +20,15 @@ class UniqueStress extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/UnwillingSmoke.php b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/UnwillingSmoke.php index 68f0d65e1e21..7660031b0a8e 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/UnwillingSmoke.php +++ b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/UnwillingSmoke.php @@ -20,15 +20,15 @@ class UnwillingSmoke extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/VibrantExcitement.php b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/VibrantExcitement.php index e03ebb35a41a..0752343e8d6c 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/VibrantExcitement.php +++ b/seed/php-sdk/unions/no-custom-config/src/Bigunion/Types/VibrantExcitement.php @@ -20,15 +20,15 @@ class VibrantExcitement extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/no-custom-config/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/unions/no-custom-config/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/unions/no-custom-config/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/unions/no-custom-config/src/Core/Client/RawClient.php b/seed/php-sdk/unions/no-custom-config/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Core/Client/RawClient.php +++ b/seed/php-sdk/unions/no-custom-config/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/unions/no-custom-config/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/unions/no-custom-config/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/unions/no-custom-config/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/unions/no-custom-config/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/unions/no-custom-config/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/unions/no-custom-config/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/unions/no-custom-config/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/unions/no-custom-config/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/unions/no-custom-config/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/unions/no-custom-config/src/Core/Types/ArrayType.php b/seed/php-sdk/unions/no-custom-config/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/unions/no-custom-config/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/unions/no-custom-config/src/Core/Types/Constant.php b/seed/php-sdk/unions/no-custom-config/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Core/Types/Constant.php +++ b/seed/php-sdk/unions/no-custom-config/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/unions/no-custom-config/src/Core/Types/Union.php b/seed/php-sdk/unions/no-custom-config/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Core/Types/Union.php +++ b/seed/php-sdk/unions/no-custom-config/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/unions/no-custom-config/src/Exceptions/SeedApiException.php b/seed/php-sdk/unions/no-custom-config/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/unions/no-custom-config/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/unions/no-custom-config/src/Exceptions/SeedException.php b/seed/php-sdk/unions/no-custom-config/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Exceptions/SeedException.php +++ b/seed/php-sdk/unions/no-custom-config/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/unions/no-custom-config/src/SeedClient.php b/seed/php-sdk/unions/no-custom-config/src/SeedClient.php index fd07d97e9231..8cc0c47fe323 100644 --- a/seed/php-sdk/unions/no-custom-config/src/SeedClient.php +++ b/seed/php-sdk/unions/no-custom-config/src/SeedClient.php @@ -7,7 +7,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var BigunionClient $bigunion @@ -46,26 +46,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->bigunion = new BigunionClient($this->client, $this->options); $this->union = new UnionClient($this->client, $this->options); } diff --git a/seed/php-sdk/unions/no-custom-config/src/Types/Traits/Foo.php b/seed/php-sdk/unions/no-custom-config/src/Types/Traits/Foo.php index 901f6d1cc180..7b400d71eacd 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Types/Traits/Foo.php +++ b/seed/php-sdk/unions/no-custom-config/src/Types/Traits/Foo.php @@ -7,7 +7,7 @@ /** * @property string $name */ -trait Foo +trait Foo { /** * @var string $name diff --git a/seed/php-sdk/unions/no-custom-config/src/Types/Types/Bar.php b/seed/php-sdk/unions/no-custom-config/src/Types/Types/Bar.php index de9118561f94..67fb977f3a74 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Types/Types/Bar.php +++ b/seed/php-sdk/unions/no-custom-config/src/Types/Types/Bar.php @@ -20,15 +20,15 @@ class Bar extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/no-custom-config/src/Types/Types/FirstItemType.php b/seed/php-sdk/unions/no-custom-config/src/Types/Types/FirstItemType.php index 69e49c41463d..809b8bac6291 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Types/Types/FirstItemType.php +++ b/seed/php-sdk/unions/no-custom-config/src/Types/Types/FirstItemType.php @@ -27,15 +27,16 @@ class FirstItemType extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->type = $values['type'] ?? null;$this->name = $values['name']; + ) { + $this->type = $values['type'] ?? null; + $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/no-custom-config/src/Types/Types/Foo.php b/seed/php-sdk/unions/no-custom-config/src/Types/Types/Foo.php index 82aff30230a8..bc35f865d077 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Types/Types/Foo.php +++ b/seed/php-sdk/unions/no-custom-config/src/Types/Types/Foo.php @@ -20,15 +20,15 @@ class Foo extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/no-custom-config/src/Types/Types/FooExtended.php b/seed/php-sdk/unions/no-custom-config/src/Types/Types/FooExtended.php index 4b69b86e9858..870a1a1a72b0 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Types/Types/FooExtended.php +++ b/seed/php-sdk/unions/no-custom-config/src/Types/Types/FooExtended.php @@ -24,15 +24,16 @@ class FooExtended extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->age = $values['age']; + ) { + $this->name = $values['name']; + $this->age = $values['age']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/no-custom-config/src/Types/Types/SecondItemType.php b/seed/php-sdk/unions/no-custom-config/src/Types/Types/SecondItemType.php index 68a534eb1efe..393552002d17 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Types/Types/SecondItemType.php +++ b/seed/php-sdk/unions/no-custom-config/src/Types/Types/SecondItemType.php @@ -27,15 +27,16 @@ class SecondItemType extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->type = $values['type'] ?? null;$this->title = $values['title']; + ) { + $this->type = $values['type'] ?? null; + $this->title = $values['title']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/no-custom-config/src/Types/Types/Union.php b/seed/php-sdk/unions/no-custom-config/src/Types/Types/Union.php index 5e8a24ac1855..3e30b4dcf023 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Types/Types/Union.php +++ b/seed/php-sdk/unions/no-custom-config/src/Types/Types/Union.php @@ -45,16 +45,17 @@ class Union extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param Foo $foo * @return Union */ - public static function foo(Foo $foo): Union { + public static function foo(Foo $foo): Union + { return new Union([ 'type' => 'foo', 'value' => $foo, @@ -65,7 +66,8 @@ public static function foo(Foo $foo): Union { * @param Bar $bar * @return Union */ - public static function bar(Bar $bar): Union { + public static function bar(Bar $bar): Union + { return new Union([ 'type' => 'bar', 'value' => $bar, @@ -75,61 +77,67 @@ public static function bar(Bar $bar): Union { /** * @return bool */ - public function isFoo(): bool { - return $this->value instanceof Foo&& $this->type === 'foo'; + public function isFoo(): bool + { + return $this->value instanceof Foo && $this->type === 'foo'; } /** * @return Foo */ - public function asFoo(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo')){ + public function asFoo(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo')) { throw new Exception( "Expected foo; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isBar(): bool { - return $this->value instanceof Bar&& $this->type === 'bar'; + public function isBar(): bool + { + return $this->value instanceof Bar && $this->type === 'bar'; } /** * @return Bar */ - public function asBar(): Bar { - if (!($this->value instanceof Bar&& $this->type === 'bar')){ + public function asBar(): Bar + { + if (!($this->value instanceof Bar && $this->type === 'bar')) { throw new Exception( "Expected bar; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'foo': $value = $this->asFoo()->jsonSerialize(); $result['foo'] = $value; @@ -140,26 +148,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -168,30 +177,31 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'foo': - if (!array_key_exists('foo', $data)){ + if (!array_key_exists('foo', $data)) { throw new Exception( "JSON data is missing property 'foo'", ); } - - if (!(is_array($data['foo']))){ + + if (!(is_array($data['foo']))) { throw new Exception( "Expected property 'foo' in JSON data to be array, instead received " . get_debug_type($data['foo']), ); @@ -199,13 +209,13 @@ public static function jsonDeserialize(array $data): static { $args['value'] = Foo::jsonDeserialize($data['foo']); break; case 'bar': - if (!array_key_exists('bar', $data)){ + if (!array_key_exists('bar', $data)) { throw new Exception( "JSON data is missing property 'bar'", ); } - - if (!(is_array($data['bar']))){ + + if (!(is_array($data['bar']))) { throw new Exception( "Expected property 'bar' in JSON data to be array, instead received " . get_debug_type($data['bar']), ); @@ -217,7 +227,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithBaseProperties.php b/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithBaseProperties.php index 921d1d8769dc..545f7d16d423 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithBaseProperties.php +++ b/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithBaseProperties.php @@ -54,9 +54,10 @@ class UnionWithBaseProperties extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->id = $values['id'];$this->type = $values['type'];$this->value = $values['value']; + ) { + $this->id = $values['id']; + $this->type = $values['type']; + $this->value = $values['value']; } /** @@ -64,7 +65,8 @@ private function __construct( * @param int $integer * @return UnionWithBaseProperties */ - public static function integer(string $id, int $integer): UnionWithBaseProperties { + public static function integer(string $id, int $integer): UnionWithBaseProperties + { return new UnionWithBaseProperties([ 'id' => $id, 'type' => 'integer', @@ -77,7 +79,8 @@ public static function integer(string $id, int $integer): UnionWithBasePropertie * @param string $string * @return UnionWithBaseProperties */ - public static function string(string $id, string $string): UnionWithBaseProperties { + public static function string(string $id, string $string): UnionWithBaseProperties + { return new UnionWithBaseProperties([ 'id' => $id, 'type' => 'string', @@ -90,7 +93,8 @@ public static function string(string $id, string $string): UnionWithBaseProperti * @param Foo $foo * @return UnionWithBaseProperties */ - public static function foo(string $id, Foo $foo): UnionWithBaseProperties { + public static function foo(string $id, Foo $foo): UnionWithBaseProperties + { return new UnionWithBaseProperties([ 'id' => $id, 'type' => 'foo', @@ -101,81 +105,89 @@ public static function foo(string $id, Foo $foo): UnionWithBaseProperties { /** * @return bool */ - public function isInteger(): bool { - return is_int($this->value)&& $this->type === 'integer'; + public function isInteger(): bool + { + return is_int($this->value) && $this->type === 'integer'; } /** * @return int */ - public function asInteger(): int { - if (!(is_int($this->value)&& $this->type === 'integer')){ + public function asInteger(): int + { + if (!(is_int($this->value) && $this->type === 'integer')) { throw new Exception( "Expected integer; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isString(): bool { - return is_string($this->value)&& $this->type === 'string'; + public function isString(): bool + { + return is_string($this->value) && $this->type === 'string'; } /** * @return string */ - public function asString(): string { - if (!(is_string($this->value)&& $this->type === 'string')){ + public function asString(): string + { + if (!(is_string($this->value) && $this->type === 'string')) { throw new Exception( "Expected string; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isFoo(): bool { - return $this->value instanceof Foo&& $this->type === 'foo'; + public function isFoo(): bool + { + return $this->value instanceof Foo && $this->type === 'foo'; } /** * @return Foo */ - public function asFoo(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo')){ + public function asFoo(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo')) { throw new Exception( "Expected foo; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'integer': $value = $this->value; $result['integer'] = $value; @@ -190,26 +202,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -218,50 +231,51 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('id', $data)){ + if (!array_key_exists('id', $data)) { throw new Exception( "JSON data is missing property 'id'", ); } - if (!(is_string($data['id']))){ + if (!(is_string($data['id']))) { throw new Exception( "Expected property 'id' in JSON data to be string, instead received " . get_debug_type($data['id']), ); } $args['id'] = $data['id']; - - if (!array_key_exists('type', $data)){ + + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'integer': - if (!array_key_exists('integer', $data)){ + if (!array_key_exists('integer', $data)) { throw new Exception( "JSON data is missing property 'integer'", ); } - + $args['value'] = $data['integer']; break; case 'string': - if (!array_key_exists('string', $data)){ + if (!array_key_exists('string', $data)) { throw new Exception( "JSON data is missing property 'string'", ); } - + $args['value'] = $data['string']; break; case 'foo': @@ -272,7 +286,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithDiscriminant.php b/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithDiscriminant.php index 8a073f33121d..12a0ec0e6c63 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithDiscriminant.php +++ b/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithDiscriminant.php @@ -42,16 +42,17 @@ class UnionWithDiscriminant extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param Foo $foo * @return UnionWithDiscriminant */ - public static function foo(Foo $foo): UnionWithDiscriminant { + public static function foo(Foo $foo): UnionWithDiscriminant + { return new UnionWithDiscriminant([ 'type' => 'foo', 'value' => $foo, @@ -62,7 +63,8 @@ public static function foo(Foo $foo): UnionWithDiscriminant { * @param Bar $bar * @return UnionWithDiscriminant */ - public static function bar(Bar $bar): UnionWithDiscriminant { + public static function bar(Bar $bar): UnionWithDiscriminant + { return new UnionWithDiscriminant([ 'type' => 'bar', 'value' => $bar, @@ -72,61 +74,67 @@ public static function bar(Bar $bar): UnionWithDiscriminant { /** * @return bool */ - public function isFoo(): bool { - return $this->value instanceof Foo&& $this->type === 'foo'; + public function isFoo(): bool + { + return $this->value instanceof Foo && $this->type === 'foo'; } /** * @return Foo */ - public function asFoo(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo')){ + public function asFoo(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo')) { throw new Exception( "Expected foo; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isBar(): bool { - return $this->value instanceof Bar&& $this->type === 'bar'; + public function isBar(): bool + { + return $this->value instanceof Bar && $this->type === 'bar'; } /** * @return Bar */ - public function asBar(): Bar { - if (!($this->value instanceof Bar&& $this->type === 'bar')){ + public function asBar(): Bar + { + if (!($this->value instanceof Bar && $this->type === 'bar')) { throw new Exception( "Expected bar; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['_type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'foo': $value = $this->asFoo()->jsonSerialize(); $result['foo'] = $value; @@ -137,26 +145,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -165,30 +174,31 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('_type', $data)){ + if (!array_key_exists('_type', $data)) { throw new Exception( "JSON data is missing property '_type'", ); } $type = $data['_type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['_type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'foo': - if (!array_key_exists('foo', $data)){ + if (!array_key_exists('foo', $data)) { throw new Exception( "JSON data is missing property 'foo'", ); } - - if (!(is_array($data['foo']))){ + + if (!(is_array($data['foo']))) { throw new Exception( "Expected property 'foo' in JSON data to be array, instead received " . get_debug_type($data['foo']), ); @@ -196,13 +206,13 @@ public static function jsonDeserialize(array $data): static { $args['value'] = Foo::jsonDeserialize($data['foo']); break; case 'bar': - if (!array_key_exists('bar', $data)){ + if (!array_key_exists('bar', $data)) { throw new Exception( "JSON data is missing property 'bar'", ); } - - if (!(is_array($data['bar']))){ + + if (!(is_array($data['bar']))) { throw new Exception( "Expected property 'bar' in JSON data to be array, instead received " . get_debug_type($data['bar']), ); @@ -214,7 +224,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithDuplicatePrimitive.php b/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithDuplicatePrimitive.php index 8b961b8a5f46..188842b28131 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithDuplicatePrimitive.php +++ b/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithDuplicatePrimitive.php @@ -46,16 +46,17 @@ class UnionWithDuplicatePrimitive extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param int $integer1 * @return UnionWithDuplicatePrimitive */ - public static function integer1(int $integer1): UnionWithDuplicatePrimitive { + public static function integer1(int $integer1): UnionWithDuplicatePrimitive + { return new UnionWithDuplicatePrimitive([ 'type' => 'integer1', 'value' => $integer1, @@ -66,7 +67,8 @@ public static function integer1(int $integer1): UnionWithDuplicatePrimitive { * @param int $integer2 * @return UnionWithDuplicatePrimitive */ - public static function integer2(int $integer2): UnionWithDuplicatePrimitive { + public static function integer2(int $integer2): UnionWithDuplicatePrimitive + { return new UnionWithDuplicatePrimitive([ 'type' => 'integer2', 'value' => $integer2, @@ -77,7 +79,8 @@ public static function integer2(int $integer2): UnionWithDuplicatePrimitive { * @param string $string1 * @return UnionWithDuplicatePrimitive */ - public static function string1(string $string1): UnionWithDuplicatePrimitive { + public static function string1(string $string1): UnionWithDuplicatePrimitive + { return new UnionWithDuplicatePrimitive([ 'type' => 'string1', 'value' => $string1, @@ -88,7 +91,8 @@ public static function string1(string $string1): UnionWithDuplicatePrimitive { * @param string $string2 * @return UnionWithDuplicatePrimitive */ - public static function string2(string $string2): UnionWithDuplicatePrimitive { + public static function string2(string $string2): UnionWithDuplicatePrimitive + { return new UnionWithDuplicatePrimitive([ 'type' => 'string2', 'value' => $string2, @@ -98,101 +102,111 @@ public static function string2(string $string2): UnionWithDuplicatePrimitive { /** * @return bool */ - public function isInteger1(): bool { - return is_int($this->value)&& $this->type === 'integer1'; + public function isInteger1(): bool + { + return is_int($this->value) && $this->type === 'integer1'; } /** * @return int */ - public function asInteger1(): int { - if (!(is_int($this->value)&& $this->type === 'integer1')){ + public function asInteger1(): int + { + if (!(is_int($this->value) && $this->type === 'integer1')) { throw new Exception( "Expected integer1; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isInteger2(): bool { - return is_int($this->value)&& $this->type === 'integer2'; + public function isInteger2(): bool + { + return is_int($this->value) && $this->type === 'integer2'; } /** * @return int */ - public function asInteger2(): int { - if (!(is_int($this->value)&& $this->type === 'integer2')){ + public function asInteger2(): int + { + if (!(is_int($this->value) && $this->type === 'integer2')) { throw new Exception( "Expected integer2; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isString1(): bool { - return is_string($this->value)&& $this->type === 'string1'; + public function isString1(): bool + { + return is_string($this->value) && $this->type === 'string1'; } /** * @return string */ - public function asString1(): string { - if (!(is_string($this->value)&& $this->type === 'string1')){ + public function asString1(): string + { + if (!(is_string($this->value) && $this->type === 'string1')) { throw new Exception( "Expected string1; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isString2(): bool { - return is_string($this->value)&& $this->type === 'string2'; + public function isString2(): bool + { + return is_string($this->value) && $this->type === 'string2'; } /** * @return string */ - public function asString2(): string { - if (!(is_string($this->value)&& $this->type === 'string2')){ + public function asString2(): string + { + if (!(is_string($this->value) && $this->type === 'string2')) { throw new Exception( "Expected string2; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'integer1': $value = $this->value; $result['integer1'] = $value; @@ -211,26 +225,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -239,56 +254,57 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'integer1': - if (!array_key_exists('integer1', $data)){ + if (!array_key_exists('integer1', $data)) { throw new Exception( "JSON data is missing property 'integer1'", ); } - + $args['value'] = $data['integer1']; break; case 'integer2': - if (!array_key_exists('integer2', $data)){ + if (!array_key_exists('integer2', $data)) { throw new Exception( "JSON data is missing property 'integer2'", ); } - + $args['value'] = $data['integer2']; break; case 'string1': - if (!array_key_exists('string1', $data)){ + if (!array_key_exists('string1', $data)) { throw new Exception( "JSON data is missing property 'string1'", ); } - + $args['value'] = $data['string1']; break; case 'string2': - if (!array_key_exists('string2', $data)){ + if (!array_key_exists('string2', $data)) { throw new Exception( "JSON data is missing property 'string2'", ); } - + $args['value'] = $data['string2']; break; case '_unknown': @@ -296,7 +312,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithDuplicateTypes.php b/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithDuplicateTypes.php index 70b9faeeea5e..84dbbf00e1e5 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithDuplicateTypes.php +++ b/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithDuplicateTypes.php @@ -40,16 +40,17 @@ class UnionWithDuplicateTypes extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param Foo $foo1 * @return UnionWithDuplicateTypes */ - public static function foo1(Foo $foo1): UnionWithDuplicateTypes { + public static function foo1(Foo $foo1): UnionWithDuplicateTypes + { return new UnionWithDuplicateTypes([ 'type' => 'foo1', 'value' => $foo1, @@ -60,7 +61,8 @@ public static function foo1(Foo $foo1): UnionWithDuplicateTypes { * @param Foo $foo2 * @return UnionWithDuplicateTypes */ - public static function foo2(Foo $foo2): UnionWithDuplicateTypes { + public static function foo2(Foo $foo2): UnionWithDuplicateTypes + { return new UnionWithDuplicateTypes([ 'type' => 'foo2', 'value' => $foo2, @@ -70,61 +72,67 @@ public static function foo2(Foo $foo2): UnionWithDuplicateTypes { /** * @return bool */ - public function isFoo1(): bool { - return $this->value instanceof Foo&& $this->type === 'foo1'; + public function isFoo1(): bool + { + return $this->value instanceof Foo && $this->type === 'foo1'; } /** * @return Foo */ - public function asFoo1(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo1')){ + public function asFoo1(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo1')) { throw new Exception( "Expected foo1; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isFoo2(): bool { - return $this->value instanceof Foo&& $this->type === 'foo2'; + public function isFoo2(): bool + { + return $this->value instanceof Foo && $this->type === 'foo2'; } /** * @return Foo */ - public function asFoo2(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo2')){ + public function asFoo2(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo2')) { throw new Exception( "Expected foo2; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'foo1': $value = $this->asFoo1()->jsonSerialize(); $result = array_merge($value, $result); @@ -135,26 +143,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -163,22 +172,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'foo1': $args['value'] = Foo::jsonDeserialize($data); break; @@ -190,7 +200,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithDuplicativeDiscriminants.php b/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithDuplicativeDiscriminants.php index 3fa8212115f3..55cd4f13c1bd 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithDuplicativeDiscriminants.php +++ b/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithDuplicativeDiscriminants.php @@ -42,16 +42,17 @@ class UnionWithDuplicativeDiscriminants extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param FirstItemType $firstItemType * @return UnionWithDuplicativeDiscriminants */ - public static function firstItemType(FirstItemType $firstItemType): UnionWithDuplicativeDiscriminants { + public static function firstItemType(FirstItemType $firstItemType): UnionWithDuplicativeDiscriminants + { return new UnionWithDuplicativeDiscriminants([ 'type' => 'firstItemType', 'value' => $firstItemType, @@ -62,7 +63,8 @@ public static function firstItemType(FirstItemType $firstItemType): UnionWithDup * @param SecondItemType $secondItemType * @return UnionWithDuplicativeDiscriminants */ - public static function secondItemType(SecondItemType $secondItemType): UnionWithDuplicativeDiscriminants { + public static function secondItemType(SecondItemType $secondItemType): UnionWithDuplicativeDiscriminants + { return new UnionWithDuplicativeDiscriminants([ 'type' => 'secondItemType', 'value' => $secondItemType, @@ -72,61 +74,67 @@ public static function secondItemType(SecondItemType $secondItemType): UnionWith /** * @return bool */ - public function isFirstItemType(): bool { - return $this->value instanceof FirstItemType&& $this->type === 'firstItemType'; + public function isFirstItemType(): bool + { + return $this->value instanceof FirstItemType && $this->type === 'firstItemType'; } /** * @return FirstItemType */ - public function asFirstItemType(): FirstItemType { - if (!($this->value instanceof FirstItemType&& $this->type === 'firstItemType')){ + public function asFirstItemType(): FirstItemType + { + if (!($this->value instanceof FirstItemType && $this->type === 'firstItemType')) { throw new Exception( "Expected firstItemType; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isSecondItemType(): bool { - return $this->value instanceof SecondItemType&& $this->type === 'secondItemType'; + public function isSecondItemType(): bool + { + return $this->value instanceof SecondItemType && $this->type === 'secondItemType'; } /** * @return SecondItemType */ - public function asSecondItemType(): SecondItemType { - if (!($this->value instanceof SecondItemType&& $this->type === 'secondItemType')){ + public function asSecondItemType(): SecondItemType + { + if (!($this->value instanceof SecondItemType && $this->type === 'secondItemType')) { throw new Exception( "Expected secondItemType; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'firstItemType': $value = $this->asFirstItemType()->jsonSerialize(); $result = array_merge($value, $result); @@ -137,26 +145,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -165,22 +174,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'firstItemType': $args['value'] = FirstItemType::jsonDeserialize($data); break; @@ -192,7 +202,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithLiteral.php b/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithLiteral.php index 513941ab20e6..233c78542c0f 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithLiteral.php +++ b/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithLiteral.php @@ -46,9 +46,10 @@ class UnionWithLiteral extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->base = $values['base'];$this->type = $values['type'];$this->value = $values['value']; + ) { + $this->base = $values['base']; + $this->type = $values['type']; + $this->value = $values['value']; } /** @@ -56,7 +57,8 @@ private function __construct( * @param 'fern' $fern * @return UnionWithLiteral */ - public static function fern(string $base, string $fern): UnionWithLiteral { + public static function fern(string $base, string $fern): UnionWithLiteral + { return new UnionWithLiteral([ 'base' => $base, 'type' => 'fern', @@ -67,67 +69,72 @@ public static function fern(string $base, string $fern): UnionWithLiteral { /** * @return bool */ - public function isFern(): bool { - return $this->value === 'fern'&& $this->type === 'fern'; + public function isFern(): bool + { + return $this->value === 'fern' && $this->type === 'fern'; } /** * @return 'fern' */ - public function asFern(): string { - if (!($this->value === 'fern'&& $this->type === 'fern')){ + public function asFern(): string + { + if (!($this->value === 'fern' && $this->type === 'fern')) { throw new Exception( "Expected fern; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'fern': $value = $this->value; $result['fern'] = $value; break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -136,41 +143,42 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('base', $data)){ + if (!array_key_exists('base', $data)) { throw new Exception( "JSON data is missing property 'base'", ); } - if (!($data['base'] === 'base')){ + if (!($data['base'] === 'base')) { throw new Exception( "Expected property 'base' in JSON data to be 'base', instead received " . get_debug_type($data['base']), ); } $args['base'] = $data['base']; - - if (!array_key_exists('type', $data)){ + + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'fern': - if (!array_key_exists('fern', $data)){ + if (!array_key_exists('fern', $data)) { throw new Exception( "JSON data is missing property 'fern'", ); } - + $args['value'] = $data['fern']; break; case '_unknown': @@ -178,7 +186,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithMultipleNoProperties.php b/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithMultipleNoProperties.php index 2436e0ccdf25..351b56a90bfa 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithMultipleNoProperties.php +++ b/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithMultipleNoProperties.php @@ -44,16 +44,17 @@ class UnionWithMultipleNoProperties extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param Foo $foo * @return UnionWithMultipleNoProperties */ - public static function foo(Foo $foo): UnionWithMultipleNoProperties { + public static function foo(Foo $foo): UnionWithMultipleNoProperties + { return new UnionWithMultipleNoProperties([ 'type' => 'foo', 'value' => $foo, @@ -63,7 +64,8 @@ public static function foo(Foo $foo): UnionWithMultipleNoProperties { /** * @return UnionWithMultipleNoProperties */ - public static function empty1(): UnionWithMultipleNoProperties { + public static function empty1(): UnionWithMultipleNoProperties + { return new UnionWithMultipleNoProperties([ 'type' => 'empty1', 'value' => null, @@ -73,7 +75,8 @@ public static function empty1(): UnionWithMultipleNoProperties { /** * @return UnionWithMultipleNoProperties */ - public static function empty2(): UnionWithMultipleNoProperties { + public static function empty2(): UnionWithMultipleNoProperties + { return new UnionWithMultipleNoProperties([ 'type' => 'empty2', 'value' => null, @@ -83,55 +86,61 @@ public static function empty2(): UnionWithMultipleNoProperties { /** * @return bool */ - public function isFoo(): bool { - return $this->value instanceof Foo&& $this->type === 'foo'; + public function isFoo(): bool + { + return $this->value instanceof Foo && $this->type === 'foo'; } /** * @return Foo */ - public function asFoo(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo')){ + public function asFoo(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo')) { throw new Exception( "Expected foo; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isEmpty1(): bool { - return is_null($this->value)&& $this->type === 'empty1'; + public function isEmpty1(): bool + { + return is_null($this->value) && $this->type === 'empty1'; } /** * @return bool */ - public function isEmpty2(): bool { - return is_null($this->value)&& $this->type === 'empty2'; + public function isEmpty2(): bool + { + return is_null($this->value) && $this->type === 'empty2'; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'foo': $value = $this->asFoo()->jsonSerialize(); $result = array_merge($value, $result); @@ -144,26 +153,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -172,22 +182,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'foo': $args['value'] = Foo::jsonDeserialize($data); break; @@ -202,7 +213,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithNoProperties.php b/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithNoProperties.php index 86687f6b6919..025656a3dbbf 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithNoProperties.php +++ b/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithNoProperties.php @@ -42,16 +42,17 @@ class UnionWithNoProperties extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param Foo $foo * @return UnionWithNoProperties */ - public static function foo(Foo $foo): UnionWithNoProperties { + public static function foo(Foo $foo): UnionWithNoProperties + { return new UnionWithNoProperties([ 'type' => 'foo', 'value' => $foo, @@ -61,7 +62,8 @@ public static function foo(Foo $foo): UnionWithNoProperties { /** * @return UnionWithNoProperties */ - public static function empty(): UnionWithNoProperties { + public static function empty(): UnionWithNoProperties + { return new UnionWithNoProperties([ 'type' => 'empty', 'value' => null, @@ -71,48 +73,53 @@ public static function empty(): UnionWithNoProperties { /** * @return bool */ - public function isFoo(): bool { - return $this->value instanceof Foo&& $this->type === 'foo'; + public function isFoo(): bool + { + return $this->value instanceof Foo && $this->type === 'foo'; } /** * @return Foo */ - public function asFoo(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo')){ + public function asFoo(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo')) { throw new Exception( "Expected foo; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isEmpty(): bool { - return is_null($this->value)&& $this->type === 'empty'; + public function isEmpty(): bool + { + return is_null($this->value) && $this->type === 'empty'; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'foo': $value = $this->asFoo()->jsonSerialize(); $result = array_merge($value, $result); @@ -122,26 +129,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -150,22 +158,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'foo': $args['value'] = Foo::jsonDeserialize($data); break; @@ -177,7 +186,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithOptionalTime.php b/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithOptionalTime.php index 0afba7944996..98db6869d002 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithOptionalTime.php +++ b/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithOptionalTime.php @@ -44,16 +44,17 @@ class UnionWithOptionalTime extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param ?DateTime $date * @return UnionWithOptionalTime */ - public static function date(?DateTime $date = null): UnionWithOptionalTime { + public static function date(?DateTime $date = null): UnionWithOptionalTime + { return new UnionWithOptionalTime([ 'type' => 'date', 'value' => $date, @@ -64,7 +65,8 @@ public static function date(?DateTime $date = null): UnionWithOptionalTime { * @param ?DateTime $datetime * @return UnionWithOptionalTime */ - public static function datetime(?DateTime $datetime = null): UnionWithOptionalTime { + public static function datetime(?DateTime $datetime = null): UnionWithOptionalTime + { return new UnionWithOptionalTime([ 'type' => 'datetime', 'value' => $datetime, @@ -74,97 +76,104 @@ public static function datetime(?DateTime $datetime = null): UnionWithOptionalTi /** * @return bool */ - public function isDate(): bool { - return (is_null($this->value) || $this->value instanceof DateTime)&& $this->type === 'date'; + public function isDate(): bool + { + return (is_null($this->value) || $this->value instanceof DateTime) && $this->type === 'date'; } /** * @return ?DateTime */ - public function asDate(): ?DateTime { - if (!((is_null($this->value) || $this->value instanceof DateTime)&& $this->type === 'date')){ + public function asDate(): ?DateTime + { + if (!((is_null($this->value) || $this->value instanceof DateTime) && $this->type === 'date')) { throw new Exception( "Expected date; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isDatetime(): bool { - return (is_null($this->value) || $this->value instanceof DateTime)&& $this->type === 'datetime'; + public function isDatetime(): bool + { + return (is_null($this->value) || $this->value instanceof DateTime) && $this->type === 'datetime'; } /** * @return ?DateTime */ - public function asDatetime(): ?DateTime { - if (!((is_null($this->value) || $this->value instanceof DateTime)&& $this->type === 'datetime')){ + public function asDatetime(): ?DateTime + { + if (!((is_null($this->value) || $this->value instanceof DateTime) && $this->type === 'datetime')) { throw new Exception( "Expected datetime; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'date': $value = $this->asDate(); - if (!is_null($value)){ + if (!is_null($value)) { $value = JsonSerializer::serializeDate($value); } $result['date'] = $value; break; case 'datetime': $value = $this->asDatetime(); - if (!is_null($value)){ + if (!is_null($value)) { $value = JsonSerializer::serializeDateTime($value); } $result['datetime'] = $value; break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -173,38 +182,39 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'date': - if (!array_key_exists('date', $data)){ + if (!array_key_exists('date', $data)) { throw new Exception( "JSON data is missing property 'date'", ); } - + $args['value'] = $data['date']; break; case 'datetime': - if (!array_key_exists('datetime', $data)){ + if (!array_key_exists('datetime', $data)) { throw new Exception( "JSON data is missing property 'datetime'", ); } - + $args['value'] = $data['datetime']; break; case '_unknown': @@ -212,7 +222,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithPrimitive.php b/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithPrimitive.php index 9a6c3bd8a564..290460149239 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithPrimitive.php +++ b/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithPrimitive.php @@ -42,16 +42,17 @@ class UnionWithPrimitive extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param int $integer * @return UnionWithPrimitive */ - public static function integer(int $integer): UnionWithPrimitive { + public static function integer(int $integer): UnionWithPrimitive + { return new UnionWithPrimitive([ 'type' => 'integer', 'value' => $integer, @@ -62,7 +63,8 @@ public static function integer(int $integer): UnionWithPrimitive { * @param string $string * @return UnionWithPrimitive */ - public static function string(string $string): UnionWithPrimitive { + public static function string(string $string): UnionWithPrimitive + { return new UnionWithPrimitive([ 'type' => 'string', 'value' => $string, @@ -72,61 +74,67 @@ public static function string(string $string): UnionWithPrimitive { /** * @return bool */ - public function isInteger(): bool { - return is_int($this->value)&& $this->type === 'integer'; + public function isInteger(): bool + { + return is_int($this->value) && $this->type === 'integer'; } /** * @return int */ - public function asInteger(): int { - if (!(is_int($this->value)&& $this->type === 'integer')){ + public function asInteger(): int + { + if (!(is_int($this->value) && $this->type === 'integer')) { throw new Exception( "Expected integer; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isString(): bool { - return is_string($this->value)&& $this->type === 'string'; + public function isString(): bool + { + return is_string($this->value) && $this->type === 'string'; } /** * @return string */ - public function asString(): string { - if (!(is_string($this->value)&& $this->type === 'string')){ + public function asString(): string + { + if (!(is_string($this->value) && $this->type === 'string')) { throw new Exception( "Expected string; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'integer': $value = $this->value; $result['integer'] = $value; @@ -137,26 +145,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -165,38 +174,39 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'integer': - if (!array_key_exists('integer', $data)){ + if (!array_key_exists('integer', $data)) { throw new Exception( "JSON data is missing property 'integer'", ); } - + $args['value'] = $data['integer']; break; case 'string': - if (!array_key_exists('string', $data)){ + if (!array_key_exists('string', $data)) { throw new Exception( "JSON data is missing property 'string'", ); } - + $args['value'] = $data['string']; break; case '_unknown': @@ -204,7 +214,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithSameNumberTypes.php b/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithSameNumberTypes.php index 59337d67d063..6deb07c36c12 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithSameNumberTypes.php +++ b/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithSameNumberTypes.php @@ -44,16 +44,17 @@ class UnionWithSameNumberTypes extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param int $positiveInt * @return UnionWithSameNumberTypes */ - public static function positiveInt(int $positiveInt): UnionWithSameNumberTypes { + public static function positiveInt(int $positiveInt): UnionWithSameNumberTypes + { return new UnionWithSameNumberTypes([ 'type' => 'positiveInt', 'value' => $positiveInt, @@ -64,7 +65,8 @@ public static function positiveInt(int $positiveInt): UnionWithSameNumberTypes { * @param int $negativeInt * @return UnionWithSameNumberTypes */ - public static function negativeInt(int $negativeInt): UnionWithSameNumberTypes { + public static function negativeInt(int $negativeInt): UnionWithSameNumberTypes + { return new UnionWithSameNumberTypes([ 'type' => 'negativeInt', 'value' => $negativeInt, @@ -75,7 +77,8 @@ public static function negativeInt(int $negativeInt): UnionWithSameNumberTypes { * @param float $anyNumber * @return UnionWithSameNumberTypes */ - public static function anyNumber(float $anyNumber): UnionWithSameNumberTypes { + public static function anyNumber(float $anyNumber): UnionWithSameNumberTypes + { return new UnionWithSameNumberTypes([ 'type' => 'anyNumber', 'value' => $anyNumber, @@ -85,81 +88,89 @@ public static function anyNumber(float $anyNumber): UnionWithSameNumberTypes { /** * @return bool */ - public function isPositiveInt(): bool { - return is_int($this->value)&& $this->type === 'positiveInt'; + public function isPositiveInt(): bool + { + return is_int($this->value) && $this->type === 'positiveInt'; } /** * @return int */ - public function asPositiveInt(): int { - if (!(is_int($this->value)&& $this->type === 'positiveInt')){ + public function asPositiveInt(): int + { + if (!(is_int($this->value) && $this->type === 'positiveInt')) { throw new Exception( "Expected positiveInt; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isNegativeInt(): bool { - return is_int($this->value)&& $this->type === 'negativeInt'; + public function isNegativeInt(): bool + { + return is_int($this->value) && $this->type === 'negativeInt'; } /** * @return int */ - public function asNegativeInt(): int { - if (!(is_int($this->value)&& $this->type === 'negativeInt')){ + public function asNegativeInt(): int + { + if (!(is_int($this->value) && $this->type === 'negativeInt')) { throw new Exception( "Expected negativeInt; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isAnyNumber(): bool { - return is_float($this->value)&& $this->type === 'anyNumber'; + public function isAnyNumber(): bool + { + return is_float($this->value) && $this->type === 'anyNumber'; } /** * @return float */ - public function asAnyNumber(): float { - if (!(is_float($this->value)&& $this->type === 'anyNumber')){ + public function asAnyNumber(): float + { + if (!(is_float($this->value) && $this->type === 'anyNumber')) { throw new Exception( "Expected anyNumber; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'positiveInt': $value = $this->value; $result['positiveInt'] = $value; @@ -174,26 +185,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -202,47 +214,48 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'positiveInt': - if (!array_key_exists('positiveInt', $data)){ + if (!array_key_exists('positiveInt', $data)) { throw new Exception( "JSON data is missing property 'positiveInt'", ); } - + $args['value'] = $data['positiveInt']; break; case 'negativeInt': - if (!array_key_exists('negativeInt', $data)){ + if (!array_key_exists('negativeInt', $data)) { throw new Exception( "JSON data is missing property 'negativeInt'", ); } - + $args['value'] = $data['negativeInt']; break; case 'anyNumber': - if (!array_key_exists('anyNumber', $data)){ + if (!array_key_exists('anyNumber', $data)) { throw new Exception( "JSON data is missing property 'anyNumber'", ); } - + $args['value'] = $data['anyNumber']; break; case '_unknown': @@ -250,7 +263,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithSameStringTypes.php b/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithSameStringTypes.php index abd175a25929..268ca8d3c7e9 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithSameStringTypes.php +++ b/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithSameStringTypes.php @@ -42,16 +42,17 @@ class UnionWithSameStringTypes extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param string $customFormat * @return UnionWithSameStringTypes */ - public static function customFormat(string $customFormat): UnionWithSameStringTypes { + public static function customFormat(string $customFormat): UnionWithSameStringTypes + { return new UnionWithSameStringTypes([ 'type' => 'customFormat', 'value' => $customFormat, @@ -62,7 +63,8 @@ public static function customFormat(string $customFormat): UnionWithSameStringTy * @param string $regularString * @return UnionWithSameStringTypes */ - public static function regularString(string $regularString): UnionWithSameStringTypes { + public static function regularString(string $regularString): UnionWithSameStringTypes + { return new UnionWithSameStringTypes([ 'type' => 'regularString', 'value' => $regularString, @@ -73,7 +75,8 @@ public static function regularString(string $regularString): UnionWithSameString * @param string $patternString * @return UnionWithSameStringTypes */ - public static function patternString(string $patternString): UnionWithSameStringTypes { + public static function patternString(string $patternString): UnionWithSameStringTypes + { return new UnionWithSameStringTypes([ 'type' => 'patternString', 'value' => $patternString, @@ -83,81 +86,89 @@ public static function patternString(string $patternString): UnionWithSameString /** * @return bool */ - public function isCustomFormat(): bool { - return is_string($this->value)&& $this->type === 'customFormat'; + public function isCustomFormat(): bool + { + return is_string($this->value) && $this->type === 'customFormat'; } /** * @return string */ - public function asCustomFormat(): string { - if (!(is_string($this->value)&& $this->type === 'customFormat')){ + public function asCustomFormat(): string + { + if (!(is_string($this->value) && $this->type === 'customFormat')) { throw new Exception( "Expected customFormat; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isRegularString(): bool { - return is_string($this->value)&& $this->type === 'regularString'; + public function isRegularString(): bool + { + return is_string($this->value) && $this->type === 'regularString'; } /** * @return string */ - public function asRegularString(): string { - if (!(is_string($this->value)&& $this->type === 'regularString')){ + public function asRegularString(): string + { + if (!(is_string($this->value) && $this->type === 'regularString')) { throw new Exception( "Expected regularString; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isPatternString(): bool { - return is_string($this->value)&& $this->type === 'patternString'; + public function isPatternString(): bool + { + return is_string($this->value) && $this->type === 'patternString'; } /** * @return string */ - public function asPatternString(): string { - if (!(is_string($this->value)&& $this->type === 'patternString')){ + public function asPatternString(): string + { + if (!(is_string($this->value) && $this->type === 'patternString')) { throw new Exception( "Expected patternString; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'customFormat': $value = $this->value; $result['customFormat'] = $value; @@ -172,26 +183,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -200,47 +212,48 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'customFormat': - if (!array_key_exists('customFormat', $data)){ + if (!array_key_exists('customFormat', $data)) { throw new Exception( "JSON data is missing property 'customFormat'", ); } - + $args['value'] = $data['customFormat']; break; case 'regularString': - if (!array_key_exists('regularString', $data)){ + if (!array_key_exists('regularString', $data)) { throw new Exception( "JSON data is missing property 'regularString'", ); } - + $args['value'] = $data['regularString']; break; case 'patternString': - if (!array_key_exists('patternString', $data)){ + if (!array_key_exists('patternString', $data)) { throw new Exception( "JSON data is missing property 'patternString'", ); } - + $args['value'] = $data['patternString']; break; case '_unknown': @@ -248,7 +261,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithSingleElement.php b/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithSingleElement.php index 086b28c04ba8..63cb0e427d92 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithSingleElement.php +++ b/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithSingleElement.php @@ -38,16 +38,17 @@ class UnionWithSingleElement extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param Foo $foo * @return UnionWithSingleElement */ - public static function foo(Foo $foo): UnionWithSingleElement { + public static function foo(Foo $foo): UnionWithSingleElement + { return new UnionWithSingleElement([ 'type' => 'foo', 'value' => $foo, @@ -57,67 +58,72 @@ public static function foo(Foo $foo): UnionWithSingleElement { /** * @return bool */ - public function isFoo(): bool { - return $this->value instanceof Foo&& $this->type === 'foo'; + public function isFoo(): bool + { + return $this->value instanceof Foo && $this->type === 'foo'; } /** * @return Foo */ - public function asFoo(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo')){ + public function asFoo(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo')) { throw new Exception( "Expected foo; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'foo': $value = $this->asFoo()->jsonSerialize(); $result = array_merge($value, $result); break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -126,22 +132,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'foo': $args['value'] = Foo::jsonDeserialize($data); break; @@ -150,7 +157,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithSubTypes.php b/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithSubTypes.php index 09211c87b0db..f3b99d8e2bcb 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithSubTypes.php +++ b/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithSubTypes.php @@ -42,16 +42,17 @@ class UnionWithSubTypes extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param Foo $foo * @return UnionWithSubTypes */ - public static function foo(Foo $foo): UnionWithSubTypes { + public static function foo(Foo $foo): UnionWithSubTypes + { return new UnionWithSubTypes([ 'type' => 'foo', 'value' => $foo, @@ -62,7 +63,8 @@ public static function foo(Foo $foo): UnionWithSubTypes { * @param FooExtended $fooExtended * @return UnionWithSubTypes */ - public static function fooExtended(FooExtended $fooExtended): UnionWithSubTypes { + public static function fooExtended(FooExtended $fooExtended): UnionWithSubTypes + { return new UnionWithSubTypes([ 'type' => 'fooExtended', 'value' => $fooExtended, @@ -72,61 +74,67 @@ public static function fooExtended(FooExtended $fooExtended): UnionWithSubTypes /** * @return bool */ - public function isFoo(): bool { - return $this->value instanceof Foo&& $this->type === 'foo'; + public function isFoo(): bool + { + return $this->value instanceof Foo && $this->type === 'foo'; } /** * @return Foo */ - public function asFoo(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo')){ + public function asFoo(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo')) { throw new Exception( "Expected foo; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isFooExtended(): bool { - return $this->value instanceof FooExtended&& $this->type === 'fooExtended'; + public function isFooExtended(): bool + { + return $this->value instanceof FooExtended && $this->type === 'fooExtended'; } /** * @return FooExtended */ - public function asFooExtended(): FooExtended { - if (!($this->value instanceof FooExtended&& $this->type === 'fooExtended')){ + public function asFooExtended(): FooExtended + { + if (!($this->value instanceof FooExtended && $this->type === 'fooExtended')) { throw new Exception( "Expected fooExtended; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'foo': $value = $this->asFoo()->jsonSerialize(); $result = array_merge($value, $result); @@ -137,26 +145,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -165,22 +174,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'foo': $args['value'] = Foo::jsonDeserialize($data); break; @@ -192,7 +202,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithTime.php b/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithTime.php index e91e58c6c3ee..7f6376e49553 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithTime.php +++ b/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithTime.php @@ -46,16 +46,17 @@ class UnionWithTime extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param int $value * @return UnionWithTime */ - public static function value(int $value): UnionWithTime { + public static function value(int $value): UnionWithTime + { return new UnionWithTime([ 'type' => 'value', 'value' => $value, @@ -66,7 +67,8 @@ public static function value(int $value): UnionWithTime { * @param DateTime $date * @return UnionWithTime */ - public static function date(DateTime $date): UnionWithTime { + public static function date(DateTime $date): UnionWithTime + { return new UnionWithTime([ 'type' => 'date', 'value' => $date, @@ -77,7 +79,8 @@ public static function date(DateTime $date): UnionWithTime { * @param DateTime $datetime * @return UnionWithTime */ - public static function datetime(DateTime $datetime): UnionWithTime { + public static function datetime(DateTime $datetime): UnionWithTime + { return new UnionWithTime([ 'type' => 'datetime', 'value' => $datetime, @@ -87,81 +90,89 @@ public static function datetime(DateTime $datetime): UnionWithTime { /** * @return bool */ - public function isValue(): bool { - return is_int($this->value)&& $this->type === 'value'; + public function isValue(): bool + { + return is_int($this->value) && $this->type === 'value'; } /** * @return int */ - public function asValue(): int { - if (!(is_int($this->value)&& $this->type === 'value')){ + public function asValue(): int + { + if (!(is_int($this->value) && $this->type === 'value')) { throw new Exception( "Expected value; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isDate(): bool { - return $this->value instanceof DateTime&& $this->type === 'date'; + public function isDate(): bool + { + return $this->value instanceof DateTime && $this->type === 'date'; } /** * @return DateTime */ - public function asDate(): DateTime { - if (!($this->value instanceof DateTime&& $this->type === 'date')){ + public function asDate(): DateTime + { + if (!($this->value instanceof DateTime && $this->type === 'date')) { throw new Exception( "Expected date; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isDatetime(): bool { - return $this->value instanceof DateTime&& $this->type === 'datetime'; + public function isDatetime(): bool + { + return $this->value instanceof DateTime && $this->type === 'datetime'; } /** * @return DateTime */ - public function asDatetime(): DateTime { - if (!($this->value instanceof DateTime&& $this->type === 'datetime')){ + public function asDatetime(): DateTime + { + if (!($this->value instanceof DateTime && $this->type === 'datetime')) { throw new Exception( "Expected datetime; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'value': $value = $this->value; $result['value'] = $value; @@ -176,26 +187,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -204,47 +216,48 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'value': - if (!array_key_exists('value', $data)){ + if (!array_key_exists('value', $data)) { throw new Exception( "JSON data is missing property 'value'", ); } - + $args['value'] = $data['value']; break; case 'date': - if (!array_key_exists('date', $data)){ + if (!array_key_exists('date', $data)) { throw new Exception( "JSON data is missing property 'date'", ); } - + $args['value'] = $data['date']; break; case 'datetime': - if (!array_key_exists('datetime', $data)){ + if (!array_key_exists('datetime', $data)) { throw new Exception( "JSON data is missing property 'datetime'", ); } - + $args['value'] = $data['datetime']; break; case '_unknown': @@ -252,7 +265,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithoutKey.php b/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithoutKey.php index 1fb2cf1a5032..a91c9824095e 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithoutKey.php +++ b/seed/php-sdk/unions/no-custom-config/src/Types/Types/UnionWithoutKey.php @@ -42,16 +42,17 @@ class UnionWithoutKey extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** * @param Foo $foo * @return UnionWithoutKey */ - public static function foo(Foo $foo): UnionWithoutKey { + public static function foo(Foo $foo): UnionWithoutKey + { return new UnionWithoutKey([ 'type' => 'foo', 'value' => $foo, @@ -62,7 +63,8 @@ public static function foo(Foo $foo): UnionWithoutKey { * @param Bar $bar * @return UnionWithoutKey */ - public static function bar(Bar $bar): UnionWithoutKey { + public static function bar(Bar $bar): UnionWithoutKey + { return new UnionWithoutKey([ 'type' => 'bar', 'value' => $bar, @@ -72,61 +74,67 @@ public static function bar(Bar $bar): UnionWithoutKey { /** * @return bool */ - public function isFoo(): bool { - return $this->value instanceof Foo&& $this->type === 'foo'; + public function isFoo(): bool + { + return $this->value instanceof Foo && $this->type === 'foo'; } /** * @return Foo */ - public function asFoo(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo')){ + public function asFoo(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo')) { throw new Exception( "Expected foo; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isBar(): bool { - return $this->value instanceof Bar&& $this->type === 'bar'; + public function isBar(): bool + { + return $this->value instanceof Bar && $this->type === 'bar'; } /** * @return Bar */ - public function asBar(): Bar { - if (!($this->value instanceof Bar&& $this->type === 'bar')){ + public function asBar(): Bar + { + if (!($this->value instanceof Bar && $this->type === 'bar')) { throw new Exception( "Expected bar; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'foo': $value = $this->asFoo()->jsonSerialize(); $result = array_merge($value, $result); @@ -137,26 +145,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -165,22 +174,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'foo': $args['value'] = Foo::jsonDeserialize($data); break; @@ -192,7 +202,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions/no-custom-config/src/Union/Types/Circle.php b/seed/php-sdk/unions/no-custom-config/src/Union/Types/Circle.php index c25a01408fa1..ee064d222ca2 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Union/Types/Circle.php +++ b/seed/php-sdk/unions/no-custom-config/src/Union/Types/Circle.php @@ -20,15 +20,15 @@ class Circle extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->radius = $values['radius']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/no-custom-config/src/Union/Types/GetShapeRequest.php b/seed/php-sdk/unions/no-custom-config/src/Union/Types/GetShapeRequest.php index a4ca1c0cdc41..5936f8fc45c1 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Union/Types/GetShapeRequest.php +++ b/seed/php-sdk/unions/no-custom-config/src/Union/Types/GetShapeRequest.php @@ -20,15 +20,15 @@ class GetShapeRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->id = $values['id']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/no-custom-config/src/Union/Types/Shape.php b/seed/php-sdk/unions/no-custom-config/src/Union/Types/Shape.php index cb7668687112..b02c60769705 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Union/Types/Shape.php +++ b/seed/php-sdk/unions/no-custom-config/src/Union/Types/Shape.php @@ -50,9 +50,10 @@ class Shape extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->id = $values['id'];$this->type = $values['type'];$this->value = $values['value']; + ) { + $this->id = $values['id']; + $this->type = $values['type']; + $this->value = $values['value']; } /** @@ -60,7 +61,8 @@ private function __construct( * @param Circle $circle * @return Shape */ - public static function circle(string $id, Circle $circle): Shape { + public static function circle(string $id, Circle $circle): Shape + { return new Shape([ 'id' => $id, 'type' => 'circle', @@ -73,7 +75,8 @@ public static function circle(string $id, Circle $circle): Shape { * @param Square $square * @return Shape */ - public static function square(string $id, Square $square): Shape { + public static function square(string $id, Square $square): Shape + { return new Shape([ 'id' => $id, 'type' => 'square', @@ -84,61 +87,67 @@ public static function square(string $id, Square $square): Shape { /** * @return bool */ - public function isCircle(): bool { - return $this->value instanceof Circle&& $this->type === 'circle'; + public function isCircle(): bool + { + return $this->value instanceof Circle && $this->type === 'circle'; } /** * @return Circle */ - public function asCircle(): Circle { - if (!($this->value instanceof Circle&& $this->type === 'circle')){ + public function asCircle(): Circle + { + if (!($this->value instanceof Circle && $this->type === 'circle')) { throw new Exception( "Expected circle; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isSquare(): bool { - return $this->value instanceof Square&& $this->type === 'square'; + public function isSquare(): bool + { + return $this->value instanceof Square && $this->type === 'square'; } /** * @return Square */ - public function asSquare(): Square { - if (!($this->value instanceof Square&& $this->type === 'square')){ + public function asSquare(): Square + { + if (!($this->value instanceof Square && $this->type === 'square')) { throw new Exception( "Expected square; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'circle': $value = $this->asCircle()->jsonSerialize(); $result = array_merge($value, $result); @@ -149,26 +158,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -177,34 +187,35 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('id', $data)){ + if (!array_key_exists('id', $data)) { throw new Exception( "JSON data is missing property 'id'", ); } - if (!(is_string($data['id']))){ + if (!(is_string($data['id']))) { throw new Exception( "Expected property 'id' in JSON data to be string, instead received " . get_debug_type($data['id']), ); } $args['id'] = $data['id']; - - if (!array_key_exists('type', $data)){ + + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'circle': $args['value'] = Circle::jsonDeserialize($data); break; @@ -216,7 +227,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions/no-custom-config/src/Union/Types/Square.php b/seed/php-sdk/unions/no-custom-config/src/Union/Types/Square.php index b7dd2a8de985..0385f38f0143 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Union/Types/Square.php +++ b/seed/php-sdk/unions/no-custom-config/src/Union/Types/Square.php @@ -20,15 +20,15 @@ class Square extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->length = $values['length']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/no-custom-config/src/Union/Types/WithName.php b/seed/php-sdk/unions/no-custom-config/src/Union/Types/WithName.php index b1629d2b71ad..109ff5d2952f 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Union/Types/WithName.php +++ b/seed/php-sdk/unions/no-custom-config/src/Union/Types/WithName.php @@ -20,15 +20,15 @@ class WithName extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/no-custom-config/src/Union/UnionClient.php b/seed/php-sdk/unions/no-custom-config/src/Union/UnionClient.php index 4f27c965e752..baacf1c699f3 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Union/UnionClient.php +++ b/seed/php-sdk/unions/no-custom-config/src/Union/UnionClient.php @@ -14,7 +14,7 @@ use Psr\Http\Client\ClientExceptionInterface; use Seed\Core\Json\JsonDecoder; -class UnionClient +class UnionClient { /** * @var array{ @@ -42,11 +42,10 @@ class UnionClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -65,7 +64,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function get(string $id, ?array $options = null): Shape { + public function get(string $id, ?array $options = null): Shape + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -77,15 +77,15 @@ public function get(string $id, ?array $options = null): Shape { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return Shape::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -117,7 +117,8 @@ public function get(string $id, ?array $options = null): Shape { * @throws SeedException * @throws SeedApiException */ - public function update(Shape $request, ?array $options = null): bool { + public function update(Shape $request, ?array $options = null): bool + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -130,15 +131,15 @@ public function update(Shape $request, ?array $options = null): bool { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeBool($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/unions/no-custom-config/src/Utils/File.php b/seed/php-sdk/unions/no-custom-config/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/unions/no-custom-config/src/Utils/File.php +++ b/seed/php-sdk/unions/no-custom-config/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/unions/no-custom-config/tests/Core/Client/RawClientTest.php b/seed/php-sdk/unions/no-custom-config/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/unions/no-custom-config/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/unions/no-custom-config/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/unions/no-custom-config/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/unions/no-custom-config/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/unions/no-custom-config/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/unions/no-custom-config/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/unions/no-custom-config/tests/Core/Json/EnumTest.php b/seed/php-sdk/unions/no-custom-config/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/unions/no-custom-config/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/unions/no-custom-config/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/unions/no-custom-config/tests/Core/Json/TraitTest.php b/seed/php-sdk/unions/no-custom-config/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/unions/no-custom-config/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/unions/no-custom-config/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/unions/no-custom-config/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/unions/no-custom-config/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/unions/no-custom-config/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/unions/no-custom-config/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/unions/property-accessors/src/Bigunion/BigunionClient.php b/seed/php-sdk/unions/property-accessors/src/Bigunion/BigunionClient.php index 25224149db51..fd1ab6f28ef3 100644 --- a/seed/php-sdk/unions/property-accessors/src/Bigunion/BigunionClient.php +++ b/seed/php-sdk/unions/property-accessors/src/Bigunion/BigunionClient.php @@ -15,7 +15,7 @@ use Seed\Core\Json\JsonDecoder; use Seed\Core\Json\JsonSerializer; -class BigunionClient +class BigunionClient { /** * @var array{ @@ -43,11 +43,10 @@ class BigunionClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -66,7 +65,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function get(string $id, ?array $options = null): BigUnion { + public function get(string $id, ?array $options = null): BigUnion + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -78,15 +78,15 @@ public function get(string $id, ?array $options = null): BigUnion { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return BigUnion::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -118,7 +118,8 @@ public function get(string $id, ?array $options = null): BigUnion { * @throws SeedException * @throws SeedApiException */ - public function update(BigUnion $request, ?array $options = null): bool { + public function update(BigUnion $request, ?array $options = null): bool + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -131,15 +132,15 @@ public function update(BigUnion $request, ?array $options = null): bool { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeBool($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -171,7 +172,8 @@ public function update(BigUnion $request, ?array $options = null): bool { * @throws SeedException * @throws SeedApiException */ - public function updateMany(array $request, ?array $options = null): array { + public function updateMany(array $request, ?array $options = null): array + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -184,15 +186,15 @@ public function updateMany(array $request, ?array $options = null): array { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, ['string' => 'bool']); // @phpstan-ignore-line } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/ActiveDiamond.php b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/ActiveDiamond.php index 8b30b512c0fa..e658dbb2b829 100644 --- a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/ActiveDiamond.php +++ b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/ActiveDiamond.php @@ -20,27 +20,32 @@ class ActiveDiamond extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function getValue(): string { - return $this->value;} + public function getValue(): string + { + return $this->value; + } /** * @param string $value */ - public function setValue(string $value): self { - $this->value = $value;return $this;} + public function setValue(string $value): self + { + $this->value = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/AttractiveScript.php b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/AttractiveScript.php index ece6844d5808..c7a8c1ddefbd 100644 --- a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/AttractiveScript.php +++ b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/AttractiveScript.php @@ -20,27 +20,32 @@ class AttractiveScript extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function getValue(): string { - return $this->value;} + public function getValue(): string + { + return $this->value; + } /** * @param string $value */ - public function setValue(string $value): self { - $this->value = $value;return $this;} + public function setValue(string $value): self + { + $this->value = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/BigUnion.php b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/BigUnion.php index cf27a60a436b..0a5955cd90fc 100644 --- a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/BigUnion.php +++ b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/BigUnion.php @@ -174,46 +174,64 @@ class BigUnion extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->id = $values['id'];$this->createdAt = $values['createdAt'];$this->archivedAt = $values['archivedAt'] ?? null;$this->type = $values['type'];$this->value = $values['value']; + ) { + $this->id = $values['id']; + $this->createdAt = $values['createdAt']; + $this->archivedAt = $values['archivedAt'] ?? null; + $this->type = $values['type']; + $this->value = $values['value']; } /** * @return string */ - public function getId(): string { - return $this->id;} + public function getId(): string + { + return $this->id; + } /** * @param string $value */ - public function setId(string $value): self { - $this->id = $value;return $this;} + public function setId(string $value): self + { + $this->id = $value; + return $this; + } /** * @return DateTime */ - public function getCreatedAt(): DateTime { - return $this->createdAt;} + public function getCreatedAt(): DateTime + { + return $this->createdAt; + } /** * @param DateTime $value */ - public function setCreatedAt(DateTime $value): self { - $this->createdAt = $value;return $this;} + public function setCreatedAt(DateTime $value): self + { + $this->createdAt = $value; + return $this; + } /** * @return ?DateTime */ - public function getArchivedAt(): ?DateTime { - return $this->archivedAt;} + public function getArchivedAt(): ?DateTime + { + return $this->archivedAt; + } /** * @param ?DateTime $value */ - public function setArchivedAt(?DateTime $value = null): self { - $this->archivedAt = $value;return $this;} + public function setArchivedAt(?DateTime $value = null): self + { + $this->archivedAt = $value; + return $this; + } /** * @return ( @@ -249,8 +267,10 @@ public function setArchivedAt(?DateTime $value = null): self { * |'_unknown' * ) */ - public function getType(): string { - return $this->type;} + public function getType(): string + { + return $this->type; + } /** * @return ( @@ -286,8 +306,10 @@ public function getType(): string { * |mixed * ) */ - public function getValue(): mixed { - return $this->value;} + public function getValue(): mixed + { + return $this->value; + } /** * @param string $id @@ -296,7 +318,8 @@ public function getValue(): mixed { * @param NormalSweet $normalSweet * @return BigUnion */ - public static function normalSweet(string $id, DateTime $createdAt, NormalSweet $normalSweet, ?DateTime $archivedAt = null): BigUnion { + public static function normalSweet(string $id, DateTime $createdAt, NormalSweet $normalSweet, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -313,7 +336,8 @@ public static function normalSweet(string $id, DateTime $createdAt, NormalSweet * @param ThankfulFactor $thankfulFactor * @return BigUnion */ - public static function thankfulFactor(string $id, DateTime $createdAt, ThankfulFactor $thankfulFactor, ?DateTime $archivedAt = null): BigUnion { + public static function thankfulFactor(string $id, DateTime $createdAt, ThankfulFactor $thankfulFactor, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -330,7 +354,8 @@ public static function thankfulFactor(string $id, DateTime $createdAt, ThankfulF * @param JumboEnd $jumboEnd * @return BigUnion */ - public static function jumboEnd(string $id, DateTime $createdAt, JumboEnd $jumboEnd, ?DateTime $archivedAt = null): BigUnion { + public static function jumboEnd(string $id, DateTime $createdAt, JumboEnd $jumboEnd, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -347,7 +372,8 @@ public static function jumboEnd(string $id, DateTime $createdAt, JumboEnd $jumbo * @param HastyPain $hastyPain * @return BigUnion */ - public static function hastyPain(string $id, DateTime $createdAt, HastyPain $hastyPain, ?DateTime $archivedAt = null): BigUnion { + public static function hastyPain(string $id, DateTime $createdAt, HastyPain $hastyPain, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -364,7 +390,8 @@ public static function hastyPain(string $id, DateTime $createdAt, HastyPain $has * @param MistySnow $mistySnow * @return BigUnion */ - public static function mistySnow(string $id, DateTime $createdAt, MistySnow $mistySnow, ?DateTime $archivedAt = null): BigUnion { + public static function mistySnow(string $id, DateTime $createdAt, MistySnow $mistySnow, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -381,7 +408,8 @@ public static function mistySnow(string $id, DateTime $createdAt, MistySnow $mis * @param DistinctFailure $distinctFailure * @return BigUnion */ - public static function distinctFailure(string $id, DateTime $createdAt, DistinctFailure $distinctFailure, ?DateTime $archivedAt = null): BigUnion { + public static function distinctFailure(string $id, DateTime $createdAt, DistinctFailure $distinctFailure, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -398,7 +426,8 @@ public static function distinctFailure(string $id, DateTime $createdAt, Distinct * @param PracticalPrinciple $practicalPrinciple * @return BigUnion */ - public static function practicalPrinciple(string $id, DateTime $createdAt, PracticalPrinciple $practicalPrinciple, ?DateTime $archivedAt = null): BigUnion { + public static function practicalPrinciple(string $id, DateTime $createdAt, PracticalPrinciple $practicalPrinciple, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -415,7 +444,8 @@ public static function practicalPrinciple(string $id, DateTime $createdAt, Pract * @param LimpingStep $limpingStep * @return BigUnion */ - public static function limpingStep(string $id, DateTime $createdAt, LimpingStep $limpingStep, ?DateTime $archivedAt = null): BigUnion { + public static function limpingStep(string $id, DateTime $createdAt, LimpingStep $limpingStep, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -432,7 +462,8 @@ public static function limpingStep(string $id, DateTime $createdAt, LimpingStep * @param VibrantExcitement $vibrantExcitement * @return BigUnion */ - public static function vibrantExcitement(string $id, DateTime $createdAt, VibrantExcitement $vibrantExcitement, ?DateTime $archivedAt = null): BigUnion { + public static function vibrantExcitement(string $id, DateTime $createdAt, VibrantExcitement $vibrantExcitement, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -449,7 +480,8 @@ public static function vibrantExcitement(string $id, DateTime $createdAt, Vibran * @param ActiveDiamond $activeDiamond * @return BigUnion */ - public static function activeDiamond(string $id, DateTime $createdAt, ActiveDiamond $activeDiamond, ?DateTime $archivedAt = null): BigUnion { + public static function activeDiamond(string $id, DateTime $createdAt, ActiveDiamond $activeDiamond, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -466,7 +498,8 @@ public static function activeDiamond(string $id, DateTime $createdAt, ActiveDiam * @param PopularLimit $popularLimit * @return BigUnion */ - public static function popularLimit(string $id, DateTime $createdAt, PopularLimit $popularLimit, ?DateTime $archivedAt = null): BigUnion { + public static function popularLimit(string $id, DateTime $createdAt, PopularLimit $popularLimit, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -483,7 +516,8 @@ public static function popularLimit(string $id, DateTime $createdAt, PopularLimi * @param FalseMirror $falseMirror * @return BigUnion */ - public static function falseMirror(string $id, DateTime $createdAt, FalseMirror $falseMirror, ?DateTime $archivedAt = null): BigUnion { + public static function falseMirror(string $id, DateTime $createdAt, FalseMirror $falseMirror, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -500,7 +534,8 @@ public static function falseMirror(string $id, DateTime $createdAt, FalseMirror * @param PrimaryBlock $primaryBlock * @return BigUnion */ - public static function primaryBlock(string $id, DateTime $createdAt, PrimaryBlock $primaryBlock, ?DateTime $archivedAt = null): BigUnion { + public static function primaryBlock(string $id, DateTime $createdAt, PrimaryBlock $primaryBlock, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -517,7 +552,8 @@ public static function primaryBlock(string $id, DateTime $createdAt, PrimaryBloc * @param RotatingRatio $rotatingRatio * @return BigUnion */ - public static function rotatingRatio(string $id, DateTime $createdAt, RotatingRatio $rotatingRatio, ?DateTime $archivedAt = null): BigUnion { + public static function rotatingRatio(string $id, DateTime $createdAt, RotatingRatio $rotatingRatio, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -534,7 +570,8 @@ public static function rotatingRatio(string $id, DateTime $createdAt, RotatingRa * @param ColorfulCover $colorfulCover * @return BigUnion */ - public static function colorfulCover(string $id, DateTime $createdAt, ColorfulCover $colorfulCover, ?DateTime $archivedAt = null): BigUnion { + public static function colorfulCover(string $id, DateTime $createdAt, ColorfulCover $colorfulCover, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -551,7 +588,8 @@ public static function colorfulCover(string $id, DateTime $createdAt, ColorfulCo * @param DisloyalValue $disloyalValue * @return BigUnion */ - public static function disloyalValue(string $id, DateTime $createdAt, DisloyalValue $disloyalValue, ?DateTime $archivedAt = null): BigUnion { + public static function disloyalValue(string $id, DateTime $createdAt, DisloyalValue $disloyalValue, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -568,7 +606,8 @@ public static function disloyalValue(string $id, DateTime $createdAt, DisloyalVa * @param GruesomeCoach $gruesomeCoach * @return BigUnion */ - public static function gruesomeCoach(string $id, DateTime $createdAt, GruesomeCoach $gruesomeCoach, ?DateTime $archivedAt = null): BigUnion { + public static function gruesomeCoach(string $id, DateTime $createdAt, GruesomeCoach $gruesomeCoach, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -585,7 +624,8 @@ public static function gruesomeCoach(string $id, DateTime $createdAt, GruesomeCo * @param TotalWork $totalWork * @return BigUnion */ - public static function totalWork(string $id, DateTime $createdAt, TotalWork $totalWork, ?DateTime $archivedAt = null): BigUnion { + public static function totalWork(string $id, DateTime $createdAt, TotalWork $totalWork, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -602,7 +642,8 @@ public static function totalWork(string $id, DateTime $createdAt, TotalWork $tot * @param HarmoniousPlay $harmoniousPlay * @return BigUnion */ - public static function harmoniousPlay(string $id, DateTime $createdAt, HarmoniousPlay $harmoniousPlay, ?DateTime $archivedAt = null): BigUnion { + public static function harmoniousPlay(string $id, DateTime $createdAt, HarmoniousPlay $harmoniousPlay, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -619,7 +660,8 @@ public static function harmoniousPlay(string $id, DateTime $createdAt, Harmoniou * @param UniqueStress $uniqueStress * @return BigUnion */ - public static function uniqueStress(string $id, DateTime $createdAt, UniqueStress $uniqueStress, ?DateTime $archivedAt = null): BigUnion { + public static function uniqueStress(string $id, DateTime $createdAt, UniqueStress $uniqueStress, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -636,7 +678,8 @@ public static function uniqueStress(string $id, DateTime $createdAt, UniqueStres * @param UnwillingSmoke $unwillingSmoke * @return BigUnion */ - public static function unwillingSmoke(string $id, DateTime $createdAt, UnwillingSmoke $unwillingSmoke, ?DateTime $archivedAt = null): BigUnion { + public static function unwillingSmoke(string $id, DateTime $createdAt, UnwillingSmoke $unwillingSmoke, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -653,7 +696,8 @@ public static function unwillingSmoke(string $id, DateTime $createdAt, Unwilling * @param FrozenSleep $frozenSleep * @return BigUnion */ - public static function frozenSleep(string $id, DateTime $createdAt, FrozenSleep $frozenSleep, ?DateTime $archivedAt = null): BigUnion { + public static function frozenSleep(string $id, DateTime $createdAt, FrozenSleep $frozenSleep, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -670,7 +714,8 @@ public static function frozenSleep(string $id, DateTime $createdAt, FrozenSleep * @param DiligentDeal $diligentDeal * @return BigUnion */ - public static function diligentDeal(string $id, DateTime $createdAt, DiligentDeal $diligentDeal, ?DateTime $archivedAt = null): BigUnion { + public static function diligentDeal(string $id, DateTime $createdAt, DiligentDeal $diligentDeal, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -687,7 +732,8 @@ public static function diligentDeal(string $id, DateTime $createdAt, DiligentDea * @param AttractiveScript $attractiveScript * @return BigUnion */ - public static function attractiveScript(string $id, DateTime $createdAt, AttractiveScript $attractiveScript, ?DateTime $archivedAt = null): BigUnion { + public static function attractiveScript(string $id, DateTime $createdAt, AttractiveScript $attractiveScript, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -704,7 +750,8 @@ public static function attractiveScript(string $id, DateTime $createdAt, Attract * @param HoarseMouse $hoarseMouse * @return BigUnion */ - public static function hoarseMouse(string $id, DateTime $createdAt, HoarseMouse $hoarseMouse, ?DateTime $archivedAt = null): BigUnion { + public static function hoarseMouse(string $id, DateTime $createdAt, HoarseMouse $hoarseMouse, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -721,7 +768,8 @@ public static function hoarseMouse(string $id, DateTime $createdAt, HoarseMouse * @param CircularCard $circularCard * @return BigUnion */ - public static function circularCard(string $id, DateTime $createdAt, CircularCard $circularCard, ?DateTime $archivedAt = null): BigUnion { + public static function circularCard(string $id, DateTime $createdAt, CircularCard $circularCard, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -738,7 +786,8 @@ public static function circularCard(string $id, DateTime $createdAt, CircularCar * @param PotableBad $potableBad * @return BigUnion */ - public static function potableBad(string $id, DateTime $createdAt, PotableBad $potableBad, ?DateTime $archivedAt = null): BigUnion { + public static function potableBad(string $id, DateTime $createdAt, PotableBad $potableBad, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -755,7 +804,8 @@ public static function potableBad(string $id, DateTime $createdAt, PotableBad $p * @param TriangularRepair $triangularRepair * @return BigUnion */ - public static function triangularRepair(string $id, DateTime $createdAt, TriangularRepair $triangularRepair, ?DateTime $archivedAt = null): BigUnion { + public static function triangularRepair(string $id, DateTime $createdAt, TriangularRepair $triangularRepair, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -772,7 +822,8 @@ public static function triangularRepair(string $id, DateTime $createdAt, Triangu * @param GaseousRoad $gaseousRoad * @return BigUnion */ - public static function gaseousRoad(string $id, DateTime $createdAt, GaseousRoad $gaseousRoad, ?DateTime $archivedAt = null): BigUnion { + public static function gaseousRoad(string $id, DateTime $createdAt, GaseousRoad $gaseousRoad, ?DateTime $archivedAt = null): BigUnion + { return new BigUnion([ 'id' => $id, 'createdAt' => $createdAt, @@ -785,601 +836,661 @@ public static function gaseousRoad(string $id, DateTime $createdAt, GaseousRoad /** * @return bool */ - public function isNormalSweet(): bool { - return $this->value instanceof NormalSweet&& $this->type === 'normalSweet'; + public function isNormalSweet(): bool + { + return $this->value instanceof NormalSweet && $this->type === 'normalSweet'; } /** * @return NormalSweet */ - public function asNormalSweet(): NormalSweet { - if (!($this->value instanceof NormalSweet&& $this->type === 'normalSweet')){ + public function asNormalSweet(): NormalSweet + { + if (!($this->value instanceof NormalSweet && $this->type === 'normalSweet')) { throw new Exception( "Expected normalSweet; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isThankfulFactor(): bool { - return $this->value instanceof ThankfulFactor&& $this->type === 'thankfulFactor'; + public function isThankfulFactor(): bool + { + return $this->value instanceof ThankfulFactor && $this->type === 'thankfulFactor'; } /** * @return ThankfulFactor */ - public function asThankfulFactor(): ThankfulFactor { - if (!($this->value instanceof ThankfulFactor&& $this->type === 'thankfulFactor')){ + public function asThankfulFactor(): ThankfulFactor + { + if (!($this->value instanceof ThankfulFactor && $this->type === 'thankfulFactor')) { throw new Exception( "Expected thankfulFactor; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isJumboEnd(): bool { - return $this->value instanceof JumboEnd&& $this->type === 'jumboEnd'; + public function isJumboEnd(): bool + { + return $this->value instanceof JumboEnd && $this->type === 'jumboEnd'; } /** * @return JumboEnd */ - public function asJumboEnd(): JumboEnd { - if (!($this->value instanceof JumboEnd&& $this->type === 'jumboEnd')){ + public function asJumboEnd(): JumboEnd + { + if (!($this->value instanceof JumboEnd && $this->type === 'jumboEnd')) { throw new Exception( "Expected jumboEnd; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isHastyPain(): bool { - return $this->value instanceof HastyPain&& $this->type === 'hastyPain'; + public function isHastyPain(): bool + { + return $this->value instanceof HastyPain && $this->type === 'hastyPain'; } /** * @return HastyPain */ - public function asHastyPain(): HastyPain { - if (!($this->value instanceof HastyPain&& $this->type === 'hastyPain')){ + public function asHastyPain(): HastyPain + { + if (!($this->value instanceof HastyPain && $this->type === 'hastyPain')) { throw new Exception( "Expected hastyPain; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isMistySnow(): bool { - return $this->value instanceof MistySnow&& $this->type === 'mistySnow'; + public function isMistySnow(): bool + { + return $this->value instanceof MistySnow && $this->type === 'mistySnow'; } /** * @return MistySnow */ - public function asMistySnow(): MistySnow { - if (!($this->value instanceof MistySnow&& $this->type === 'mistySnow')){ + public function asMistySnow(): MistySnow + { + if (!($this->value instanceof MistySnow && $this->type === 'mistySnow')) { throw new Exception( "Expected mistySnow; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isDistinctFailure(): bool { - return $this->value instanceof DistinctFailure&& $this->type === 'distinctFailure'; + public function isDistinctFailure(): bool + { + return $this->value instanceof DistinctFailure && $this->type === 'distinctFailure'; } /** * @return DistinctFailure */ - public function asDistinctFailure(): DistinctFailure { - if (!($this->value instanceof DistinctFailure&& $this->type === 'distinctFailure')){ + public function asDistinctFailure(): DistinctFailure + { + if (!($this->value instanceof DistinctFailure && $this->type === 'distinctFailure')) { throw new Exception( "Expected distinctFailure; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isPracticalPrinciple(): bool { - return $this->value instanceof PracticalPrinciple&& $this->type === 'practicalPrinciple'; + public function isPracticalPrinciple(): bool + { + return $this->value instanceof PracticalPrinciple && $this->type === 'practicalPrinciple'; } /** * @return PracticalPrinciple */ - public function asPracticalPrinciple(): PracticalPrinciple { - if (!($this->value instanceof PracticalPrinciple&& $this->type === 'practicalPrinciple')){ + public function asPracticalPrinciple(): PracticalPrinciple + { + if (!($this->value instanceof PracticalPrinciple && $this->type === 'practicalPrinciple')) { throw new Exception( "Expected practicalPrinciple; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isLimpingStep(): bool { - return $this->value instanceof LimpingStep&& $this->type === 'limpingStep'; + public function isLimpingStep(): bool + { + return $this->value instanceof LimpingStep && $this->type === 'limpingStep'; } /** * @return LimpingStep */ - public function asLimpingStep(): LimpingStep { - if (!($this->value instanceof LimpingStep&& $this->type === 'limpingStep')){ + public function asLimpingStep(): LimpingStep + { + if (!($this->value instanceof LimpingStep && $this->type === 'limpingStep')) { throw new Exception( "Expected limpingStep; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isVibrantExcitement(): bool { - return $this->value instanceof VibrantExcitement&& $this->type === 'vibrantExcitement'; + public function isVibrantExcitement(): bool + { + return $this->value instanceof VibrantExcitement && $this->type === 'vibrantExcitement'; } /** * @return VibrantExcitement */ - public function asVibrantExcitement(): VibrantExcitement { - if (!($this->value instanceof VibrantExcitement&& $this->type === 'vibrantExcitement')){ + public function asVibrantExcitement(): VibrantExcitement + { + if (!($this->value instanceof VibrantExcitement && $this->type === 'vibrantExcitement')) { throw new Exception( "Expected vibrantExcitement; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isActiveDiamond(): bool { - return $this->value instanceof ActiveDiamond&& $this->type === 'activeDiamond'; + public function isActiveDiamond(): bool + { + return $this->value instanceof ActiveDiamond && $this->type === 'activeDiamond'; } /** * @return ActiveDiamond */ - public function asActiveDiamond(): ActiveDiamond { - if (!($this->value instanceof ActiveDiamond&& $this->type === 'activeDiamond')){ + public function asActiveDiamond(): ActiveDiamond + { + if (!($this->value instanceof ActiveDiamond && $this->type === 'activeDiamond')) { throw new Exception( "Expected activeDiamond; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isPopularLimit(): bool { - return $this->value instanceof PopularLimit&& $this->type === 'popularLimit'; + public function isPopularLimit(): bool + { + return $this->value instanceof PopularLimit && $this->type === 'popularLimit'; } /** * @return PopularLimit */ - public function asPopularLimit(): PopularLimit { - if (!($this->value instanceof PopularLimit&& $this->type === 'popularLimit')){ + public function asPopularLimit(): PopularLimit + { + if (!($this->value instanceof PopularLimit && $this->type === 'popularLimit')) { throw new Exception( "Expected popularLimit; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isFalseMirror(): bool { - return $this->value instanceof FalseMirror&& $this->type === 'falseMirror'; + public function isFalseMirror(): bool + { + return $this->value instanceof FalseMirror && $this->type === 'falseMirror'; } /** * @return FalseMirror */ - public function asFalseMirror(): FalseMirror { - if (!($this->value instanceof FalseMirror&& $this->type === 'falseMirror')){ + public function asFalseMirror(): FalseMirror + { + if (!($this->value instanceof FalseMirror && $this->type === 'falseMirror')) { throw new Exception( "Expected falseMirror; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isPrimaryBlock(): bool { - return $this->value instanceof PrimaryBlock&& $this->type === 'primaryBlock'; + public function isPrimaryBlock(): bool + { + return $this->value instanceof PrimaryBlock && $this->type === 'primaryBlock'; } /** * @return PrimaryBlock */ - public function asPrimaryBlock(): PrimaryBlock { - if (!($this->value instanceof PrimaryBlock&& $this->type === 'primaryBlock')){ + public function asPrimaryBlock(): PrimaryBlock + { + if (!($this->value instanceof PrimaryBlock && $this->type === 'primaryBlock')) { throw new Exception( "Expected primaryBlock; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isRotatingRatio(): bool { - return $this->value instanceof RotatingRatio&& $this->type === 'rotatingRatio'; + public function isRotatingRatio(): bool + { + return $this->value instanceof RotatingRatio && $this->type === 'rotatingRatio'; } /** * @return RotatingRatio */ - public function asRotatingRatio(): RotatingRatio { - if (!($this->value instanceof RotatingRatio&& $this->type === 'rotatingRatio')){ + public function asRotatingRatio(): RotatingRatio + { + if (!($this->value instanceof RotatingRatio && $this->type === 'rotatingRatio')) { throw new Exception( "Expected rotatingRatio; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isColorfulCover(): bool { - return $this->value instanceof ColorfulCover&& $this->type === 'colorfulCover'; + public function isColorfulCover(): bool + { + return $this->value instanceof ColorfulCover && $this->type === 'colorfulCover'; } /** * @return ColorfulCover */ - public function asColorfulCover(): ColorfulCover { - if (!($this->value instanceof ColorfulCover&& $this->type === 'colorfulCover')){ + public function asColorfulCover(): ColorfulCover + { + if (!($this->value instanceof ColorfulCover && $this->type === 'colorfulCover')) { throw new Exception( "Expected colorfulCover; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isDisloyalValue(): bool { - return $this->value instanceof DisloyalValue&& $this->type === 'disloyalValue'; + public function isDisloyalValue(): bool + { + return $this->value instanceof DisloyalValue && $this->type === 'disloyalValue'; } /** * @return DisloyalValue */ - public function asDisloyalValue(): DisloyalValue { - if (!($this->value instanceof DisloyalValue&& $this->type === 'disloyalValue')){ + public function asDisloyalValue(): DisloyalValue + { + if (!($this->value instanceof DisloyalValue && $this->type === 'disloyalValue')) { throw new Exception( "Expected disloyalValue; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isGruesomeCoach(): bool { - return $this->value instanceof GruesomeCoach&& $this->type === 'gruesomeCoach'; + public function isGruesomeCoach(): bool + { + return $this->value instanceof GruesomeCoach && $this->type === 'gruesomeCoach'; } /** * @return GruesomeCoach */ - public function asGruesomeCoach(): GruesomeCoach { - if (!($this->value instanceof GruesomeCoach&& $this->type === 'gruesomeCoach')){ + public function asGruesomeCoach(): GruesomeCoach + { + if (!($this->value instanceof GruesomeCoach && $this->type === 'gruesomeCoach')) { throw new Exception( "Expected gruesomeCoach; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isTotalWork(): bool { - return $this->value instanceof TotalWork&& $this->type === 'totalWork'; + public function isTotalWork(): bool + { + return $this->value instanceof TotalWork && $this->type === 'totalWork'; } /** * @return TotalWork */ - public function asTotalWork(): TotalWork { - if (!($this->value instanceof TotalWork&& $this->type === 'totalWork')){ + public function asTotalWork(): TotalWork + { + if (!($this->value instanceof TotalWork && $this->type === 'totalWork')) { throw new Exception( "Expected totalWork; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isHarmoniousPlay(): bool { - return $this->value instanceof HarmoniousPlay&& $this->type === 'harmoniousPlay'; + public function isHarmoniousPlay(): bool + { + return $this->value instanceof HarmoniousPlay && $this->type === 'harmoniousPlay'; } /** * @return HarmoniousPlay */ - public function asHarmoniousPlay(): HarmoniousPlay { - if (!($this->value instanceof HarmoniousPlay&& $this->type === 'harmoniousPlay')){ + public function asHarmoniousPlay(): HarmoniousPlay + { + if (!($this->value instanceof HarmoniousPlay && $this->type === 'harmoniousPlay')) { throw new Exception( "Expected harmoniousPlay; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isUniqueStress(): bool { - return $this->value instanceof UniqueStress&& $this->type === 'uniqueStress'; + public function isUniqueStress(): bool + { + return $this->value instanceof UniqueStress && $this->type === 'uniqueStress'; } /** * @return UniqueStress */ - public function asUniqueStress(): UniqueStress { - if (!($this->value instanceof UniqueStress&& $this->type === 'uniqueStress')){ + public function asUniqueStress(): UniqueStress + { + if (!($this->value instanceof UniqueStress && $this->type === 'uniqueStress')) { throw new Exception( "Expected uniqueStress; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isUnwillingSmoke(): bool { - return $this->value instanceof UnwillingSmoke&& $this->type === 'unwillingSmoke'; + public function isUnwillingSmoke(): bool + { + return $this->value instanceof UnwillingSmoke && $this->type === 'unwillingSmoke'; } /** * @return UnwillingSmoke */ - public function asUnwillingSmoke(): UnwillingSmoke { - if (!($this->value instanceof UnwillingSmoke&& $this->type === 'unwillingSmoke')){ + public function asUnwillingSmoke(): UnwillingSmoke + { + if (!($this->value instanceof UnwillingSmoke && $this->type === 'unwillingSmoke')) { throw new Exception( "Expected unwillingSmoke; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isFrozenSleep(): bool { - return $this->value instanceof FrozenSleep&& $this->type === 'frozenSleep'; + public function isFrozenSleep(): bool + { + return $this->value instanceof FrozenSleep && $this->type === 'frozenSleep'; } /** * @return FrozenSleep */ - public function asFrozenSleep(): FrozenSleep { - if (!($this->value instanceof FrozenSleep&& $this->type === 'frozenSleep')){ + public function asFrozenSleep(): FrozenSleep + { + if (!($this->value instanceof FrozenSleep && $this->type === 'frozenSleep')) { throw new Exception( "Expected frozenSleep; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isDiligentDeal(): bool { - return $this->value instanceof DiligentDeal&& $this->type === 'diligentDeal'; + public function isDiligentDeal(): bool + { + return $this->value instanceof DiligentDeal && $this->type === 'diligentDeal'; } /** * @return DiligentDeal */ - public function asDiligentDeal(): DiligentDeal { - if (!($this->value instanceof DiligentDeal&& $this->type === 'diligentDeal')){ + public function asDiligentDeal(): DiligentDeal + { + if (!($this->value instanceof DiligentDeal && $this->type === 'diligentDeal')) { throw new Exception( "Expected diligentDeal; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isAttractiveScript(): bool { - return $this->value instanceof AttractiveScript&& $this->type === 'attractiveScript'; + public function isAttractiveScript(): bool + { + return $this->value instanceof AttractiveScript && $this->type === 'attractiveScript'; } /** * @return AttractiveScript */ - public function asAttractiveScript(): AttractiveScript { - if (!($this->value instanceof AttractiveScript&& $this->type === 'attractiveScript')){ + public function asAttractiveScript(): AttractiveScript + { + if (!($this->value instanceof AttractiveScript && $this->type === 'attractiveScript')) { throw new Exception( "Expected attractiveScript; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isHoarseMouse(): bool { - return $this->value instanceof HoarseMouse&& $this->type === 'hoarseMouse'; + public function isHoarseMouse(): bool + { + return $this->value instanceof HoarseMouse && $this->type === 'hoarseMouse'; } /** * @return HoarseMouse */ - public function asHoarseMouse(): HoarseMouse { - if (!($this->value instanceof HoarseMouse&& $this->type === 'hoarseMouse')){ + public function asHoarseMouse(): HoarseMouse + { + if (!($this->value instanceof HoarseMouse && $this->type === 'hoarseMouse')) { throw new Exception( "Expected hoarseMouse; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isCircularCard(): bool { - return $this->value instanceof CircularCard&& $this->type === 'circularCard'; + public function isCircularCard(): bool + { + return $this->value instanceof CircularCard && $this->type === 'circularCard'; } /** * @return CircularCard */ - public function asCircularCard(): CircularCard { - if (!($this->value instanceof CircularCard&& $this->type === 'circularCard')){ + public function asCircularCard(): CircularCard + { + if (!($this->value instanceof CircularCard && $this->type === 'circularCard')) { throw new Exception( "Expected circularCard; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isPotableBad(): bool { - return $this->value instanceof PotableBad&& $this->type === 'potableBad'; + public function isPotableBad(): bool + { + return $this->value instanceof PotableBad && $this->type === 'potableBad'; } /** * @return PotableBad */ - public function asPotableBad(): PotableBad { - if (!($this->value instanceof PotableBad&& $this->type === 'potableBad')){ + public function asPotableBad(): PotableBad + { + if (!($this->value instanceof PotableBad && $this->type === 'potableBad')) { throw new Exception( "Expected potableBad; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isTriangularRepair(): bool { - return $this->value instanceof TriangularRepair&& $this->type === 'triangularRepair'; + public function isTriangularRepair(): bool + { + return $this->value instanceof TriangularRepair && $this->type === 'triangularRepair'; } /** * @return TriangularRepair */ - public function asTriangularRepair(): TriangularRepair { - if (!($this->value instanceof TriangularRepair&& $this->type === 'triangularRepair')){ + public function asTriangularRepair(): TriangularRepair + { + if (!($this->value instanceof TriangularRepair && $this->type === 'triangularRepair')) { throw new Exception( "Expected triangularRepair; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isGaseousRoad(): bool { - return $this->value instanceof GaseousRoad&& $this->type === 'gaseousRoad'; + public function isGaseousRoad(): bool + { + return $this->value instanceof GaseousRoad && $this->type === 'gaseousRoad'; } /** * @return GaseousRoad */ - public function asGaseousRoad(): GaseousRoad { - if (!($this->value instanceof GaseousRoad&& $this->type === 'gaseousRoad')){ + public function asGaseousRoad(): GaseousRoad + { + if (!($this->value instanceof GaseousRoad && $this->type === 'gaseousRoad')) { throw new Exception( "Expected gaseousRoad; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'normalSweet': $value = $this->asNormalSweet()->jsonSerialize(); $result = array_merge($value, $result); @@ -1498,26 +1609,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -1526,58 +1638,59 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('id', $data)){ + if (!array_key_exists('id', $data)) { throw new Exception( "JSON data is missing property 'id'", ); } - if (!(is_string($data['id']))){ + if (!(is_string($data['id']))) { throw new Exception( "Expected property 'id' in JSON data to be string, instead received " . get_debug_type($data['id']), ); } $args['id'] = $data['id']; - - if (!array_key_exists('created-at', $data)){ + + if (!array_key_exists('created-at', $data)) { throw new Exception( "JSON data is missing property 'created-at'", ); } - if (!($data['created-at'] instanceof DateTime)){ + if (!($data['created-at'] instanceof DateTime)) { throw new Exception( "Expected property 'createdAt' in JSON data to be dateTime, instead received " . get_debug_type($data['created-at']), ); } $args['createdAt'] = $data['created-at']; - - if (!array_key_exists('archived-at', $data)){ + + if (!array_key_exists('archived-at', $data)) { throw new Exception( "JSON data is missing property 'archived-at'", ); } - if (!((is_null($data['archived-at']) || $data['archived-at'] instanceof DateTime))){ + if (!((is_null($data['archived-at']) || $data['archived-at'] instanceof DateTime))) { throw new Exception( "Expected property 'archivedAt' in JSON data to be optional, instead received " . get_debug_type($data['archived-at']), ); } $args['archivedAt'] = $data['archived-at']; - - if (!array_key_exists('type', $data)){ + + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'normalSweet': $args['value'] = NormalSweet::jsonDeserialize($data); break; @@ -1670,7 +1783,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/CircularCard.php b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/CircularCard.php index b16bdc6dced4..9de869181f4f 100644 --- a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/CircularCard.php +++ b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/CircularCard.php @@ -20,27 +20,32 @@ class CircularCard extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function getValue(): string { - return $this->value;} + public function getValue(): string + { + return $this->value; + } /** * @param string $value */ - public function setValue(string $value): self { - $this->value = $value;return $this;} + public function setValue(string $value): self + { + $this->value = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/ColorfulCover.php b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/ColorfulCover.php index 03ff61ea5c06..7e3c05f4cf4c 100644 --- a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/ColorfulCover.php +++ b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/ColorfulCover.php @@ -20,27 +20,32 @@ class ColorfulCover extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function getValue(): string { - return $this->value;} + public function getValue(): string + { + return $this->value; + } /** * @param string $value */ - public function setValue(string $value): self { - $this->value = $value;return $this;} + public function setValue(string $value): self + { + $this->value = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/DiligentDeal.php b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/DiligentDeal.php index 1d1f59aaa9f7..9b4431d8566a 100644 --- a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/DiligentDeal.php +++ b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/DiligentDeal.php @@ -20,27 +20,32 @@ class DiligentDeal extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function getValue(): string { - return $this->value;} + public function getValue(): string + { + return $this->value; + } /** * @param string $value */ - public function setValue(string $value): self { - $this->value = $value;return $this;} + public function setValue(string $value): self + { + $this->value = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/DisloyalValue.php b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/DisloyalValue.php index 8559fb790828..4270e97872ee 100644 --- a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/DisloyalValue.php +++ b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/DisloyalValue.php @@ -20,27 +20,32 @@ class DisloyalValue extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function getValue(): string { - return $this->value;} + public function getValue(): string + { + return $this->value; + } /** * @param string $value */ - public function setValue(string $value): self { - $this->value = $value;return $this;} + public function setValue(string $value): self + { + $this->value = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/DistinctFailure.php b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/DistinctFailure.php index a7c3e665f820..7685c43a1d73 100644 --- a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/DistinctFailure.php +++ b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/DistinctFailure.php @@ -20,27 +20,32 @@ class DistinctFailure extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function getValue(): string { - return $this->value;} + public function getValue(): string + { + return $this->value; + } /** * @param string $value */ - public function setValue(string $value): self { - $this->value = $value;return $this;} + public function setValue(string $value): self + { + $this->value = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/FalseMirror.php b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/FalseMirror.php index 606f8b93e3d2..3230e88a4a3a 100644 --- a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/FalseMirror.php +++ b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/FalseMirror.php @@ -20,27 +20,32 @@ class FalseMirror extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function getValue(): string { - return $this->value;} + public function getValue(): string + { + return $this->value; + } /** * @param string $value */ - public function setValue(string $value): self { - $this->value = $value;return $this;} + public function setValue(string $value): self + { + $this->value = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/FrozenSleep.php b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/FrozenSleep.php index 51139daf1d26..4bb4ab21f21a 100644 --- a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/FrozenSleep.php +++ b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/FrozenSleep.php @@ -20,27 +20,32 @@ class FrozenSleep extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function getValue(): string { - return $this->value;} + public function getValue(): string + { + return $this->value; + } /** * @param string $value */ - public function setValue(string $value): self { - $this->value = $value;return $this;} + public function setValue(string $value): self + { + $this->value = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/GaseousRoad.php b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/GaseousRoad.php index 8516ee9ee330..cf4ecc683c19 100644 --- a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/GaseousRoad.php +++ b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/GaseousRoad.php @@ -20,27 +20,32 @@ class GaseousRoad extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function getValue(): string { - return $this->value;} + public function getValue(): string + { + return $this->value; + } /** * @param string $value */ - public function setValue(string $value): self { - $this->value = $value;return $this;} + public function setValue(string $value): self + { + $this->value = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/GruesomeCoach.php b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/GruesomeCoach.php index b1d342238f2e..a7a7cc1db88f 100644 --- a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/GruesomeCoach.php +++ b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/GruesomeCoach.php @@ -20,27 +20,32 @@ class GruesomeCoach extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function getValue(): string { - return $this->value;} + public function getValue(): string + { + return $this->value; + } /** * @param string $value */ - public function setValue(string $value): self { - $this->value = $value;return $this;} + public function setValue(string $value): self + { + $this->value = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/HarmoniousPlay.php b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/HarmoniousPlay.php index 8d7fba7c3cbf..1132322b4c30 100644 --- a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/HarmoniousPlay.php +++ b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/HarmoniousPlay.php @@ -20,27 +20,32 @@ class HarmoniousPlay extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function getValue(): string { - return $this->value;} + public function getValue(): string + { + return $this->value; + } /** * @param string $value */ - public function setValue(string $value): self { - $this->value = $value;return $this;} + public function setValue(string $value): self + { + $this->value = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/HastyPain.php b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/HastyPain.php index 082f64f67c57..a5f030ff14df 100644 --- a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/HastyPain.php +++ b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/HastyPain.php @@ -20,27 +20,32 @@ class HastyPain extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function getValue(): string { - return $this->value;} + public function getValue(): string + { + return $this->value; + } /** * @param string $value */ - public function setValue(string $value): self { - $this->value = $value;return $this;} + public function setValue(string $value): self + { + $this->value = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/HoarseMouse.php b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/HoarseMouse.php index f7ce3b3665da..3e9343a71472 100644 --- a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/HoarseMouse.php +++ b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/HoarseMouse.php @@ -20,27 +20,32 @@ class HoarseMouse extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function getValue(): string { - return $this->value;} + public function getValue(): string + { + return $this->value; + } /** * @param string $value */ - public function setValue(string $value): self { - $this->value = $value;return $this;} + public function setValue(string $value): self + { + $this->value = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/JumboEnd.php b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/JumboEnd.php index b01500dde32b..32afd09f22d7 100644 --- a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/JumboEnd.php +++ b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/JumboEnd.php @@ -20,27 +20,32 @@ class JumboEnd extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function getValue(): string { - return $this->value;} + public function getValue(): string + { + return $this->value; + } /** * @param string $value */ - public function setValue(string $value): self { - $this->value = $value;return $this;} + public function setValue(string $value): self + { + $this->value = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/LimpingStep.php b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/LimpingStep.php index 743c3ee8a7aa..0aab4f1f5944 100644 --- a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/LimpingStep.php +++ b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/LimpingStep.php @@ -20,27 +20,32 @@ class LimpingStep extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function getValue(): string { - return $this->value;} + public function getValue(): string + { + return $this->value; + } /** * @param string $value */ - public function setValue(string $value): self { - $this->value = $value;return $this;} + public function setValue(string $value): self + { + $this->value = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/MistySnow.php b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/MistySnow.php index c010903acbe2..6329f94e8d5a 100644 --- a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/MistySnow.php +++ b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/MistySnow.php @@ -20,27 +20,32 @@ class MistySnow extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function getValue(): string { - return $this->value;} + public function getValue(): string + { + return $this->value; + } /** * @param string $value */ - public function setValue(string $value): self { - $this->value = $value;return $this;} + public function setValue(string $value): self + { + $this->value = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/NormalSweet.php b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/NormalSweet.php index f67582abe812..27f151b155da 100644 --- a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/NormalSweet.php +++ b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/NormalSweet.php @@ -20,27 +20,32 @@ class NormalSweet extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function getValue(): string { - return $this->value;} + public function getValue(): string + { + return $this->value; + } /** * @param string $value */ - public function setValue(string $value): self { - $this->value = $value;return $this;} + public function setValue(string $value): self + { + $this->value = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/PopularLimit.php b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/PopularLimit.php index 45b2cd189f0b..a7bbe159bd9b 100644 --- a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/PopularLimit.php +++ b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/PopularLimit.php @@ -20,27 +20,32 @@ class PopularLimit extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function getValue(): string { - return $this->value;} + public function getValue(): string + { + return $this->value; + } /** * @param string $value */ - public function setValue(string $value): self { - $this->value = $value;return $this;} + public function setValue(string $value): self + { + $this->value = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/PotableBad.php b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/PotableBad.php index 646ee1095252..62c592ae0c08 100644 --- a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/PotableBad.php +++ b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/PotableBad.php @@ -20,27 +20,32 @@ class PotableBad extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function getValue(): string { - return $this->value;} + public function getValue(): string + { + return $this->value; + } /** * @param string $value */ - public function setValue(string $value): self { - $this->value = $value;return $this;} + public function setValue(string $value): self + { + $this->value = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/PracticalPrinciple.php b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/PracticalPrinciple.php index 772a8bbb5831..2226425d8ccb 100644 --- a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/PracticalPrinciple.php +++ b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/PracticalPrinciple.php @@ -20,27 +20,32 @@ class PracticalPrinciple extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function getValue(): string { - return $this->value;} + public function getValue(): string + { + return $this->value; + } /** * @param string $value */ - public function setValue(string $value): self { - $this->value = $value;return $this;} + public function setValue(string $value): self + { + $this->value = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/PrimaryBlock.php b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/PrimaryBlock.php index 9b3d631932c6..55a7e68630e8 100644 --- a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/PrimaryBlock.php +++ b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/PrimaryBlock.php @@ -20,27 +20,32 @@ class PrimaryBlock extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function getValue(): string { - return $this->value;} + public function getValue(): string + { + return $this->value; + } /** * @param string $value */ - public function setValue(string $value): self { - $this->value = $value;return $this;} + public function setValue(string $value): self + { + $this->value = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/RotatingRatio.php b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/RotatingRatio.php index 08c869928c5c..5866280e70f1 100644 --- a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/RotatingRatio.php +++ b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/RotatingRatio.php @@ -20,27 +20,32 @@ class RotatingRatio extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function getValue(): string { - return $this->value;} + public function getValue(): string + { + return $this->value; + } /** * @param string $value */ - public function setValue(string $value): self { - $this->value = $value;return $this;} + public function setValue(string $value): self + { + $this->value = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/ThankfulFactor.php b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/ThankfulFactor.php index d10a58e132ef..8f71a08b49fa 100644 --- a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/ThankfulFactor.php +++ b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/ThankfulFactor.php @@ -20,27 +20,32 @@ class ThankfulFactor extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function getValue(): string { - return $this->value;} + public function getValue(): string + { + return $this->value; + } /** * @param string $value */ - public function setValue(string $value): self { - $this->value = $value;return $this;} + public function setValue(string $value): self + { + $this->value = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/TotalWork.php b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/TotalWork.php index a8e82547ceba..98e3d70c8a2b 100644 --- a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/TotalWork.php +++ b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/TotalWork.php @@ -20,27 +20,32 @@ class TotalWork extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function getValue(): string { - return $this->value;} + public function getValue(): string + { + return $this->value; + } /** * @param string $value */ - public function setValue(string $value): self { - $this->value = $value;return $this;} + public function setValue(string $value): self + { + $this->value = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/TriangularRepair.php b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/TriangularRepair.php index 2fca804a8495..c5ad40e58050 100644 --- a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/TriangularRepair.php +++ b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/TriangularRepair.php @@ -20,27 +20,32 @@ class TriangularRepair extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function getValue(): string { - return $this->value;} + public function getValue(): string + { + return $this->value; + } /** * @param string $value */ - public function setValue(string $value): self { - $this->value = $value;return $this;} + public function setValue(string $value): self + { + $this->value = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/UniqueStress.php b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/UniqueStress.php index 272a6e0b4bf2..730ae433d981 100644 --- a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/UniqueStress.php +++ b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/UniqueStress.php @@ -20,27 +20,32 @@ class UniqueStress extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function getValue(): string { - return $this->value;} + public function getValue(): string + { + return $this->value; + } /** * @param string $value */ - public function setValue(string $value): self { - $this->value = $value;return $this;} + public function setValue(string $value): self + { + $this->value = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/UnwillingSmoke.php b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/UnwillingSmoke.php index faa05573c8d7..47895c06629f 100644 --- a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/UnwillingSmoke.php +++ b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/UnwillingSmoke.php @@ -20,27 +20,32 @@ class UnwillingSmoke extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function getValue(): string { - return $this->value;} + public function getValue(): string + { + return $this->value; + } /** * @param string $value */ - public function setValue(string $value): self { - $this->value = $value;return $this;} + public function setValue(string $value): self + { + $this->value = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/VibrantExcitement.php b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/VibrantExcitement.php index 96d266c465af..f966379b5ae4 100644 --- a/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/VibrantExcitement.php +++ b/seed/php-sdk/unions/property-accessors/src/Bigunion/Types/VibrantExcitement.php @@ -20,27 +20,32 @@ class VibrantExcitement extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->value = $values['value']; } /** * @return string */ - public function getValue(): string { - return $this->value;} + public function getValue(): string + { + return $this->value; + } /** * @param string $value */ - public function setValue(string $value): self { - $this->value = $value;return $this;} + public function setValue(string $value): self + { + $this->value = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/property-accessors/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/unions/property-accessors/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/unions/property-accessors/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/unions/property-accessors/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/unions/property-accessors/src/Core/Client/RawClient.php b/seed/php-sdk/unions/property-accessors/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/unions/property-accessors/src/Core/Client/RawClient.php +++ b/seed/php-sdk/unions/property-accessors/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/unions/property-accessors/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/unions/property-accessors/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/unions/property-accessors/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/unions/property-accessors/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/unions/property-accessors/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/unions/property-accessors/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/unions/property-accessors/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/unions/property-accessors/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/unions/property-accessors/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/unions/property-accessors/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/unions/property-accessors/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/unions/property-accessors/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/unions/property-accessors/src/Core/Types/ArrayType.php b/seed/php-sdk/unions/property-accessors/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/unions/property-accessors/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/unions/property-accessors/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/unions/property-accessors/src/Core/Types/Constant.php b/seed/php-sdk/unions/property-accessors/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/unions/property-accessors/src/Core/Types/Constant.php +++ b/seed/php-sdk/unions/property-accessors/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/unions/property-accessors/src/Core/Types/Union.php b/seed/php-sdk/unions/property-accessors/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/unions/property-accessors/src/Core/Types/Union.php +++ b/seed/php-sdk/unions/property-accessors/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/unions/property-accessors/src/Exceptions/SeedApiException.php b/seed/php-sdk/unions/property-accessors/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/unions/property-accessors/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/unions/property-accessors/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/unions/property-accessors/src/Exceptions/SeedException.php b/seed/php-sdk/unions/property-accessors/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/unions/property-accessors/src/Exceptions/SeedException.php +++ b/seed/php-sdk/unions/property-accessors/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/unions/property-accessors/src/SeedClient.php b/seed/php-sdk/unions/property-accessors/src/SeedClient.php index fd07d97e9231..8cc0c47fe323 100644 --- a/seed/php-sdk/unions/property-accessors/src/SeedClient.php +++ b/seed/php-sdk/unions/property-accessors/src/SeedClient.php @@ -7,7 +7,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var BigunionClient $bigunion @@ -46,26 +46,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->bigunion = new BigunionClient($this->client, $this->options); $this->union = new UnionClient($this->client, $this->options); } diff --git a/seed/php-sdk/unions/property-accessors/src/Types/Traits/Foo.php b/seed/php-sdk/unions/property-accessors/src/Types/Traits/Foo.php index c5b21207ad32..b6c9323a4330 100644 --- a/seed/php-sdk/unions/property-accessors/src/Types/Traits/Foo.php +++ b/seed/php-sdk/unions/property-accessors/src/Types/Traits/Foo.php @@ -7,7 +7,7 @@ /** * @property string $name */ -trait Foo +trait Foo { /** * @var string $name @@ -18,12 +18,17 @@ trait Foo /** * @return string */ - public function getName(): string { - return $this->name;} + public function getName(): string + { + return $this->name; + } /** * @param string $value */ - public function setName(string $value): self { - $this->name = $value;return $this;} + public function setName(string $value): self + { + $this->name = $value; + return $this; + } } diff --git a/seed/php-sdk/unions/property-accessors/src/Types/Types/Bar.php b/seed/php-sdk/unions/property-accessors/src/Types/Types/Bar.php index 23b236837e88..5481aec946d8 100644 --- a/seed/php-sdk/unions/property-accessors/src/Types/Types/Bar.php +++ b/seed/php-sdk/unions/property-accessors/src/Types/Types/Bar.php @@ -20,27 +20,32 @@ class Bar extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; } /** * @return string */ - public function getName(): string { - return $this->name;} + public function getName(): string + { + return $this->name; + } /** * @param string $value */ - public function setName(string $value): self { - $this->name = $value;return $this;} + public function setName(string $value): self + { + $this->name = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/property-accessors/src/Types/Types/FirstItemType.php b/seed/php-sdk/unions/property-accessors/src/Types/Types/FirstItemType.php index 69100890a139..f4cb2af6e80e 100644 --- a/seed/php-sdk/unions/property-accessors/src/Types/Types/FirstItemType.php +++ b/seed/php-sdk/unions/property-accessors/src/Types/Types/FirstItemType.php @@ -27,39 +27,50 @@ class FirstItemType extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->type = $values['type'] ?? null;$this->name = $values['name']; + ) { + $this->type = $values['type'] ?? null; + $this->name = $values['name']; } /** * @return ?'firstItemType' */ - public function getType(): ?string { - return $this->type;} + public function getType(): ?string + { + return $this->type; + } /** * @param ?'firstItemType' $value */ - public function setType(?string $value = null): self { - $this->type = $value;return $this;} + public function setType(?string $value = null): self + { + $this->type = $value; + return $this; + } /** * @return string */ - public function getName(): string { - return $this->name;} + public function getName(): string + { + return $this->name; + } /** * @param string $value */ - public function setName(string $value): self { - $this->name = $value;return $this;} + public function setName(string $value): self + { + $this->name = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/property-accessors/src/Types/Types/Foo.php b/seed/php-sdk/unions/property-accessors/src/Types/Types/Foo.php index a053ec842a8c..1ebc47a15c53 100644 --- a/seed/php-sdk/unions/property-accessors/src/Types/Types/Foo.php +++ b/seed/php-sdk/unions/property-accessors/src/Types/Types/Foo.php @@ -20,27 +20,32 @@ class Foo extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; } /** * @return string */ - public function getName(): string { - return $this->name;} + public function getName(): string + { + return $this->name; + } /** * @param string $value */ - public function setName(string $value): self { - $this->name = $value;return $this;} + public function setName(string $value): self + { + $this->name = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/property-accessors/src/Types/Types/FooExtended.php b/seed/php-sdk/unions/property-accessors/src/Types/Types/FooExtended.php index 0e22ac0e80d0..e7726daabfb1 100644 --- a/seed/php-sdk/unions/property-accessors/src/Types/Types/FooExtended.php +++ b/seed/php-sdk/unions/property-accessors/src/Types/Types/FooExtended.php @@ -24,27 +24,33 @@ class FooExtended extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->name = $values['name'];$this->age = $values['age']; + ) { + $this->name = $values['name']; + $this->age = $values['age']; } /** * @return int */ - public function getAge(): int { - return $this->age;} + public function getAge(): int + { + return $this->age; + } /** * @param int $value */ - public function setAge(int $value): self { - $this->age = $value;return $this;} + public function setAge(int $value): self + { + $this->age = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/property-accessors/src/Types/Types/SecondItemType.php b/seed/php-sdk/unions/property-accessors/src/Types/Types/SecondItemType.php index 8b0f9d482b54..d84547c75c52 100644 --- a/seed/php-sdk/unions/property-accessors/src/Types/Types/SecondItemType.php +++ b/seed/php-sdk/unions/property-accessors/src/Types/Types/SecondItemType.php @@ -27,39 +27,50 @@ class SecondItemType extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->type = $values['type'] ?? null;$this->title = $values['title']; + ) { + $this->type = $values['type'] ?? null; + $this->title = $values['title']; } /** * @return ?'secondItemType' */ - public function getType(): ?string { - return $this->type;} + public function getType(): ?string + { + return $this->type; + } /** * @param ?'secondItemType' $value */ - public function setType(?string $value = null): self { - $this->type = $value;return $this;} + public function setType(?string $value = null): self + { + $this->type = $value; + return $this; + } /** * @return string */ - public function getTitle(): string { - return $this->title;} + public function getTitle(): string + { + return $this->title; + } /** * @param string $value */ - public function setTitle(string $value): self { - $this->title = $value;return $this;} + public function setTitle(string $value): self + { + $this->title = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/property-accessors/src/Types/Types/Union.php b/seed/php-sdk/unions/property-accessors/src/Types/Types/Union.php index f81514fceb62..972499e93830 100644 --- a/seed/php-sdk/unions/property-accessors/src/Types/Types/Union.php +++ b/seed/php-sdk/unions/property-accessors/src/Types/Types/Union.php @@ -45,9 +45,9 @@ class Union extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** @@ -57,8 +57,10 @@ private function __construct( * |'_unknown' * ) */ - public function getType(): string { - return $this->type;} + public function getType(): string + { + return $this->type; + } /** * @return ( @@ -67,14 +69,17 @@ public function getType(): string { * |mixed * ) */ - public function getValue(): mixed { - return $this->value;} + public function getValue(): mixed + { + return $this->value; + } /** * @param Foo $foo * @return Union */ - public static function foo(Foo $foo): Union { + public static function foo(Foo $foo): Union + { return new Union([ 'type' => 'foo', 'value' => $foo, @@ -85,7 +90,8 @@ public static function foo(Foo $foo): Union { * @param Bar $bar * @return Union */ - public static function bar(Bar $bar): Union { + public static function bar(Bar $bar): Union + { return new Union([ 'type' => 'bar', 'value' => $bar, @@ -95,61 +101,67 @@ public static function bar(Bar $bar): Union { /** * @return bool */ - public function isFoo(): bool { - return $this->value instanceof Foo&& $this->type === 'foo'; + public function isFoo(): bool + { + return $this->value instanceof Foo && $this->type === 'foo'; } /** * @return Foo */ - public function asFoo(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo')){ + public function asFoo(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo')) { throw new Exception( "Expected foo; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isBar(): bool { - return $this->value instanceof Bar&& $this->type === 'bar'; + public function isBar(): bool + { + return $this->value instanceof Bar && $this->type === 'bar'; } /** * @return Bar */ - public function asBar(): Bar { - if (!($this->value instanceof Bar&& $this->type === 'bar')){ + public function asBar(): Bar + { + if (!($this->value instanceof Bar && $this->type === 'bar')) { throw new Exception( "Expected bar; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'foo': $value = $this->asFoo()->jsonSerialize(); $result['foo'] = $value; @@ -160,26 +172,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -188,30 +201,31 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'foo': - if (!array_key_exists('foo', $data)){ + if (!array_key_exists('foo', $data)) { throw new Exception( "JSON data is missing property 'foo'", ); } - - if (!(is_array($data['foo']))){ + + if (!(is_array($data['foo']))) { throw new Exception( "Expected property 'foo' in JSON data to be array, instead received " . get_debug_type($data['foo']), ); @@ -219,13 +233,13 @@ public static function jsonDeserialize(array $data): static { $args['value'] = Foo::jsonDeserialize($data['foo']); break; case 'bar': - if (!array_key_exists('bar', $data)){ + if (!array_key_exists('bar', $data)) { throw new Exception( "JSON data is missing property 'bar'", ); } - - if (!(is_array($data['bar']))){ + + if (!(is_array($data['bar']))) { throw new Exception( "Expected property 'bar' in JSON data to be array, instead received " . get_debug_type($data['bar']), ); @@ -237,7 +251,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithBaseProperties.php b/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithBaseProperties.php index ef1f55a30930..912172682ae2 100644 --- a/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithBaseProperties.php +++ b/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithBaseProperties.php @@ -54,22 +54,28 @@ class UnionWithBaseProperties extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->id = $values['id'];$this->type = $values['type'];$this->value = $values['value']; + ) { + $this->id = $values['id']; + $this->type = $values['type']; + $this->value = $values['value']; } /** * @return string */ - public function getId(): string { - return $this->id;} + public function getId(): string + { + return $this->id; + } /** * @param string $value */ - public function setId(string $value): self { - $this->id = $value;return $this;} + public function setId(string $value): self + { + $this->id = $value; + return $this; + } /** * @return ( @@ -79,8 +85,10 @@ public function setId(string $value): self { * |'_unknown' * ) */ - public function getType(): string { - return $this->type;} + public function getType(): string + { + return $this->type; + } /** * @return ( @@ -90,15 +98,18 @@ public function getType(): string { * |mixed * ) */ - public function getValue(): mixed { - return $this->value;} + public function getValue(): mixed + { + return $this->value; + } /** * @param string $id * @param int $integer * @return UnionWithBaseProperties */ - public static function integer(string $id, int $integer): UnionWithBaseProperties { + public static function integer(string $id, int $integer): UnionWithBaseProperties + { return new UnionWithBaseProperties([ 'id' => $id, 'type' => 'integer', @@ -111,7 +122,8 @@ public static function integer(string $id, int $integer): UnionWithBasePropertie * @param string $string * @return UnionWithBaseProperties */ - public static function string(string $id, string $string): UnionWithBaseProperties { + public static function string(string $id, string $string): UnionWithBaseProperties + { return new UnionWithBaseProperties([ 'id' => $id, 'type' => 'string', @@ -124,7 +136,8 @@ public static function string(string $id, string $string): UnionWithBaseProperti * @param Foo $foo * @return UnionWithBaseProperties */ - public static function foo(string $id, Foo $foo): UnionWithBaseProperties { + public static function foo(string $id, Foo $foo): UnionWithBaseProperties + { return new UnionWithBaseProperties([ 'id' => $id, 'type' => 'foo', @@ -135,81 +148,89 @@ public static function foo(string $id, Foo $foo): UnionWithBaseProperties { /** * @return bool */ - public function isInteger(): bool { - return is_int($this->value)&& $this->type === 'integer'; + public function isInteger(): bool + { + return is_int($this->value) && $this->type === 'integer'; } /** * @return int */ - public function asInteger(): int { - if (!(is_int($this->value)&& $this->type === 'integer')){ + public function asInteger(): int + { + if (!(is_int($this->value) && $this->type === 'integer')) { throw new Exception( "Expected integer; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isString(): bool { - return is_string($this->value)&& $this->type === 'string'; + public function isString(): bool + { + return is_string($this->value) && $this->type === 'string'; } /** * @return string */ - public function asString(): string { - if (!(is_string($this->value)&& $this->type === 'string')){ + public function asString(): string + { + if (!(is_string($this->value) && $this->type === 'string')) { throw new Exception( "Expected string; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isFoo(): bool { - return $this->value instanceof Foo&& $this->type === 'foo'; + public function isFoo(): bool + { + return $this->value instanceof Foo && $this->type === 'foo'; } /** * @return Foo */ - public function asFoo(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo')){ + public function asFoo(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo')) { throw new Exception( "Expected foo; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'integer': $value = $this->value; $result['integer'] = $value; @@ -224,26 +245,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -252,50 +274,51 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('id', $data)){ + if (!array_key_exists('id', $data)) { throw new Exception( "JSON data is missing property 'id'", ); } - if (!(is_string($data['id']))){ + if (!(is_string($data['id']))) { throw new Exception( "Expected property 'id' in JSON data to be string, instead received " . get_debug_type($data['id']), ); } $args['id'] = $data['id']; - - if (!array_key_exists('type', $data)){ + + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'integer': - if (!array_key_exists('integer', $data)){ + if (!array_key_exists('integer', $data)) { throw new Exception( "JSON data is missing property 'integer'", ); } - + $args['value'] = $data['integer']; break; case 'string': - if (!array_key_exists('string', $data)){ + if (!array_key_exists('string', $data)) { throw new Exception( "JSON data is missing property 'string'", ); } - + $args['value'] = $data['string']; break; case 'foo': @@ -306,7 +329,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithDiscriminant.php b/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithDiscriminant.php index 4c4ec4c02977..7e99264021ee 100644 --- a/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithDiscriminant.php +++ b/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithDiscriminant.php @@ -42,9 +42,9 @@ class UnionWithDiscriminant extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** @@ -54,8 +54,10 @@ private function __construct( * |'_unknown' * ) */ - public function getType(): string { - return $this->type;} + public function getType(): string + { + return $this->type; + } /** * @return ( @@ -64,14 +66,17 @@ public function getType(): string { * |mixed * ) */ - public function getValue(): mixed { - return $this->value;} + public function getValue(): mixed + { + return $this->value; + } /** * @param Foo $foo * @return UnionWithDiscriminant */ - public static function foo(Foo $foo): UnionWithDiscriminant { + public static function foo(Foo $foo): UnionWithDiscriminant + { return new UnionWithDiscriminant([ 'type' => 'foo', 'value' => $foo, @@ -82,7 +87,8 @@ public static function foo(Foo $foo): UnionWithDiscriminant { * @param Bar $bar * @return UnionWithDiscriminant */ - public static function bar(Bar $bar): UnionWithDiscriminant { + public static function bar(Bar $bar): UnionWithDiscriminant + { return new UnionWithDiscriminant([ 'type' => 'bar', 'value' => $bar, @@ -92,61 +98,67 @@ public static function bar(Bar $bar): UnionWithDiscriminant { /** * @return bool */ - public function isFoo(): bool { - return $this->value instanceof Foo&& $this->type === 'foo'; + public function isFoo(): bool + { + return $this->value instanceof Foo && $this->type === 'foo'; } /** * @return Foo */ - public function asFoo(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo')){ + public function asFoo(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo')) { throw new Exception( "Expected foo; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isBar(): bool { - return $this->value instanceof Bar&& $this->type === 'bar'; + public function isBar(): bool + { + return $this->value instanceof Bar && $this->type === 'bar'; } /** * @return Bar */ - public function asBar(): Bar { - if (!($this->value instanceof Bar&& $this->type === 'bar')){ + public function asBar(): Bar + { + if (!($this->value instanceof Bar && $this->type === 'bar')) { throw new Exception( "Expected bar; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['_type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'foo': $value = $this->asFoo()->jsonSerialize(); $result['foo'] = $value; @@ -157,26 +169,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -185,30 +198,31 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('_type', $data)){ + if (!array_key_exists('_type', $data)) { throw new Exception( "JSON data is missing property '_type'", ); } $type = $data['_type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['_type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'foo': - if (!array_key_exists('foo', $data)){ + if (!array_key_exists('foo', $data)) { throw new Exception( "JSON data is missing property 'foo'", ); } - - if (!(is_array($data['foo']))){ + + if (!(is_array($data['foo']))) { throw new Exception( "Expected property 'foo' in JSON data to be array, instead received " . get_debug_type($data['foo']), ); @@ -216,13 +230,13 @@ public static function jsonDeserialize(array $data): static { $args['value'] = Foo::jsonDeserialize($data['foo']); break; case 'bar': - if (!array_key_exists('bar', $data)){ + if (!array_key_exists('bar', $data)) { throw new Exception( "JSON data is missing property 'bar'", ); } - - if (!(is_array($data['bar']))){ + + if (!(is_array($data['bar']))) { throw new Exception( "Expected property 'bar' in JSON data to be array, instead received " . get_debug_type($data['bar']), ); @@ -234,7 +248,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithDuplicatePrimitive.php b/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithDuplicatePrimitive.php index 2c15eb8bed46..f8eb50c75ad4 100644 --- a/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithDuplicatePrimitive.php +++ b/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithDuplicatePrimitive.php @@ -46,9 +46,9 @@ class UnionWithDuplicatePrimitive extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** @@ -60,8 +60,10 @@ private function __construct( * |'_unknown' * ) */ - public function getType(): string { - return $this->type;} + public function getType(): string + { + return $this->type; + } /** * @return ( @@ -70,14 +72,17 @@ public function getType(): string { * |mixed * ) */ - public function getValue(): mixed { - return $this->value;} + public function getValue(): mixed + { + return $this->value; + } /** * @param int $integer1 * @return UnionWithDuplicatePrimitive */ - public static function integer1(int $integer1): UnionWithDuplicatePrimitive { + public static function integer1(int $integer1): UnionWithDuplicatePrimitive + { return new UnionWithDuplicatePrimitive([ 'type' => 'integer1', 'value' => $integer1, @@ -88,7 +93,8 @@ public static function integer1(int $integer1): UnionWithDuplicatePrimitive { * @param int $integer2 * @return UnionWithDuplicatePrimitive */ - public static function integer2(int $integer2): UnionWithDuplicatePrimitive { + public static function integer2(int $integer2): UnionWithDuplicatePrimitive + { return new UnionWithDuplicatePrimitive([ 'type' => 'integer2', 'value' => $integer2, @@ -99,7 +105,8 @@ public static function integer2(int $integer2): UnionWithDuplicatePrimitive { * @param string $string1 * @return UnionWithDuplicatePrimitive */ - public static function string1(string $string1): UnionWithDuplicatePrimitive { + public static function string1(string $string1): UnionWithDuplicatePrimitive + { return new UnionWithDuplicatePrimitive([ 'type' => 'string1', 'value' => $string1, @@ -110,7 +117,8 @@ public static function string1(string $string1): UnionWithDuplicatePrimitive { * @param string $string2 * @return UnionWithDuplicatePrimitive */ - public static function string2(string $string2): UnionWithDuplicatePrimitive { + public static function string2(string $string2): UnionWithDuplicatePrimitive + { return new UnionWithDuplicatePrimitive([ 'type' => 'string2', 'value' => $string2, @@ -120,101 +128,111 @@ public static function string2(string $string2): UnionWithDuplicatePrimitive { /** * @return bool */ - public function isInteger1(): bool { - return is_int($this->value)&& $this->type === 'integer1'; + public function isInteger1(): bool + { + return is_int($this->value) && $this->type === 'integer1'; } /** * @return int */ - public function asInteger1(): int { - if (!(is_int($this->value)&& $this->type === 'integer1')){ + public function asInteger1(): int + { + if (!(is_int($this->value) && $this->type === 'integer1')) { throw new Exception( "Expected integer1; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isInteger2(): bool { - return is_int($this->value)&& $this->type === 'integer2'; + public function isInteger2(): bool + { + return is_int($this->value) && $this->type === 'integer2'; } /** * @return int */ - public function asInteger2(): int { - if (!(is_int($this->value)&& $this->type === 'integer2')){ + public function asInteger2(): int + { + if (!(is_int($this->value) && $this->type === 'integer2')) { throw new Exception( "Expected integer2; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isString1(): bool { - return is_string($this->value)&& $this->type === 'string1'; + public function isString1(): bool + { + return is_string($this->value) && $this->type === 'string1'; } /** * @return string */ - public function asString1(): string { - if (!(is_string($this->value)&& $this->type === 'string1')){ + public function asString1(): string + { + if (!(is_string($this->value) && $this->type === 'string1')) { throw new Exception( "Expected string1; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isString2(): bool { - return is_string($this->value)&& $this->type === 'string2'; + public function isString2(): bool + { + return is_string($this->value) && $this->type === 'string2'; } /** * @return string */ - public function asString2(): string { - if (!(is_string($this->value)&& $this->type === 'string2')){ + public function asString2(): string + { + if (!(is_string($this->value) && $this->type === 'string2')) { throw new Exception( "Expected string2; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'integer1': $value = $this->value; $result['integer1'] = $value; @@ -233,26 +251,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -261,56 +280,57 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'integer1': - if (!array_key_exists('integer1', $data)){ + if (!array_key_exists('integer1', $data)) { throw new Exception( "JSON data is missing property 'integer1'", ); } - + $args['value'] = $data['integer1']; break; case 'integer2': - if (!array_key_exists('integer2', $data)){ + if (!array_key_exists('integer2', $data)) { throw new Exception( "JSON data is missing property 'integer2'", ); } - + $args['value'] = $data['integer2']; break; case 'string1': - if (!array_key_exists('string1', $data)){ + if (!array_key_exists('string1', $data)) { throw new Exception( "JSON data is missing property 'string1'", ); } - + $args['value'] = $data['string1']; break; case 'string2': - if (!array_key_exists('string2', $data)){ + if (!array_key_exists('string2', $data)) { throw new Exception( "JSON data is missing property 'string2'", ); } - + $args['value'] = $data['string2']; break; case '_unknown': @@ -318,7 +338,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithDuplicateTypes.php b/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithDuplicateTypes.php index d33250846d95..3ffc3ab46ca6 100644 --- a/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithDuplicateTypes.php +++ b/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithDuplicateTypes.php @@ -40,9 +40,9 @@ class UnionWithDuplicateTypes extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** @@ -52,8 +52,10 @@ private function __construct( * |'_unknown' * ) */ - public function getType(): string { - return $this->type;} + public function getType(): string + { + return $this->type; + } /** * @return ( @@ -61,14 +63,17 @@ public function getType(): string { * |mixed * ) */ - public function getValue(): mixed { - return $this->value;} + public function getValue(): mixed + { + return $this->value; + } /** * @param Foo $foo1 * @return UnionWithDuplicateTypes */ - public static function foo1(Foo $foo1): UnionWithDuplicateTypes { + public static function foo1(Foo $foo1): UnionWithDuplicateTypes + { return new UnionWithDuplicateTypes([ 'type' => 'foo1', 'value' => $foo1, @@ -79,7 +84,8 @@ public static function foo1(Foo $foo1): UnionWithDuplicateTypes { * @param Foo $foo2 * @return UnionWithDuplicateTypes */ - public static function foo2(Foo $foo2): UnionWithDuplicateTypes { + public static function foo2(Foo $foo2): UnionWithDuplicateTypes + { return new UnionWithDuplicateTypes([ 'type' => 'foo2', 'value' => $foo2, @@ -89,61 +95,67 @@ public static function foo2(Foo $foo2): UnionWithDuplicateTypes { /** * @return bool */ - public function isFoo1(): bool { - return $this->value instanceof Foo&& $this->type === 'foo1'; + public function isFoo1(): bool + { + return $this->value instanceof Foo && $this->type === 'foo1'; } /** * @return Foo */ - public function asFoo1(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo1')){ + public function asFoo1(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo1')) { throw new Exception( "Expected foo1; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isFoo2(): bool { - return $this->value instanceof Foo&& $this->type === 'foo2'; + public function isFoo2(): bool + { + return $this->value instanceof Foo && $this->type === 'foo2'; } /** * @return Foo */ - public function asFoo2(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo2')){ + public function asFoo2(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo2')) { throw new Exception( "Expected foo2; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'foo1': $value = $this->asFoo1()->jsonSerialize(); $result = array_merge($value, $result); @@ -154,26 +166,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -182,22 +195,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'foo1': $args['value'] = Foo::jsonDeserialize($data); break; @@ -209,7 +223,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithDuplicativeDiscriminants.php b/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithDuplicativeDiscriminants.php index c677debaf655..7777581a7745 100644 --- a/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithDuplicativeDiscriminants.php +++ b/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithDuplicativeDiscriminants.php @@ -42,9 +42,9 @@ class UnionWithDuplicativeDiscriminants extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** @@ -54,8 +54,10 @@ private function __construct( * |'_unknown' * ) */ - public function getType(): string { - return $this->type;} + public function getType(): string + { + return $this->type; + } /** * @return ( @@ -64,14 +66,17 @@ public function getType(): string { * |mixed * ) */ - public function getValue(): mixed { - return $this->value;} + public function getValue(): mixed + { + return $this->value; + } /** * @param FirstItemType $firstItemType * @return UnionWithDuplicativeDiscriminants */ - public static function firstItemType(FirstItemType $firstItemType): UnionWithDuplicativeDiscriminants { + public static function firstItemType(FirstItemType $firstItemType): UnionWithDuplicativeDiscriminants + { return new UnionWithDuplicativeDiscriminants([ 'type' => 'firstItemType', 'value' => $firstItemType, @@ -82,7 +87,8 @@ public static function firstItemType(FirstItemType $firstItemType): UnionWithDup * @param SecondItemType $secondItemType * @return UnionWithDuplicativeDiscriminants */ - public static function secondItemType(SecondItemType $secondItemType): UnionWithDuplicativeDiscriminants { + public static function secondItemType(SecondItemType $secondItemType): UnionWithDuplicativeDiscriminants + { return new UnionWithDuplicativeDiscriminants([ 'type' => 'secondItemType', 'value' => $secondItemType, @@ -92,61 +98,67 @@ public static function secondItemType(SecondItemType $secondItemType): UnionWith /** * @return bool */ - public function isFirstItemType(): bool { - return $this->value instanceof FirstItemType&& $this->type === 'firstItemType'; + public function isFirstItemType(): bool + { + return $this->value instanceof FirstItemType && $this->type === 'firstItemType'; } /** * @return FirstItemType */ - public function asFirstItemType(): FirstItemType { - if (!($this->value instanceof FirstItemType&& $this->type === 'firstItemType')){ + public function asFirstItemType(): FirstItemType + { + if (!($this->value instanceof FirstItemType && $this->type === 'firstItemType')) { throw new Exception( "Expected firstItemType; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isSecondItemType(): bool { - return $this->value instanceof SecondItemType&& $this->type === 'secondItemType'; + public function isSecondItemType(): bool + { + return $this->value instanceof SecondItemType && $this->type === 'secondItemType'; } /** * @return SecondItemType */ - public function asSecondItemType(): SecondItemType { - if (!($this->value instanceof SecondItemType&& $this->type === 'secondItemType')){ + public function asSecondItemType(): SecondItemType + { + if (!($this->value instanceof SecondItemType && $this->type === 'secondItemType')) { throw new Exception( "Expected secondItemType; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'firstItemType': $value = $this->asFirstItemType()->jsonSerialize(); $result = array_merge($value, $result); @@ -157,26 +169,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -185,22 +198,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'firstItemType': $args['value'] = FirstItemType::jsonDeserialize($data); break; @@ -212,7 +226,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithLiteral.php b/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithLiteral.php index 21fb087bbf14..cd84dfed10ad 100644 --- a/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithLiteral.php +++ b/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithLiteral.php @@ -46,22 +46,28 @@ class UnionWithLiteral extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->base = $values['base'];$this->type = $values['type'];$this->value = $values['value']; + ) { + $this->base = $values['base']; + $this->type = $values['type']; + $this->value = $values['value']; } /** * @return 'base' */ - public function getBase(): string { - return $this->base;} + public function getBase(): string + { + return $this->base; + } /** * @param 'base' $value */ - public function setBase(string $value): self { - $this->base = $value;return $this;} + public function setBase(string $value): self + { + $this->base = $value; + return $this; + } /** * @return ( @@ -69,8 +75,10 @@ public function setBase(string $value): self { * |'_unknown' * ) */ - public function getType(): string { - return $this->type;} + public function getType(): string + { + return $this->type; + } /** * @return ( @@ -78,15 +86,18 @@ public function getType(): string { * |mixed * ) */ - public function getValue(): mixed { - return $this->value;} + public function getValue(): mixed + { + return $this->value; + } /** * @param 'base' $base * @param 'fern' $fern * @return UnionWithLiteral */ - public static function fern(string $base, string $fern): UnionWithLiteral { + public static function fern(string $base, string $fern): UnionWithLiteral + { return new UnionWithLiteral([ 'base' => $base, 'type' => 'fern', @@ -97,67 +108,72 @@ public static function fern(string $base, string $fern): UnionWithLiteral { /** * @return bool */ - public function isFern(): bool { - return $this->value === 'fern'&& $this->type === 'fern'; + public function isFern(): bool + { + return $this->value === 'fern' && $this->type === 'fern'; } /** * @return 'fern' */ - public function asFern(): string { - if (!($this->value === 'fern'&& $this->type === 'fern')){ + public function asFern(): string + { + if (!($this->value === 'fern' && $this->type === 'fern')) { throw new Exception( "Expected fern; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'fern': $value = $this->value; $result['fern'] = $value; break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -166,41 +182,42 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('base', $data)){ + if (!array_key_exists('base', $data)) { throw new Exception( "JSON data is missing property 'base'", ); } - if (!($data['base'] === 'base')){ + if (!($data['base'] === 'base')) { throw new Exception( "Expected property 'base' in JSON data to be 'base', instead received " . get_debug_type($data['base']), ); } $args['base'] = $data['base']; - - if (!array_key_exists('type', $data)){ + + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'fern': - if (!array_key_exists('fern', $data)){ + if (!array_key_exists('fern', $data)) { throw new Exception( "JSON data is missing property 'fern'", ); } - + $args['value'] = $data['fern']; break; case '_unknown': @@ -208,7 +225,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithMultipleNoProperties.php b/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithMultipleNoProperties.php index a43521303428..07340e32270d 100644 --- a/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithMultipleNoProperties.php +++ b/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithMultipleNoProperties.php @@ -44,9 +44,9 @@ class UnionWithMultipleNoProperties extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** @@ -57,8 +57,10 @@ private function __construct( * |'_unknown' * ) */ - public function getType(): string { - return $this->type;} + public function getType(): string + { + return $this->type; + } /** * @return ( @@ -67,14 +69,17 @@ public function getType(): string { * |mixed * ) */ - public function getValue(): mixed { - return $this->value;} + public function getValue(): mixed + { + return $this->value; + } /** * @param Foo $foo * @return UnionWithMultipleNoProperties */ - public static function foo(Foo $foo): UnionWithMultipleNoProperties { + public static function foo(Foo $foo): UnionWithMultipleNoProperties + { return new UnionWithMultipleNoProperties([ 'type' => 'foo', 'value' => $foo, @@ -84,7 +89,8 @@ public static function foo(Foo $foo): UnionWithMultipleNoProperties { /** * @return UnionWithMultipleNoProperties */ - public static function empty1(): UnionWithMultipleNoProperties { + public static function empty1(): UnionWithMultipleNoProperties + { return new UnionWithMultipleNoProperties([ 'type' => 'empty1', 'value' => null, @@ -94,7 +100,8 @@ public static function empty1(): UnionWithMultipleNoProperties { /** * @return UnionWithMultipleNoProperties */ - public static function empty2(): UnionWithMultipleNoProperties { + public static function empty2(): UnionWithMultipleNoProperties + { return new UnionWithMultipleNoProperties([ 'type' => 'empty2', 'value' => null, @@ -104,55 +111,61 @@ public static function empty2(): UnionWithMultipleNoProperties { /** * @return bool */ - public function isFoo(): bool { - return $this->value instanceof Foo&& $this->type === 'foo'; + public function isFoo(): bool + { + return $this->value instanceof Foo && $this->type === 'foo'; } /** * @return Foo */ - public function asFoo(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo')){ + public function asFoo(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo')) { throw new Exception( "Expected foo; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isEmpty1(): bool { - return is_null($this->value)&& $this->type === 'empty1'; + public function isEmpty1(): bool + { + return is_null($this->value) && $this->type === 'empty1'; } /** * @return bool */ - public function isEmpty2(): bool { - return is_null($this->value)&& $this->type === 'empty2'; + public function isEmpty2(): bool + { + return is_null($this->value) && $this->type === 'empty2'; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'foo': $value = $this->asFoo()->jsonSerialize(); $result = array_merge($value, $result); @@ -165,26 +178,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -193,22 +207,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'foo': $args['value'] = Foo::jsonDeserialize($data); break; @@ -223,7 +238,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithNoProperties.php b/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithNoProperties.php index 726ca678c2a0..ca17ccc69854 100644 --- a/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithNoProperties.php +++ b/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithNoProperties.php @@ -42,9 +42,9 @@ class UnionWithNoProperties extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** @@ -54,8 +54,10 @@ private function __construct( * |'_unknown' * ) */ - public function getType(): string { - return $this->type;} + public function getType(): string + { + return $this->type; + } /** * @return ( @@ -64,14 +66,17 @@ public function getType(): string { * |mixed * ) */ - public function getValue(): mixed { - return $this->value;} + public function getValue(): mixed + { + return $this->value; + } /** * @param Foo $foo * @return UnionWithNoProperties */ - public static function foo(Foo $foo): UnionWithNoProperties { + public static function foo(Foo $foo): UnionWithNoProperties + { return new UnionWithNoProperties([ 'type' => 'foo', 'value' => $foo, @@ -81,7 +86,8 @@ public static function foo(Foo $foo): UnionWithNoProperties { /** * @return UnionWithNoProperties */ - public static function empty(): UnionWithNoProperties { + public static function empty(): UnionWithNoProperties + { return new UnionWithNoProperties([ 'type' => 'empty', 'value' => null, @@ -91,48 +97,53 @@ public static function empty(): UnionWithNoProperties { /** * @return bool */ - public function isFoo(): bool { - return $this->value instanceof Foo&& $this->type === 'foo'; + public function isFoo(): bool + { + return $this->value instanceof Foo && $this->type === 'foo'; } /** * @return Foo */ - public function asFoo(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo')){ + public function asFoo(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo')) { throw new Exception( "Expected foo; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isEmpty(): bool { - return is_null($this->value)&& $this->type === 'empty'; + public function isEmpty(): bool + { + return is_null($this->value) && $this->type === 'empty'; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'foo': $value = $this->asFoo()->jsonSerialize(); $result = array_merge($value, $result); @@ -142,26 +153,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -170,22 +182,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'foo': $args['value'] = Foo::jsonDeserialize($data); break; @@ -197,7 +210,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithOptionalTime.php b/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithOptionalTime.php index b9ce034ca0b5..b883f605e520 100644 --- a/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithOptionalTime.php +++ b/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithOptionalTime.php @@ -44,9 +44,9 @@ class UnionWithOptionalTime extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** @@ -56,8 +56,10 @@ private function __construct( * |'_unknown' * ) */ - public function getType(): string { - return $this->type;} + public function getType(): string + { + return $this->type; + } /** * @return ( @@ -66,14 +68,17 @@ public function getType(): string { * |null * ) */ - public function getValue(): mixed { - return $this->value;} + public function getValue(): mixed + { + return $this->value; + } /** * @param ?DateTime $date * @return UnionWithOptionalTime */ - public static function date(?DateTime $date = null): UnionWithOptionalTime { + public static function date(?DateTime $date = null): UnionWithOptionalTime + { return new UnionWithOptionalTime([ 'type' => 'date', 'value' => $date, @@ -84,7 +89,8 @@ public static function date(?DateTime $date = null): UnionWithOptionalTime { * @param ?DateTime $datetime * @return UnionWithOptionalTime */ - public static function datetime(?DateTime $datetime = null): UnionWithOptionalTime { + public static function datetime(?DateTime $datetime = null): UnionWithOptionalTime + { return new UnionWithOptionalTime([ 'type' => 'datetime', 'value' => $datetime, @@ -94,97 +100,104 @@ public static function datetime(?DateTime $datetime = null): UnionWithOptionalTi /** * @return bool */ - public function isDate(): bool { - return (is_null($this->value) || $this->value instanceof DateTime)&& $this->type === 'date'; + public function isDate(): bool + { + return (is_null($this->value) || $this->value instanceof DateTime) && $this->type === 'date'; } /** * @return ?DateTime */ - public function asDate(): ?DateTime { - if (!((is_null($this->value) || $this->value instanceof DateTime)&& $this->type === 'date')){ + public function asDate(): ?DateTime + { + if (!((is_null($this->value) || $this->value instanceof DateTime) && $this->type === 'date')) { throw new Exception( "Expected date; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isDatetime(): bool { - return (is_null($this->value) || $this->value instanceof DateTime)&& $this->type === 'datetime'; + public function isDatetime(): bool + { + return (is_null($this->value) || $this->value instanceof DateTime) && $this->type === 'datetime'; } /** * @return ?DateTime */ - public function asDatetime(): ?DateTime { - if (!((is_null($this->value) || $this->value instanceof DateTime)&& $this->type === 'datetime')){ + public function asDatetime(): ?DateTime + { + if (!((is_null($this->value) || $this->value instanceof DateTime) && $this->type === 'datetime')) { throw new Exception( "Expected datetime; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'date': $value = $this->asDate(); - if (!is_null($value)){ + if (!is_null($value)) { $value = JsonSerializer::serializeDate($value); } $result['date'] = $value; break; case 'datetime': $value = $this->asDatetime(); - if (!is_null($value)){ + if (!is_null($value)) { $value = JsonSerializer::serializeDateTime($value); } $result['datetime'] = $value; break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -193,38 +206,39 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'date': - if (!array_key_exists('date', $data)){ + if (!array_key_exists('date', $data)) { throw new Exception( "JSON data is missing property 'date'", ); } - + $args['value'] = $data['date']; break; case 'datetime': - if (!array_key_exists('datetime', $data)){ + if (!array_key_exists('datetime', $data)) { throw new Exception( "JSON data is missing property 'datetime'", ); } - + $args['value'] = $data['datetime']; break; case '_unknown': @@ -232,7 +246,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithPrimitive.php b/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithPrimitive.php index b63c7ae21d17..f65cd71a97a0 100644 --- a/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithPrimitive.php +++ b/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithPrimitive.php @@ -42,9 +42,9 @@ class UnionWithPrimitive extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** @@ -54,8 +54,10 @@ private function __construct( * |'_unknown' * ) */ - public function getType(): string { - return $this->type;} + public function getType(): string + { + return $this->type; + } /** * @return ( @@ -64,14 +66,17 @@ public function getType(): string { * |mixed * ) */ - public function getValue(): mixed { - return $this->value;} + public function getValue(): mixed + { + return $this->value; + } /** * @param int $integer * @return UnionWithPrimitive */ - public static function integer(int $integer): UnionWithPrimitive { + public static function integer(int $integer): UnionWithPrimitive + { return new UnionWithPrimitive([ 'type' => 'integer', 'value' => $integer, @@ -82,7 +87,8 @@ public static function integer(int $integer): UnionWithPrimitive { * @param string $string * @return UnionWithPrimitive */ - public static function string(string $string): UnionWithPrimitive { + public static function string(string $string): UnionWithPrimitive + { return new UnionWithPrimitive([ 'type' => 'string', 'value' => $string, @@ -92,61 +98,67 @@ public static function string(string $string): UnionWithPrimitive { /** * @return bool */ - public function isInteger(): bool { - return is_int($this->value)&& $this->type === 'integer'; + public function isInteger(): bool + { + return is_int($this->value) && $this->type === 'integer'; } /** * @return int */ - public function asInteger(): int { - if (!(is_int($this->value)&& $this->type === 'integer')){ + public function asInteger(): int + { + if (!(is_int($this->value) && $this->type === 'integer')) { throw new Exception( "Expected integer; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isString(): bool { - return is_string($this->value)&& $this->type === 'string'; + public function isString(): bool + { + return is_string($this->value) && $this->type === 'string'; } /** * @return string */ - public function asString(): string { - if (!(is_string($this->value)&& $this->type === 'string')){ + public function asString(): string + { + if (!(is_string($this->value) && $this->type === 'string')) { throw new Exception( "Expected string; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'integer': $value = $this->value; $result['integer'] = $value; @@ -157,26 +169,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -185,38 +198,39 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'integer': - if (!array_key_exists('integer', $data)){ + if (!array_key_exists('integer', $data)) { throw new Exception( "JSON data is missing property 'integer'", ); } - + $args['value'] = $data['integer']; break; case 'string': - if (!array_key_exists('string', $data)){ + if (!array_key_exists('string', $data)) { throw new Exception( "JSON data is missing property 'string'", ); } - + $args['value'] = $data['string']; break; case '_unknown': @@ -224,7 +238,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithSameNumberTypes.php b/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithSameNumberTypes.php index 969ead9e4b4a..de4d6c216408 100644 --- a/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithSameNumberTypes.php +++ b/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithSameNumberTypes.php @@ -44,9 +44,9 @@ class UnionWithSameNumberTypes extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** @@ -57,8 +57,10 @@ private function __construct( * |'_unknown' * ) */ - public function getType(): string { - return $this->type;} + public function getType(): string + { + return $this->type; + } /** * @return ( @@ -67,14 +69,17 @@ public function getType(): string { * |mixed * ) */ - public function getValue(): mixed { - return $this->value;} + public function getValue(): mixed + { + return $this->value; + } /** * @param int $positiveInt * @return UnionWithSameNumberTypes */ - public static function positiveInt(int $positiveInt): UnionWithSameNumberTypes { + public static function positiveInt(int $positiveInt): UnionWithSameNumberTypes + { return new UnionWithSameNumberTypes([ 'type' => 'positiveInt', 'value' => $positiveInt, @@ -85,7 +90,8 @@ public static function positiveInt(int $positiveInt): UnionWithSameNumberTypes { * @param int $negativeInt * @return UnionWithSameNumberTypes */ - public static function negativeInt(int $negativeInt): UnionWithSameNumberTypes { + public static function negativeInt(int $negativeInt): UnionWithSameNumberTypes + { return new UnionWithSameNumberTypes([ 'type' => 'negativeInt', 'value' => $negativeInt, @@ -96,7 +102,8 @@ public static function negativeInt(int $negativeInt): UnionWithSameNumberTypes { * @param float $anyNumber * @return UnionWithSameNumberTypes */ - public static function anyNumber(float $anyNumber): UnionWithSameNumberTypes { + public static function anyNumber(float $anyNumber): UnionWithSameNumberTypes + { return new UnionWithSameNumberTypes([ 'type' => 'anyNumber', 'value' => $anyNumber, @@ -106,81 +113,89 @@ public static function anyNumber(float $anyNumber): UnionWithSameNumberTypes { /** * @return bool */ - public function isPositiveInt(): bool { - return is_int($this->value)&& $this->type === 'positiveInt'; + public function isPositiveInt(): bool + { + return is_int($this->value) && $this->type === 'positiveInt'; } /** * @return int */ - public function asPositiveInt(): int { - if (!(is_int($this->value)&& $this->type === 'positiveInt')){ + public function asPositiveInt(): int + { + if (!(is_int($this->value) && $this->type === 'positiveInt')) { throw new Exception( "Expected positiveInt; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isNegativeInt(): bool { - return is_int($this->value)&& $this->type === 'negativeInt'; + public function isNegativeInt(): bool + { + return is_int($this->value) && $this->type === 'negativeInt'; } /** * @return int */ - public function asNegativeInt(): int { - if (!(is_int($this->value)&& $this->type === 'negativeInt')){ + public function asNegativeInt(): int + { + if (!(is_int($this->value) && $this->type === 'negativeInt')) { throw new Exception( "Expected negativeInt; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isAnyNumber(): bool { - return is_float($this->value)&& $this->type === 'anyNumber'; + public function isAnyNumber(): bool + { + return is_float($this->value) && $this->type === 'anyNumber'; } /** * @return float */ - public function asAnyNumber(): float { - if (!(is_float($this->value)&& $this->type === 'anyNumber')){ + public function asAnyNumber(): float + { + if (!(is_float($this->value) && $this->type === 'anyNumber')) { throw new Exception( "Expected anyNumber; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'positiveInt': $value = $this->value; $result['positiveInt'] = $value; @@ -195,26 +210,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -223,47 +239,48 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'positiveInt': - if (!array_key_exists('positiveInt', $data)){ + if (!array_key_exists('positiveInt', $data)) { throw new Exception( "JSON data is missing property 'positiveInt'", ); } - + $args['value'] = $data['positiveInt']; break; case 'negativeInt': - if (!array_key_exists('negativeInt', $data)){ + if (!array_key_exists('negativeInt', $data)) { throw new Exception( "JSON data is missing property 'negativeInt'", ); } - + $args['value'] = $data['negativeInt']; break; case 'anyNumber': - if (!array_key_exists('anyNumber', $data)){ + if (!array_key_exists('anyNumber', $data)) { throw new Exception( "JSON data is missing property 'anyNumber'", ); } - + $args['value'] = $data['anyNumber']; break; case '_unknown': @@ -271,7 +288,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithSameStringTypes.php b/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithSameStringTypes.php index 7642b5b425f3..2edd72464369 100644 --- a/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithSameStringTypes.php +++ b/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithSameStringTypes.php @@ -42,9 +42,9 @@ class UnionWithSameStringTypes extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** @@ -55,8 +55,10 @@ private function __construct( * |'_unknown' * ) */ - public function getType(): string { - return $this->type;} + public function getType(): string + { + return $this->type; + } /** * @return ( @@ -64,14 +66,17 @@ public function getType(): string { * |mixed * ) */ - public function getValue(): mixed { - return $this->value;} + public function getValue(): mixed + { + return $this->value; + } /** * @param string $customFormat * @return UnionWithSameStringTypes */ - public static function customFormat(string $customFormat): UnionWithSameStringTypes { + public static function customFormat(string $customFormat): UnionWithSameStringTypes + { return new UnionWithSameStringTypes([ 'type' => 'customFormat', 'value' => $customFormat, @@ -82,7 +87,8 @@ public static function customFormat(string $customFormat): UnionWithSameStringTy * @param string $regularString * @return UnionWithSameStringTypes */ - public static function regularString(string $regularString): UnionWithSameStringTypes { + public static function regularString(string $regularString): UnionWithSameStringTypes + { return new UnionWithSameStringTypes([ 'type' => 'regularString', 'value' => $regularString, @@ -93,7 +99,8 @@ public static function regularString(string $regularString): UnionWithSameString * @param string $patternString * @return UnionWithSameStringTypes */ - public static function patternString(string $patternString): UnionWithSameStringTypes { + public static function patternString(string $patternString): UnionWithSameStringTypes + { return new UnionWithSameStringTypes([ 'type' => 'patternString', 'value' => $patternString, @@ -103,81 +110,89 @@ public static function patternString(string $patternString): UnionWithSameString /** * @return bool */ - public function isCustomFormat(): bool { - return is_string($this->value)&& $this->type === 'customFormat'; + public function isCustomFormat(): bool + { + return is_string($this->value) && $this->type === 'customFormat'; } /** * @return string */ - public function asCustomFormat(): string { - if (!(is_string($this->value)&& $this->type === 'customFormat')){ + public function asCustomFormat(): string + { + if (!(is_string($this->value) && $this->type === 'customFormat')) { throw new Exception( "Expected customFormat; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isRegularString(): bool { - return is_string($this->value)&& $this->type === 'regularString'; + public function isRegularString(): bool + { + return is_string($this->value) && $this->type === 'regularString'; } /** * @return string */ - public function asRegularString(): string { - if (!(is_string($this->value)&& $this->type === 'regularString')){ + public function asRegularString(): string + { + if (!(is_string($this->value) && $this->type === 'regularString')) { throw new Exception( "Expected regularString; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isPatternString(): bool { - return is_string($this->value)&& $this->type === 'patternString'; + public function isPatternString(): bool + { + return is_string($this->value) && $this->type === 'patternString'; } /** * @return string */ - public function asPatternString(): string { - if (!(is_string($this->value)&& $this->type === 'patternString')){ + public function asPatternString(): string + { + if (!(is_string($this->value) && $this->type === 'patternString')) { throw new Exception( "Expected patternString; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'customFormat': $value = $this->value; $result['customFormat'] = $value; @@ -192,26 +207,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -220,47 +236,48 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'customFormat': - if (!array_key_exists('customFormat', $data)){ + if (!array_key_exists('customFormat', $data)) { throw new Exception( "JSON data is missing property 'customFormat'", ); } - + $args['value'] = $data['customFormat']; break; case 'regularString': - if (!array_key_exists('regularString', $data)){ + if (!array_key_exists('regularString', $data)) { throw new Exception( "JSON data is missing property 'regularString'", ); } - + $args['value'] = $data['regularString']; break; case 'patternString': - if (!array_key_exists('patternString', $data)){ + if (!array_key_exists('patternString', $data)) { throw new Exception( "JSON data is missing property 'patternString'", ); } - + $args['value'] = $data['patternString']; break; case '_unknown': @@ -268,7 +285,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithSingleElement.php b/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithSingleElement.php index 84dab1f390a5..7ad629a4bbeb 100644 --- a/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithSingleElement.php +++ b/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithSingleElement.php @@ -38,9 +38,9 @@ class UnionWithSingleElement extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** @@ -49,8 +49,10 @@ private function __construct( * |'_unknown' * ) */ - public function getType(): string { - return $this->type;} + public function getType(): string + { + return $this->type; + } /** * @return ( @@ -58,14 +60,17 @@ public function getType(): string { * |mixed * ) */ - public function getValue(): mixed { - return $this->value;} + public function getValue(): mixed + { + return $this->value; + } /** * @param Foo $foo * @return UnionWithSingleElement */ - public static function foo(Foo $foo): UnionWithSingleElement { + public static function foo(Foo $foo): UnionWithSingleElement + { return new UnionWithSingleElement([ 'type' => 'foo', 'value' => $foo, @@ -75,67 +80,72 @@ public static function foo(Foo $foo): UnionWithSingleElement { /** * @return bool */ - public function isFoo(): bool { - return $this->value instanceof Foo&& $this->type === 'foo'; + public function isFoo(): bool + { + return $this->value instanceof Foo && $this->type === 'foo'; } /** * @return Foo */ - public function asFoo(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo')){ + public function asFoo(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo')) { throw new Exception( "Expected foo; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'foo': $value = $this->asFoo()->jsonSerialize(); $result = array_merge($value, $result); break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -144,22 +154,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'foo': $args['value'] = Foo::jsonDeserialize($data); break; @@ -168,7 +179,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithSubTypes.php b/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithSubTypes.php index 32c36fb546b5..8c8f5cd502fe 100644 --- a/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithSubTypes.php +++ b/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithSubTypes.php @@ -42,9 +42,9 @@ class UnionWithSubTypes extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** @@ -54,8 +54,10 @@ private function __construct( * |'_unknown' * ) */ - public function getType(): string { - return $this->type;} + public function getType(): string + { + return $this->type; + } /** * @return ( @@ -64,14 +66,17 @@ public function getType(): string { * |mixed * ) */ - public function getValue(): mixed { - return $this->value;} + public function getValue(): mixed + { + return $this->value; + } /** * @param Foo $foo * @return UnionWithSubTypes */ - public static function foo(Foo $foo): UnionWithSubTypes { + public static function foo(Foo $foo): UnionWithSubTypes + { return new UnionWithSubTypes([ 'type' => 'foo', 'value' => $foo, @@ -82,7 +87,8 @@ public static function foo(Foo $foo): UnionWithSubTypes { * @param FooExtended $fooExtended * @return UnionWithSubTypes */ - public static function fooExtended(FooExtended $fooExtended): UnionWithSubTypes { + public static function fooExtended(FooExtended $fooExtended): UnionWithSubTypes + { return new UnionWithSubTypes([ 'type' => 'fooExtended', 'value' => $fooExtended, @@ -92,61 +98,67 @@ public static function fooExtended(FooExtended $fooExtended): UnionWithSubTypes /** * @return bool */ - public function isFoo(): bool { - return $this->value instanceof Foo&& $this->type === 'foo'; + public function isFoo(): bool + { + return $this->value instanceof Foo && $this->type === 'foo'; } /** * @return Foo */ - public function asFoo(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo')){ + public function asFoo(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo')) { throw new Exception( "Expected foo; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isFooExtended(): bool { - return $this->value instanceof FooExtended&& $this->type === 'fooExtended'; + public function isFooExtended(): bool + { + return $this->value instanceof FooExtended && $this->type === 'fooExtended'; } /** * @return FooExtended */ - public function asFooExtended(): FooExtended { - if (!($this->value instanceof FooExtended&& $this->type === 'fooExtended')){ + public function asFooExtended(): FooExtended + { + if (!($this->value instanceof FooExtended && $this->type === 'fooExtended')) { throw new Exception( "Expected fooExtended; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'foo': $value = $this->asFoo()->jsonSerialize(); $result = array_merge($value, $result); @@ -157,26 +169,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -185,22 +198,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'foo': $args['value'] = Foo::jsonDeserialize($data); break; @@ -212,7 +226,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithTime.php b/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithTime.php index 9911c2d2bd5c..83eb95faea5c 100644 --- a/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithTime.php +++ b/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithTime.php @@ -46,9 +46,9 @@ class UnionWithTime extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** @@ -59,8 +59,10 @@ private function __construct( * |'_unknown' * ) */ - public function getType(): string { - return $this->type;} + public function getType(): string + { + return $this->type; + } /** * @return ( @@ -69,14 +71,17 @@ public function getType(): string { * |mixed * ) */ - public function getValue(): mixed { - return $this->value;} + public function getValue(): mixed + { + return $this->value; + } /** * @param int $value * @return UnionWithTime */ - public static function value(int $value): UnionWithTime { + public static function value(int $value): UnionWithTime + { return new UnionWithTime([ 'type' => 'value', 'value' => $value, @@ -87,7 +92,8 @@ public static function value(int $value): UnionWithTime { * @param DateTime $date * @return UnionWithTime */ - public static function date(DateTime $date): UnionWithTime { + public static function date(DateTime $date): UnionWithTime + { return new UnionWithTime([ 'type' => 'date', 'value' => $date, @@ -98,7 +104,8 @@ public static function date(DateTime $date): UnionWithTime { * @param DateTime $datetime * @return UnionWithTime */ - public static function datetime(DateTime $datetime): UnionWithTime { + public static function datetime(DateTime $datetime): UnionWithTime + { return new UnionWithTime([ 'type' => 'datetime', 'value' => $datetime, @@ -108,81 +115,89 @@ public static function datetime(DateTime $datetime): UnionWithTime { /** * @return bool */ - public function isValue(): bool { - return is_int($this->value)&& $this->type === 'value'; + public function isValue(): bool + { + return is_int($this->value) && $this->type === 'value'; } /** * @return int */ - public function asValue(): int { - if (!(is_int($this->value)&& $this->type === 'value')){ + public function asValue(): int + { + if (!(is_int($this->value) && $this->type === 'value')) { throw new Exception( "Expected value; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isDate(): bool { - return $this->value instanceof DateTime&& $this->type === 'date'; + public function isDate(): bool + { + return $this->value instanceof DateTime && $this->type === 'date'; } /** * @return DateTime */ - public function asDate(): DateTime { - if (!($this->value instanceof DateTime&& $this->type === 'date')){ + public function asDate(): DateTime + { + if (!($this->value instanceof DateTime && $this->type === 'date')) { throw new Exception( "Expected date; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isDatetime(): bool { - return $this->value instanceof DateTime&& $this->type === 'datetime'; + public function isDatetime(): bool + { + return $this->value instanceof DateTime && $this->type === 'datetime'; } /** * @return DateTime */ - public function asDatetime(): DateTime { - if (!($this->value instanceof DateTime&& $this->type === 'datetime')){ + public function asDatetime(): DateTime + { + if (!($this->value instanceof DateTime && $this->type === 'datetime')) { throw new Exception( "Expected datetime; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'value': $value = $this->value; $result['value'] = $value; @@ -197,26 +212,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -225,47 +241,48 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'value': - if (!array_key_exists('value', $data)){ + if (!array_key_exists('value', $data)) { throw new Exception( "JSON data is missing property 'value'", ); } - + $args['value'] = $data['value']; break; case 'date': - if (!array_key_exists('date', $data)){ + if (!array_key_exists('date', $data)) { throw new Exception( "JSON data is missing property 'date'", ); } - + $args['value'] = $data['date']; break; case 'datetime': - if (!array_key_exists('datetime', $data)){ + if (!array_key_exists('datetime', $data)) { throw new Exception( "JSON data is missing property 'datetime'", ); } - + $args['value'] = $data['datetime']; break; case '_unknown': @@ -273,7 +290,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithoutKey.php b/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithoutKey.php index fcc11a0d65e3..4aadfad75114 100644 --- a/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithoutKey.php +++ b/seed/php-sdk/unions/property-accessors/src/Types/Types/UnionWithoutKey.php @@ -42,9 +42,9 @@ class UnionWithoutKey extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->type = $values['type'];$this->value = $values['value']; + ) { + $this->type = $values['type']; + $this->value = $values['value']; } /** @@ -54,8 +54,10 @@ private function __construct( * |'_unknown' * ) */ - public function getType(): string { - return $this->type;} + public function getType(): string + { + return $this->type; + } /** * @return ( @@ -64,14 +66,17 @@ public function getType(): string { * |mixed * ) */ - public function getValue(): mixed { - return $this->value;} + public function getValue(): mixed + { + return $this->value; + } /** * @param Foo $foo * @return UnionWithoutKey */ - public static function foo(Foo $foo): UnionWithoutKey { + public static function foo(Foo $foo): UnionWithoutKey + { return new UnionWithoutKey([ 'type' => 'foo', 'value' => $foo, @@ -82,7 +87,8 @@ public static function foo(Foo $foo): UnionWithoutKey { * @param Bar $bar * @return UnionWithoutKey */ - public static function bar(Bar $bar): UnionWithoutKey { + public static function bar(Bar $bar): UnionWithoutKey + { return new UnionWithoutKey([ 'type' => 'bar', 'value' => $bar, @@ -92,61 +98,67 @@ public static function bar(Bar $bar): UnionWithoutKey { /** * @return bool */ - public function isFoo(): bool { - return $this->value instanceof Foo&& $this->type === 'foo'; + public function isFoo(): bool + { + return $this->value instanceof Foo && $this->type === 'foo'; } /** * @return Foo */ - public function asFoo(): Foo { - if (!($this->value instanceof Foo&& $this->type === 'foo')){ + public function asFoo(): Foo + { + if (!($this->value instanceof Foo && $this->type === 'foo')) { throw new Exception( "Expected foo; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isBar(): bool { - return $this->value instanceof Bar&& $this->type === 'bar'; + public function isBar(): bool + { + return $this->value instanceof Bar && $this->type === 'bar'; } /** * @return Bar */ - public function asBar(): Bar { - if (!($this->value instanceof Bar&& $this->type === 'bar')){ + public function asBar(): Bar + { + if (!($this->value instanceof Bar && $this->type === 'bar')) { throw new Exception( "Expected bar; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'foo': $value = $this->asFoo()->jsonSerialize(); $result = array_merge($value, $result); @@ -157,26 +169,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -185,22 +198,23 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('type', $data)){ + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'foo': $args['value'] = Foo::jsonDeserialize($data); break; @@ -212,7 +226,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions/property-accessors/src/Union/Types/Circle.php b/seed/php-sdk/unions/property-accessors/src/Union/Types/Circle.php index 2e917828da4b..8d0ffc80e80e 100644 --- a/seed/php-sdk/unions/property-accessors/src/Union/Types/Circle.php +++ b/seed/php-sdk/unions/property-accessors/src/Union/Types/Circle.php @@ -20,27 +20,32 @@ class Circle extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->radius = $values['radius']; } /** * @return float */ - public function getRadius(): float { - return $this->radius;} + public function getRadius(): float + { + return $this->radius; + } /** * @param float $value */ - public function setRadius(float $value): self { - $this->radius = $value;return $this;} + public function setRadius(float $value): self + { + $this->radius = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/property-accessors/src/Union/Types/GetShapeRequest.php b/seed/php-sdk/unions/property-accessors/src/Union/Types/GetShapeRequest.php index 2f51d2dea8a2..4d5ca3ed1dc8 100644 --- a/seed/php-sdk/unions/property-accessors/src/Union/Types/GetShapeRequest.php +++ b/seed/php-sdk/unions/property-accessors/src/Union/Types/GetShapeRequest.php @@ -20,27 +20,32 @@ class GetShapeRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->id = $values['id']; } /** * @return string */ - public function getId(): string { - return $this->id;} + public function getId(): string + { + return $this->id; + } /** * @param string $value */ - public function setId(string $value): self { - $this->id = $value;return $this;} + public function setId(string $value): self + { + $this->id = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/property-accessors/src/Union/Types/Shape.php b/seed/php-sdk/unions/property-accessors/src/Union/Types/Shape.php index a1f4e74b769d..d1ca605fcb3b 100644 --- a/seed/php-sdk/unions/property-accessors/src/Union/Types/Shape.php +++ b/seed/php-sdk/unions/property-accessors/src/Union/Types/Shape.php @@ -50,22 +50,28 @@ class Shape extends JsonSerializableType */ private function __construct( array $values, - ) - { - $this->id = $values['id'];$this->type = $values['type'];$this->value = $values['value']; + ) { + $this->id = $values['id']; + $this->type = $values['type']; + $this->value = $values['value']; } /** * @return string */ - public function getId(): string { - return $this->id;} + public function getId(): string + { + return $this->id; + } /** * @param string $value */ - public function setId(string $value): self { - $this->id = $value;return $this;} + public function setId(string $value): self + { + $this->id = $value; + return $this; + } /** * @return ( @@ -74,8 +80,10 @@ public function setId(string $value): self { * |'_unknown' * ) */ - public function getType(): string { - return $this->type;} + public function getType(): string + { + return $this->type; + } /** * @return ( @@ -84,15 +92,18 @@ public function getType(): string { * |mixed * ) */ - public function getValue(): mixed { - return $this->value;} + public function getValue(): mixed + { + return $this->value; + } /** * @param string $id * @param Circle $circle * @return Shape */ - public static function circle(string $id, Circle $circle): Shape { + public static function circle(string $id, Circle $circle): Shape + { return new Shape([ 'id' => $id, 'type' => 'circle', @@ -105,7 +116,8 @@ public static function circle(string $id, Circle $circle): Shape { * @param Square $square * @return Shape */ - public static function square(string $id, Square $square): Shape { + public static function square(string $id, Square $square): Shape + { return new Shape([ 'id' => $id, 'type' => 'square', @@ -116,61 +128,67 @@ public static function square(string $id, Square $square): Shape { /** * @return bool */ - public function isCircle(): bool { - return $this->value instanceof Circle&& $this->type === 'circle'; + public function isCircle(): bool + { + return $this->value instanceof Circle && $this->type === 'circle'; } /** * @return Circle */ - public function asCircle(): Circle { - if (!($this->value instanceof Circle&& $this->type === 'circle')){ + public function asCircle(): Circle + { + if (!($this->value instanceof Circle && $this->type === 'circle')) { throw new Exception( "Expected circle; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return bool */ - public function isSquare(): bool { - return $this->value instanceof Square&& $this->type === 'square'; + public function isSquare(): bool + { + return $this->value instanceof Square && $this->type === 'square'; } /** * @return Square */ - public function asSquare(): Square { - if (!($this->value instanceof Square&& $this->type === 'square')){ + public function asSquare(): Square + { + if (!($this->value instanceof Square && $this->type === 'square')) { throw new Exception( "Expected square; got " . $this->type . " with value of type " . get_debug_type($this->value), ); } - + return $this->value; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } /** * @return array */ - public function jsonSerialize(): array { + public function jsonSerialize(): array + { $result = []; $result['type'] = $this->type; - + $base = parent::jsonSerialize(); $result = array_merge($base, $result); - - switch ($this->type){ + + switch ($this->type) { case 'circle': $value = $this->asCircle()->jsonSerialize(); $result = array_merge($value, $result); @@ -181,26 +199,27 @@ public function jsonSerialize(): array { break; case '_unknown': default: - if (is_null($this->value)){ + if (is_null($this->value)) { break; } - if ($this->value instanceof JsonSerializableType){ + if ($this->value instanceof JsonSerializableType) { $value = $this->value->jsonSerialize(); $result = array_merge($value, $result); - } elseif (is_array($this->value)){ + } elseif (is_array($this->value)) { $result = array_merge($this->value, $result); } } - + return $result; } /** * @param string $json */ - public static function fromJson(string $json): static { + public static function fromJson(string $json): static + { $decodedJson = JsonDecoder::decode($json); - if (!is_array($decodedJson)){ + if (!is_array($decodedJson)) { throw new Exception("Unexpected non-array decoded type: " . gettype($decodedJson)); } return self::jsonDeserialize($decodedJson); @@ -209,34 +228,35 @@ public static function fromJson(string $json): static { /** * @param array $data */ - public static function jsonDeserialize(array $data): static { + public static function jsonDeserialize(array $data): static + { $args = []; - if (!array_key_exists('id', $data)){ + if (!array_key_exists('id', $data)) { throw new Exception( "JSON data is missing property 'id'", ); } - if (!(is_string($data['id']))){ + if (!(is_string($data['id']))) { throw new Exception( "Expected property 'id' in JSON data to be string, instead received " . get_debug_type($data['id']), ); } $args['id'] = $data['id']; - - if (!array_key_exists('type', $data)){ + + if (!array_key_exists('type', $data)) { throw new Exception( "JSON data is missing property 'type'", ); } $type = $data['type']; - if (!(is_string($type))){ + if (!(is_string($type))) { throw new Exception( "Expected property 'type' in JSON data to be string, instead received " . get_debug_type($data['type']), ); } - + $args['type'] = $type; - switch ($type){ + switch ($type) { case 'circle': $args['value'] = Circle::jsonDeserialize($data); break; @@ -248,7 +268,7 @@ public static function jsonDeserialize(array $data): static { $args['type'] = '_unknown'; $args['value'] = $data; } - + // @phpstan-ignore-next-line return new static($args); } diff --git a/seed/php-sdk/unions/property-accessors/src/Union/Types/Square.php b/seed/php-sdk/unions/property-accessors/src/Union/Types/Square.php index 92e88fcd8da2..ddc418d06712 100644 --- a/seed/php-sdk/unions/property-accessors/src/Union/Types/Square.php +++ b/seed/php-sdk/unions/property-accessors/src/Union/Types/Square.php @@ -20,27 +20,32 @@ class Square extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->length = $values['length']; } /** * @return float */ - public function getLength(): float { - return $this->length;} + public function getLength(): float + { + return $this->length; + } /** * @param float $value */ - public function setLength(float $value): self { - $this->length = $value;return $this;} + public function setLength(float $value): self + { + $this->length = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/property-accessors/src/Union/Types/WithName.php b/seed/php-sdk/unions/property-accessors/src/Union/Types/WithName.php index 108ef282724f..5745f0df0de5 100644 --- a/seed/php-sdk/unions/property-accessors/src/Union/Types/WithName.php +++ b/seed/php-sdk/unions/property-accessors/src/Union/Types/WithName.php @@ -20,27 +20,32 @@ class WithName extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; } /** * @return string */ - public function getName(): string { - return $this->name;} + public function getName(): string + { + return $this->name; + } /** * @param string $value */ - public function setName(string $value): self { - $this->name = $value;return $this;} + public function setName(string $value): self + { + $this->name = $value; + return $this; + } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unions/property-accessors/src/Union/UnionClient.php b/seed/php-sdk/unions/property-accessors/src/Union/UnionClient.php index 4f27c965e752..baacf1c699f3 100644 --- a/seed/php-sdk/unions/property-accessors/src/Union/UnionClient.php +++ b/seed/php-sdk/unions/property-accessors/src/Union/UnionClient.php @@ -14,7 +14,7 @@ use Psr\Http\Client\ClientExceptionInterface; use Seed\Core\Json\JsonDecoder; -class UnionClient +class UnionClient { /** * @var array{ @@ -42,11 +42,10 @@ class UnionClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -65,7 +64,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function get(string $id, ?array $options = null): Shape { + public function get(string $id, ?array $options = null): Shape + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -77,15 +77,15 @@ public function get(string $id, ?array $options = null): Shape { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return Shape::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -117,7 +117,8 @@ public function get(string $id, ?array $options = null): Shape { * @throws SeedException * @throws SeedApiException */ - public function update(Shape $request, ?array $options = null): bool { + public function update(Shape $request, ?array $options = null): bool + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -130,15 +131,15 @@ public function update(Shape $request, ?array $options = null): bool { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeBool($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/unions/property-accessors/src/Utils/File.php b/seed/php-sdk/unions/property-accessors/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/unions/property-accessors/src/Utils/File.php +++ b/seed/php-sdk/unions/property-accessors/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/unions/property-accessors/tests/Core/Client/RawClientTest.php b/seed/php-sdk/unions/property-accessors/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/unions/property-accessors/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/unions/property-accessors/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/unions/property-accessors/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/unions/property-accessors/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/unions/property-accessors/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/unions/property-accessors/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/unions/property-accessors/tests/Core/Json/EnumTest.php b/seed/php-sdk/unions/property-accessors/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/unions/property-accessors/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/unions/property-accessors/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/unions/property-accessors/tests/Core/Json/TraitTest.php b/seed/php-sdk/unions/property-accessors/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/unions/property-accessors/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/unions/property-accessors/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/unions/property-accessors/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/unions/property-accessors/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/unions/property-accessors/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/unions/property-accessors/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/unknown/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/unknown/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/unknown/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/unknown/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/unknown/src/Core/Client/RawClient.php b/seed/php-sdk/unknown/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/unknown/src/Core/Client/RawClient.php +++ b/seed/php-sdk/unknown/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/unknown/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/unknown/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/unknown/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/unknown/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/unknown/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/unknown/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/unknown/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/unknown/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/unknown/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/unknown/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/unknown/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/unknown/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/unknown/src/Core/Types/ArrayType.php b/seed/php-sdk/unknown/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/unknown/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/unknown/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/unknown/src/Core/Types/Constant.php b/seed/php-sdk/unknown/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/unknown/src/Core/Types/Constant.php +++ b/seed/php-sdk/unknown/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/unknown/src/Core/Types/Union.php b/seed/php-sdk/unknown/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/unknown/src/Core/Types/Union.php +++ b/seed/php-sdk/unknown/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/unknown/src/Exceptions/SeedApiException.php b/seed/php-sdk/unknown/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/unknown/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/unknown/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/unknown/src/Exceptions/SeedException.php b/seed/php-sdk/unknown/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/unknown/src/Exceptions/SeedException.php +++ b/seed/php-sdk/unknown/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/unknown/src/SeedClient.php b/seed/php-sdk/unknown/src/SeedClient.php index 2e7eaa2d94bb..2feb246293f6 100644 --- a/seed/php-sdk/unknown/src/SeedClient.php +++ b/seed/php-sdk/unknown/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var UnknownClient $unknown @@ -40,26 +40,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->unknown = new UnknownClient($this->client, $this->options); } } diff --git a/seed/php-sdk/unknown/src/Unknown/Types/MyObject.php b/seed/php-sdk/unknown/src/Unknown/Types/MyObject.php index 431948b63da7..8dda7ca09b64 100644 --- a/seed/php-sdk/unknown/src/Unknown/Types/MyObject.php +++ b/seed/php-sdk/unknown/src/Unknown/Types/MyObject.php @@ -20,15 +20,15 @@ class MyObject extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->unknown = $values['unknown']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/unknown/src/Unknown/UnknownClient.php b/seed/php-sdk/unknown/src/Unknown/UnknownClient.php index 4fa169d1825d..e1769ecc9628 100644 --- a/seed/php-sdk/unknown/src/Unknown/UnknownClient.php +++ b/seed/php-sdk/unknown/src/Unknown/UnknownClient.php @@ -14,7 +14,7 @@ use Psr\Http\Client\ClientExceptionInterface; use Seed\Unknown\Types\MyObject; -class UnknownClient +class UnknownClient { /** * @var array{ @@ -42,11 +42,10 @@ class UnknownClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -65,7 +64,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function post(mixed $request, ?array $options = null): array { + public function post(mixed $request, ?array $options = null): array + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -78,15 +78,15 @@ public function post(mixed $request, ?array $options = null): array { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, ['mixed']); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -118,7 +118,8 @@ public function post(mixed $request, ?array $options = null): array { * @throws SeedException * @throws SeedApiException */ - public function postObject(MyObject $request, ?array $options = null): array { + public function postObject(MyObject $request, ?array $options = null): array + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -131,15 +132,15 @@ public function postObject(MyObject $request, ?array $options = null): array { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return JsonDecoder::decodeArray($json, ['mixed']); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/unknown/src/Utils/File.php b/seed/php-sdk/unknown/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/unknown/src/Utils/File.php +++ b/seed/php-sdk/unknown/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/unknown/tests/Core/Client/RawClientTest.php b/seed/php-sdk/unknown/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/unknown/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/unknown/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/unknown/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/unknown/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/unknown/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/unknown/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/unknown/tests/Core/Json/EnumTest.php b/seed/php-sdk/unknown/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/unknown/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/unknown/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/unknown/tests/Core/Json/TraitTest.php b/seed/php-sdk/unknown/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/unknown/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/unknown/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/unknown/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/unknown/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/unknown/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/unknown/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/url-form-encoded/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/url-form-encoded/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/url-form-encoded/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/url-form-encoded/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/url-form-encoded/src/Core/Client/RawClient.php b/seed/php-sdk/url-form-encoded/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/url-form-encoded/src/Core/Client/RawClient.php +++ b/seed/php-sdk/url-form-encoded/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/url-form-encoded/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/url-form-encoded/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/url-form-encoded/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/url-form-encoded/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/url-form-encoded/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/url-form-encoded/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/url-form-encoded/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/url-form-encoded/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/url-form-encoded/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/url-form-encoded/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/url-form-encoded/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/url-form-encoded/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/url-form-encoded/src/Core/Types/ArrayType.php b/seed/php-sdk/url-form-encoded/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/url-form-encoded/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/url-form-encoded/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/url-form-encoded/src/Core/Types/Constant.php b/seed/php-sdk/url-form-encoded/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/url-form-encoded/src/Core/Types/Constant.php +++ b/seed/php-sdk/url-form-encoded/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/url-form-encoded/src/Core/Types/Union.php b/seed/php-sdk/url-form-encoded/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/url-form-encoded/src/Core/Types/Union.php +++ b/seed/php-sdk/url-form-encoded/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/url-form-encoded/src/Exceptions/SeedApiException.php b/seed/php-sdk/url-form-encoded/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/url-form-encoded/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/url-form-encoded/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/url-form-encoded/src/Exceptions/SeedException.php b/seed/php-sdk/url-form-encoded/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/url-form-encoded/src/Exceptions/SeedException.php +++ b/seed/php-sdk/url-form-encoded/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/url-form-encoded/src/Requests/PostSubmitRequest.php b/seed/php-sdk/url-form-encoded/src/Requests/PostSubmitRequest.php index 1389372623b3..8fb8f66c06dc 100644 --- a/seed/php-sdk/url-form-encoded/src/Requests/PostSubmitRequest.php +++ b/seed/php-sdk/url-form-encoded/src/Requests/PostSubmitRequest.php @@ -27,8 +27,8 @@ class PostSubmitRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->username = $values['username'];$this->email = $values['email']; + ) { + $this->username = $values['username']; + $this->email = $values['email']; } } diff --git a/seed/php-sdk/url-form-encoded/src/SeedClient.php b/seed/php-sdk/url-form-encoded/src/SeedClient.php index eb260dacc3d0..46136ae9b194 100644 --- a/seed/php-sdk/url-form-encoded/src/SeedClient.php +++ b/seed/php-sdk/url-form-encoded/src/SeedClient.php @@ -14,7 +14,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class SeedClient +class SeedClient { /** * @var array{ @@ -43,22 +43,21 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); @@ -78,7 +77,8 @@ public function __construct( * @throws SeedException * @throws SeedApiException */ - public function submitFormData(PostSubmitRequest $request, ?array $options = null): PostSubmitResponse { + public function submitFormData(PostSubmitRequest $request, ?array $options = null): PostSubmitResponse + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -91,15 +91,15 @@ public function submitFormData(PostSubmitRequest $request, ?array $options = nul $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return PostSubmitResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/url-form-encoded/src/Types/PostSubmitResponse.php b/seed/php-sdk/url-form-encoded/src/Types/PostSubmitResponse.php index ebf89c59ef7a..4c031e985a0a 100644 --- a/seed/php-sdk/url-form-encoded/src/Types/PostSubmitResponse.php +++ b/seed/php-sdk/url-form-encoded/src/Types/PostSubmitResponse.php @@ -27,15 +27,16 @@ class PostSubmitResponse extends JsonSerializableType */ public function __construct( array $values = [], - ) - { - $this->status = $values['status'] ?? null;$this->message = $values['message'] ?? null; + ) { + $this->status = $values['status'] ?? null; + $this->message = $values['message'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/url-form-encoded/src/Utils/File.php b/seed/php-sdk/url-form-encoded/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/url-form-encoded/src/Utils/File.php +++ b/seed/php-sdk/url-form-encoded/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/url-form-encoded/tests/Core/Client/RawClientTest.php b/seed/php-sdk/url-form-encoded/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/url-form-encoded/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/url-form-encoded/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/url-form-encoded/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/url-form-encoded/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/url-form-encoded/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/url-form-encoded/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/url-form-encoded/tests/Core/Json/EnumTest.php b/seed/php-sdk/url-form-encoded/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/url-form-encoded/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/url-form-encoded/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/url-form-encoded/tests/Core/Json/TraitTest.php b/seed/php-sdk/url-form-encoded/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/url-form-encoded/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/url-form-encoded/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/url-form-encoded/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/url-form-encoded/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/url-form-encoded/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/url-form-encoded/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/validation/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/validation/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/validation/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/validation/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/validation/src/Core/Client/RawClient.php b/seed/php-sdk/validation/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/validation/src/Core/Client/RawClient.php +++ b/seed/php-sdk/validation/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/validation/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/validation/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/validation/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/validation/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/validation/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/validation/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/validation/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/validation/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/validation/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/validation/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/validation/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/validation/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/validation/src/Core/Types/ArrayType.php b/seed/php-sdk/validation/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/validation/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/validation/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/validation/src/Core/Types/Constant.php b/seed/php-sdk/validation/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/validation/src/Core/Types/Constant.php +++ b/seed/php-sdk/validation/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/validation/src/Core/Types/Union.php b/seed/php-sdk/validation/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/validation/src/Core/Types/Union.php +++ b/seed/php-sdk/validation/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/validation/src/Exceptions/SeedApiException.php b/seed/php-sdk/validation/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/validation/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/validation/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/validation/src/Exceptions/SeedException.php b/seed/php-sdk/validation/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/validation/src/Exceptions/SeedException.php +++ b/seed/php-sdk/validation/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/validation/src/Requests/CreateRequest.php b/seed/php-sdk/validation/src/Requests/CreateRequest.php index 3774647fd8ed..c911f38ec0cf 100644 --- a/seed/php-sdk/validation/src/Requests/CreateRequest.php +++ b/seed/php-sdk/validation/src/Requests/CreateRequest.php @@ -42,8 +42,10 @@ class CreateRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->decimal = $values['decimal'];$this->even = $values['even'];$this->name = $values['name'];$this->shape = $values['shape']; + ) { + $this->decimal = $values['decimal']; + $this->even = $values['even']; + $this->name = $values['name']; + $this->shape = $values['shape']; } } diff --git a/seed/php-sdk/validation/src/Requests/GetRequest.php b/seed/php-sdk/validation/src/Requests/GetRequest.php index 308d1df528ae..d9fd14e9d112 100644 --- a/seed/php-sdk/validation/src/Requests/GetRequest.php +++ b/seed/php-sdk/validation/src/Requests/GetRequest.php @@ -30,8 +30,9 @@ class GetRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->decimal = $values['decimal'];$this->even = $values['even'];$this->name = $values['name']; + ) { + $this->decimal = $values['decimal']; + $this->even = $values['even']; + $this->name = $values['name']; } } diff --git a/seed/php-sdk/validation/src/SeedClient.php b/seed/php-sdk/validation/src/SeedClient.php index c44afffa8076..02e358f40849 100644 --- a/seed/php-sdk/validation/src/SeedClient.php +++ b/seed/php-sdk/validation/src/SeedClient.php @@ -15,7 +15,7 @@ use Psr\Http\Client\ClientExceptionInterface; use Seed\Requests\GetRequest; -class SeedClient +class SeedClient { /** * @var array{ @@ -44,22 +44,21 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); @@ -79,7 +78,8 @@ public function __construct( * @throws SeedException * @throws SeedApiException */ - public function create(CreateRequest $request, ?array $options = null): Type { + public function create(CreateRequest $request, ?array $options = null): Type + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -92,15 +92,15 @@ public function create(CreateRequest $request, ?array $options = null): Type { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return Type::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -132,7 +132,8 @@ public function create(CreateRequest $request, ?array $options = null): Type { * @throws SeedException * @throws SeedApiException */ - public function get(GetRequest $request, ?array $options = null): Type { + public function get(GetRequest $request, ?array $options = null): Type + { $options = array_merge($this->options, $options ?? []); $query = []; $query['decimal'] = $request->decimal; @@ -149,15 +150,15 @@ public function get(GetRequest $request, ?array $options = null): Type { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return Type::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/validation/src/Types/Shape.php b/seed/php-sdk/validation/src/Types/Shape.php index 92d9f9b4bbb5..c111ab4cdc4d 100644 --- a/seed/php-sdk/validation/src/Types/Shape.php +++ b/seed/php-sdk/validation/src/Types/Shape.php @@ -2,8 +2,8 @@ namespace Seed\Types; -enum Shape - : string { +enum Shape: string +{ case Square = "SQUARE"; case Circle = "CIRCLE"; case Triangle = "TRIANGLE"; diff --git a/seed/php-sdk/validation/src/Types/Type.php b/seed/php-sdk/validation/src/Types/Type.php index ba9382876492..8456d2bbf203 100644 --- a/seed/php-sdk/validation/src/Types/Type.php +++ b/seed/php-sdk/validation/src/Types/Type.php @@ -44,15 +44,18 @@ class Type extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->decimal = $values['decimal'];$this->even = $values['even'];$this->name = $values['name'];$this->shape = $values['shape']; + ) { + $this->decimal = $values['decimal']; + $this->even = $values['even']; + $this->name = $values['name']; + $this->shape = $values['shape']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/validation/src/Utils/File.php b/seed/php-sdk/validation/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/validation/src/Utils/File.php +++ b/seed/php-sdk/validation/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/validation/tests/Core/Client/RawClientTest.php b/seed/php-sdk/validation/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/validation/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/validation/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/validation/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/validation/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/validation/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/validation/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/validation/tests/Core/Json/EnumTest.php b/seed/php-sdk/validation/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/validation/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/validation/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/validation/tests/Core/Json/TraitTest.php b/seed/php-sdk/validation/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/validation/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/validation/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/validation/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/validation/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/validation/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/validation/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/variables/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/variables/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/variables/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/variables/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/variables/src/Core/Client/RawClient.php b/seed/php-sdk/variables/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/variables/src/Core/Client/RawClient.php +++ b/seed/php-sdk/variables/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/variables/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/variables/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/variables/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/variables/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/variables/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/variables/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/variables/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/variables/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/variables/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/variables/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/variables/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/variables/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/variables/src/Core/Types/ArrayType.php b/seed/php-sdk/variables/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/variables/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/variables/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/variables/src/Core/Types/Constant.php b/seed/php-sdk/variables/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/variables/src/Core/Types/Constant.php +++ b/seed/php-sdk/variables/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/variables/src/Core/Types/Union.php b/seed/php-sdk/variables/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/variables/src/Core/Types/Union.php +++ b/seed/php-sdk/variables/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/variables/src/Exceptions/SeedApiException.php b/seed/php-sdk/variables/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/variables/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/variables/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/variables/src/Exceptions/SeedException.php b/seed/php-sdk/variables/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/variables/src/Exceptions/SeedException.php +++ b/seed/php-sdk/variables/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/variables/src/SeedClient.php b/seed/php-sdk/variables/src/SeedClient.php index 8d7adcb3b0f2..0fd6a9d1029e 100644 --- a/seed/php-sdk/variables/src/SeedClient.php +++ b/seed/php-sdk/variables/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var ServiceClient $service @@ -40,26 +40,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->service = new ServiceClient($this->client, $this->options); } } diff --git a/seed/php-sdk/variables/src/Service/ServiceClient.php b/seed/php-sdk/variables/src/Service/ServiceClient.php index 42fd42bcc8b2..55414e81906b 100644 --- a/seed/php-sdk/variables/src/Service/ServiceClient.php +++ b/seed/php-sdk/variables/src/Service/ServiceClient.php @@ -11,7 +11,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class ServiceClient +class ServiceClient { /** * @var array{ @@ -39,11 +39,10 @@ class ServiceClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -61,7 +60,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function post(string $endpointParam, ?array $options = null): void { + public function post(string $endpointParam, ?array $options = null): void + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -73,12 +73,12 @@ public function post(string $endpointParam, ?array $options = null): void { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { return; } } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/variables/src/Utils/File.php b/seed/php-sdk/variables/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/variables/src/Utils/File.php +++ b/seed/php-sdk/variables/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/variables/tests/Core/Client/RawClientTest.php b/seed/php-sdk/variables/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/variables/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/variables/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/variables/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/variables/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/variables/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/variables/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/variables/tests/Core/Json/EnumTest.php b/seed/php-sdk/variables/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/variables/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/variables/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/variables/tests/Core/Json/TraitTest.php b/seed/php-sdk/variables/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/variables/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/variables/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/variables/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/variables/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/variables/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/variables/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/version-no-default/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/version-no-default/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/version-no-default/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/version-no-default/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/version-no-default/src/Core/Client/RawClient.php b/seed/php-sdk/version-no-default/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/version-no-default/src/Core/Client/RawClient.php +++ b/seed/php-sdk/version-no-default/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/version-no-default/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/version-no-default/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/version-no-default/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/version-no-default/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/version-no-default/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/version-no-default/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/version-no-default/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/version-no-default/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/version-no-default/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/version-no-default/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/version-no-default/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/version-no-default/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/version-no-default/src/Core/Types/ArrayType.php b/seed/php-sdk/version-no-default/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/version-no-default/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/version-no-default/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/version-no-default/src/Core/Types/Constant.php b/seed/php-sdk/version-no-default/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/version-no-default/src/Core/Types/Constant.php +++ b/seed/php-sdk/version-no-default/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/version-no-default/src/Core/Types/Union.php b/seed/php-sdk/version-no-default/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/version-no-default/src/Core/Types/Union.php +++ b/seed/php-sdk/version-no-default/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/version-no-default/src/Exceptions/SeedApiException.php b/seed/php-sdk/version-no-default/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/version-no-default/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/version-no-default/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/version-no-default/src/Exceptions/SeedException.php b/seed/php-sdk/version-no-default/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/version-no-default/src/Exceptions/SeedException.php +++ b/seed/php-sdk/version-no-default/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/version-no-default/src/SeedClient.php b/seed/php-sdk/version-no-default/src/SeedClient.php index a13bf5acc053..ee87d1cb303f 100644 --- a/seed/php-sdk/version-no-default/src/SeedClient.php +++ b/seed/php-sdk/version-no-default/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var UserClient $user @@ -40,26 +40,25 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->user = new UserClient($this->client, $this->options); } } diff --git a/seed/php-sdk/version-no-default/src/User/Types/User.php b/seed/php-sdk/version-no-default/src/User/Types/User.php index ef498cdc789b..566655f7df2d 100644 --- a/seed/php-sdk/version-no-default/src/User/Types/User.php +++ b/seed/php-sdk/version-no-default/src/User/Types/User.php @@ -27,15 +27,16 @@ class User extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->name = $values['name']; + ) { + $this->id = $values['id']; + $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/version-no-default/src/User/UserClient.php b/seed/php-sdk/version-no-default/src/User/UserClient.php index 176d7fa24e4b..e041dacd73f1 100644 --- a/seed/php-sdk/version-no-default/src/User/UserClient.php +++ b/seed/php-sdk/version-no-default/src/User/UserClient.php @@ -13,7 +13,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class UserClient +class UserClient { /** * @var array{ @@ -41,11 +41,10 @@ class UserClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -64,7 +63,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getUser(string $userId, ?array $options = null): User { + public function getUser(string $userId, ?array $options = null): User + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -76,15 +76,15 @@ public function getUser(string $userId, ?array $options = null): User { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return User::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/version-no-default/src/Utils/File.php b/seed/php-sdk/version-no-default/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/version-no-default/src/Utils/File.php +++ b/seed/php-sdk/version-no-default/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/version-no-default/tests/Core/Client/RawClientTest.php b/seed/php-sdk/version-no-default/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/version-no-default/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/version-no-default/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/version-no-default/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/version-no-default/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/version-no-default/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/version-no-default/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/version-no-default/tests/Core/Json/EnumTest.php b/seed/php-sdk/version-no-default/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/version-no-default/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/version-no-default/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/version-no-default/tests/Core/Json/TraitTest.php b/seed/php-sdk/version-no-default/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/version-no-default/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/version-no-default/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/version-no-default/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/version-no-default/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/version-no-default/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/version-no-default/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/version/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/version/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/version/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/version/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/version/src/Core/Client/RawClient.php b/seed/php-sdk/version/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/version/src/Core/Client/RawClient.php +++ b/seed/php-sdk/version/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/version/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/version/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/version/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/version/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/version/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/version/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/version/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/version/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/version/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/version/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/version/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/version/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/version/src/Core/Types/ArrayType.php b/seed/php-sdk/version/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/version/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/version/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/version/src/Core/Types/Constant.php b/seed/php-sdk/version/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/version/src/Core/Types/Constant.php +++ b/seed/php-sdk/version/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/version/src/Core/Types/Union.php b/seed/php-sdk/version/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/version/src/Core/Types/Union.php +++ b/seed/php-sdk/version/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/version/src/Exceptions/SeedApiException.php b/seed/php-sdk/version/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/version/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/version/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/version/src/Exceptions/SeedException.php b/seed/php-sdk/version/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/version/src/Exceptions/SeedException.php +++ b/seed/php-sdk/version/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/version/src/SeedClient.php b/seed/php-sdk/version/src/SeedClient.php index c5ae4451c356..2516eb9cf444 100644 --- a/seed/php-sdk/version/src/SeedClient.php +++ b/seed/php-sdk/version/src/SeedClient.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Seed\Core\Client\RawClient; -class SeedClient +class SeedClient { /** * @var UserClient $user @@ -40,8 +40,7 @@ class SeedClient */ public function __construct( ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', @@ -49,18 +48,18 @@ public function __construct( 'User-Agent' => 'seed/seed/0.0.1', 'X-API-Version' => '2.0.0', ]; - + $this->options = $options ?? []; - + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + $this->client = new RawClient( options: $this->options, ); - + $this->user = new UserClient($this->client, $this->options); } } diff --git a/seed/php-sdk/version/src/User/Types/User.php b/seed/php-sdk/version/src/User/Types/User.php index ef498cdc789b..566655f7df2d 100644 --- a/seed/php-sdk/version/src/User/Types/User.php +++ b/seed/php-sdk/version/src/User/Types/User.php @@ -27,15 +27,16 @@ class User extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->id = $values['id'];$this->name = $values['name']; + ) { + $this->id = $values['id']; + $this->name = $values['name']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/version/src/User/UserClient.php b/seed/php-sdk/version/src/User/UserClient.php index 176d7fa24e4b..e041dacd73f1 100644 --- a/seed/php-sdk/version/src/User/UserClient.php +++ b/seed/php-sdk/version/src/User/UserClient.php @@ -13,7 +13,7 @@ use GuzzleHttp\Exception\RequestException; use Psr\Http\Client\ClientExceptionInterface; -class UserClient +class UserClient { /** * @var array{ @@ -41,11 +41,10 @@ class UserClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -64,7 +63,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getUser(string $userId, ?array $options = null): User { + public function getUser(string $userId, ?array $options = null): User + { $options = array_merge($this->options, $options ?? []); try { $response = $this->client->sendRequest( @@ -76,15 +76,15 @@ public function getUser(string $userId, ?array $options = null): User { $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return User::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/version/src/Utils/File.php b/seed/php-sdk/version/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/version/src/Utils/File.php +++ b/seed/php-sdk/version/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/version/tests/Core/Client/RawClientTest.php b/seed/php-sdk/version/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/version/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/version/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/version/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/version/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/version/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/version/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/version/tests/Core/Json/EnumTest.php b/seed/php-sdk/version/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/version/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/version/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/version/tests/Core/Json/TraitTest.php b/seed/php-sdk/version/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/version/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/version/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/version/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/version/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/version/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/version/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/websocket-bearer-auth/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/websocket-bearer-auth/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/websocket-bearer-auth/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/websocket-bearer-auth/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/websocket-bearer-auth/src/Core/Client/RawClient.php b/seed/php-sdk/websocket-bearer-auth/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/websocket-bearer-auth/src/Core/Client/RawClient.php +++ b/seed/php-sdk/websocket-bearer-auth/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/websocket-bearer-auth/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/websocket-bearer-auth/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/websocket-bearer-auth/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/websocket-bearer-auth/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/websocket-bearer-auth/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/websocket-bearer-auth/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/websocket-bearer-auth/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/websocket-bearer-auth/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/websocket-bearer-auth/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/websocket-bearer-auth/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/websocket-bearer-auth/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/websocket-bearer-auth/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/websocket-bearer-auth/src/Core/Types/ArrayType.php b/seed/php-sdk/websocket-bearer-auth/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/websocket-bearer-auth/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/websocket-bearer-auth/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/websocket-bearer-auth/src/Core/Types/Constant.php b/seed/php-sdk/websocket-bearer-auth/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/websocket-bearer-auth/src/Core/Types/Constant.php +++ b/seed/php-sdk/websocket-bearer-auth/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/websocket-bearer-auth/src/Core/Types/Union.php b/seed/php-sdk/websocket-bearer-auth/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/websocket-bearer-auth/src/Core/Types/Union.php +++ b/seed/php-sdk/websocket-bearer-auth/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/websocket-bearer-auth/src/Exceptions/SeedApiException.php b/seed/php-sdk/websocket-bearer-auth/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/websocket-bearer-auth/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/websocket-bearer-auth/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/websocket-bearer-auth/src/Exceptions/SeedException.php b/seed/php-sdk/websocket-bearer-auth/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/websocket-bearer-auth/src/Exceptions/SeedException.php +++ b/seed/php-sdk/websocket-bearer-auth/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/websocket-bearer-auth/src/Realtime/Types/ReceiveEvent.php b/seed/php-sdk/websocket-bearer-auth/src/Realtime/Types/ReceiveEvent.php index 33107fdaec3c..a8b0c5089954 100644 --- a/seed/php-sdk/websocket-bearer-auth/src/Realtime/Types/ReceiveEvent.php +++ b/seed/php-sdk/websocket-bearer-auth/src/Realtime/Types/ReceiveEvent.php @@ -27,15 +27,16 @@ class ReceiveEvent extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->alpha = $values['alpha'];$this->beta = $values['beta']; + ) { + $this->alpha = $values['alpha']; + $this->beta = $values['beta']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/websocket-bearer-auth/src/Realtime/Types/ReceiveEvent2.php b/seed/php-sdk/websocket-bearer-auth/src/Realtime/Types/ReceiveEvent2.php index a78a961c78bf..81df90533a4e 100644 --- a/seed/php-sdk/websocket-bearer-auth/src/Realtime/Types/ReceiveEvent2.php +++ b/seed/php-sdk/websocket-bearer-auth/src/Realtime/Types/ReceiveEvent2.php @@ -34,15 +34,17 @@ class ReceiveEvent2 extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->gamma = $values['gamma'];$this->delta = $values['delta'];$this->epsilon = $values['epsilon']; + ) { + $this->gamma = $values['gamma']; + $this->delta = $values['delta']; + $this->epsilon = $values['epsilon']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/websocket-bearer-auth/src/Realtime/Types/ReceiveEvent3.php b/seed/php-sdk/websocket-bearer-auth/src/Realtime/Types/ReceiveEvent3.php index 9e8e0adea636..1ba38f512ae1 100644 --- a/seed/php-sdk/websocket-bearer-auth/src/Realtime/Types/ReceiveEvent3.php +++ b/seed/php-sdk/websocket-bearer-auth/src/Realtime/Types/ReceiveEvent3.php @@ -20,15 +20,15 @@ class ReceiveEvent3 extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->receiveText3 = $values['receiveText3']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/websocket-bearer-auth/src/Realtime/Types/ReceiveSnakeCase.php b/seed/php-sdk/websocket-bearer-auth/src/Realtime/Types/ReceiveSnakeCase.php index b32f36399189..76e40e11868e 100644 --- a/seed/php-sdk/websocket-bearer-auth/src/Realtime/Types/ReceiveSnakeCase.php +++ b/seed/php-sdk/websocket-bearer-auth/src/Realtime/Types/ReceiveSnakeCase.php @@ -27,15 +27,16 @@ class ReceiveSnakeCase extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->receiveText = $values['receiveText'];$this->receiveInt = $values['receiveInt']; + ) { + $this->receiveText = $values['receiveText']; + $this->receiveInt = $values['receiveInt']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/websocket-bearer-auth/src/Realtime/Types/SendEvent.php b/seed/php-sdk/websocket-bearer-auth/src/Realtime/Types/SendEvent.php index 51bf846db683..dd67bbc75dd1 100644 --- a/seed/php-sdk/websocket-bearer-auth/src/Realtime/Types/SendEvent.php +++ b/seed/php-sdk/websocket-bearer-auth/src/Realtime/Types/SendEvent.php @@ -27,15 +27,16 @@ class SendEvent extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->sendText = $values['sendText'];$this->sendParam = $values['sendParam']; + ) { + $this->sendText = $values['sendText']; + $this->sendParam = $values['sendParam']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/websocket-bearer-auth/src/Realtime/Types/SendEvent2.php b/seed/php-sdk/websocket-bearer-auth/src/Realtime/Types/SendEvent2.php index 626fe0c74e70..90618e890fc4 100644 --- a/seed/php-sdk/websocket-bearer-auth/src/Realtime/Types/SendEvent2.php +++ b/seed/php-sdk/websocket-bearer-auth/src/Realtime/Types/SendEvent2.php @@ -27,15 +27,16 @@ class SendEvent2 extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->sendText2 = $values['sendText2'];$this->sendParam2 = $values['sendParam2']; + ) { + $this->sendText2 = $values['sendText2']; + $this->sendParam2 = $values['sendParam2']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/websocket-bearer-auth/src/Realtime/Types/SendSnakeCase.php b/seed/php-sdk/websocket-bearer-auth/src/Realtime/Types/SendSnakeCase.php index 58006c969776..91086ef5e53f 100644 --- a/seed/php-sdk/websocket-bearer-auth/src/Realtime/Types/SendSnakeCase.php +++ b/seed/php-sdk/websocket-bearer-auth/src/Realtime/Types/SendSnakeCase.php @@ -27,15 +27,16 @@ class SendSnakeCase extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->sendText = $values['sendText'];$this->sendParam = $values['sendParam']; + ) { + $this->sendText = $values['sendText']; + $this->sendParam = $values['sendParam']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/websocket-bearer-auth/src/RealtimeNoAuth/Types/NoAuthReceiveEvent.php b/seed/php-sdk/websocket-bearer-auth/src/RealtimeNoAuth/Types/NoAuthReceiveEvent.php index 4e380cc73a84..07e662fa32bd 100644 --- a/seed/php-sdk/websocket-bearer-auth/src/RealtimeNoAuth/Types/NoAuthReceiveEvent.php +++ b/seed/php-sdk/websocket-bearer-auth/src/RealtimeNoAuth/Types/NoAuthReceiveEvent.php @@ -20,15 +20,15 @@ class NoAuthReceiveEvent extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->response = $values['response']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/websocket-bearer-auth/src/RealtimeNoAuth/Types/NoAuthSendEvent.php b/seed/php-sdk/websocket-bearer-auth/src/RealtimeNoAuth/Types/NoAuthSendEvent.php index 6b95e0542fc3..0f56c4dfcc54 100644 --- a/seed/php-sdk/websocket-bearer-auth/src/RealtimeNoAuth/Types/NoAuthSendEvent.php +++ b/seed/php-sdk/websocket-bearer-auth/src/RealtimeNoAuth/Types/NoAuthSendEvent.php @@ -20,15 +20,15 @@ class NoAuthSendEvent extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->text = $values['text']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/websocket-bearer-auth/src/SeedClient.php b/seed/php-sdk/websocket-bearer-auth/src/SeedClient.php index efe6e7438b31..37a1c4dd76aa 100644 --- a/seed/php-sdk/websocket-bearer-auth/src/SeedClient.php +++ b/seed/php-sdk/websocket-bearer-auth/src/SeedClient.php @@ -2,7 +2,6 @@ namespace Seed; -class SeedClient +class SeedClient { - } diff --git a/seed/php-sdk/websocket-bearer-auth/src/Utils/File.php b/seed/php-sdk/websocket-bearer-auth/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/websocket-bearer-auth/src/Utils/File.php +++ b/seed/php-sdk/websocket-bearer-auth/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/websocket-bearer-auth/tests/Core/Client/RawClientTest.php b/seed/php-sdk/websocket-bearer-auth/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/websocket-bearer-auth/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/websocket-bearer-auth/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/websocket-bearer-auth/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/websocket-bearer-auth/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/websocket-bearer-auth/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/websocket-bearer-auth/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/websocket-bearer-auth/tests/Core/Json/EnumTest.php b/seed/php-sdk/websocket-bearer-auth/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/websocket-bearer-auth/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/websocket-bearer-auth/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/websocket-bearer-auth/tests/Core/Json/TraitTest.php b/seed/php-sdk/websocket-bearer-auth/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/websocket-bearer-auth/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/websocket-bearer-auth/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/websocket-bearer-auth/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/websocket-bearer-auth/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/websocket-bearer-auth/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/websocket-bearer-auth/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/websocket-inferred-auth/src/Auth/AuthClient.php b/seed/php-sdk/websocket-inferred-auth/src/Auth/AuthClient.php index 7ad1292f95fb..785e797ce7d7 100644 --- a/seed/php-sdk/websocket-inferred-auth/src/Auth/AuthClient.php +++ b/seed/php-sdk/websocket-inferred-auth/src/Auth/AuthClient.php @@ -15,7 +15,7 @@ use Psr\Http\Client\ClientExceptionInterface; use Seed\Auth\Requests\RefreshTokenRequest; -class AuthClient +class AuthClient { /** * @var array{ @@ -43,11 +43,10 @@ class AuthClient * headers?: array, * } $options */ - function __construct( + public function __construct( RawClient $client, ?array $options = null, - ) - { + ) { $this->client = $client; $this->options = $options ?? []; } @@ -66,7 +65,8 @@ function __construct( * @throws SeedException * @throws SeedApiException */ - public function getTokenWithClientCredentials(GetTokenRequest $request, ?array $options = null): TokenResponse { + public function getTokenWithClientCredentials(GetTokenRequest $request, ?array $options = null): TokenResponse + { $options = array_merge($this->options, $options ?? []); $headers = []; $headers['X-Api-Key'] = $request->xApiKey; @@ -82,15 +82,15 @@ public function getTokenWithClientCredentials(GetTokenRequest $request, ?array $ $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return TokenResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( @@ -122,7 +122,8 @@ public function getTokenWithClientCredentials(GetTokenRequest $request, ?array $ * @throws SeedException * @throws SeedApiException */ - public function refreshToken(RefreshTokenRequest $request, ?array $options = null): TokenResponse { + public function refreshToken(RefreshTokenRequest $request, ?array $options = null): TokenResponse + { $options = array_merge($this->options, $options ?? []); $headers = []; $headers['X-Api-Key'] = $request->xApiKey; @@ -138,15 +139,15 @@ public function refreshToken(RefreshTokenRequest $request, ?array $options = nul $options, ); $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400){ + if ($statusCode >= 200 && $statusCode < 400) { $json = $response->getBody()->getContents(); return TokenResponse::fromJson($json); } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); } catch (RequestException $e) { $response = $e->getResponse(); - if ($response === null){ + if ($response === null) { throw new SeedException(message: $e->getMessage(), previous: $e); } throw new SeedApiException( diff --git a/seed/php-sdk/websocket-inferred-auth/src/Auth/Requests/GetTokenRequest.php b/seed/php-sdk/websocket-inferred-auth/src/Auth/Requests/GetTokenRequest.php index bc4ae17c29a0..697143e98133 100644 --- a/seed/php-sdk/websocket-inferred-auth/src/Auth/Requests/GetTokenRequest.php +++ b/seed/php-sdk/websocket-inferred-auth/src/Auth/Requests/GetTokenRequest.php @@ -54,8 +54,12 @@ class GetTokenRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->xApiKey = $values['xApiKey'];$this->clientId = $values['clientId'];$this->clientSecret = $values['clientSecret'];$this->audience = $values['audience'];$this->grantType = $values['grantType'];$this->scope = $values['scope'] ?? null; + ) { + $this->xApiKey = $values['xApiKey']; + $this->clientId = $values['clientId']; + $this->clientSecret = $values['clientSecret']; + $this->audience = $values['audience']; + $this->grantType = $values['grantType']; + $this->scope = $values['scope'] ?? null; } } diff --git a/seed/php-sdk/websocket-inferred-auth/src/Auth/Requests/RefreshTokenRequest.php b/seed/php-sdk/websocket-inferred-auth/src/Auth/Requests/RefreshTokenRequest.php index 4bdcbb368671..295e771fba01 100644 --- a/seed/php-sdk/websocket-inferred-auth/src/Auth/Requests/RefreshTokenRequest.php +++ b/seed/php-sdk/websocket-inferred-auth/src/Auth/Requests/RefreshTokenRequest.php @@ -61,8 +61,13 @@ class RefreshTokenRequest extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->xApiKey = $values['xApiKey'];$this->clientId = $values['clientId'];$this->clientSecret = $values['clientSecret'];$this->refreshToken = $values['refreshToken'];$this->audience = $values['audience'];$this->grantType = $values['grantType'];$this->scope = $values['scope'] ?? null; + ) { + $this->xApiKey = $values['xApiKey']; + $this->clientId = $values['clientId']; + $this->clientSecret = $values['clientSecret']; + $this->refreshToken = $values['refreshToken']; + $this->audience = $values['audience']; + $this->grantType = $values['grantType']; + $this->scope = $values['scope'] ?? null; } } diff --git a/seed/php-sdk/websocket-inferred-auth/src/Auth/Types/TokenResponse.php b/seed/php-sdk/websocket-inferred-auth/src/Auth/Types/TokenResponse.php index 7727dea3a96e..d175c0311eb2 100644 --- a/seed/php-sdk/websocket-inferred-auth/src/Auth/Types/TokenResponse.php +++ b/seed/php-sdk/websocket-inferred-auth/src/Auth/Types/TokenResponse.php @@ -30,15 +30,16 @@ class TokenResponse extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->accessToken = $values['accessToken'];$this->refreshToken = $values['refreshToken'] ?? null; + ) { + $this->accessToken = $values['accessToken']; + $this->refreshToken = $values['refreshToken'] ?? null; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/websocket-inferred-auth/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/websocket-inferred-auth/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/websocket-inferred-auth/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/websocket-inferred-auth/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/websocket-inferred-auth/src/Core/Client/RawClient.php b/seed/php-sdk/websocket-inferred-auth/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/websocket-inferred-auth/src/Core/Client/RawClient.php +++ b/seed/php-sdk/websocket-inferred-auth/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/websocket-inferred-auth/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/websocket-inferred-auth/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/websocket-inferred-auth/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/websocket-inferred-auth/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/websocket-inferred-auth/src/Core/InferredAuthProvider.php b/seed/php-sdk/websocket-inferred-auth/src/Core/InferredAuthProvider.php index 4ed88b38733d..16fe88230618 100644 --- a/seed/php-sdk/websocket-inferred-auth/src/Core/InferredAuthProvider.php +++ b/seed/php-sdk/websocket-inferred-auth/src/Core/InferredAuthProvider.php @@ -9,7 +9,7 @@ * The InferredAuthProvider retrieves an access token from the configured token endpoint. * The access token is then used as the bearer token in every authenticated request. */ -class InferredAuthProvider +class InferredAuthProvider { /** * @var AuthClient $authClient @@ -30,11 +30,10 @@ class InferredAuthProvider * @param AuthClient $authClient The client used to retrieve the access token. * @param array $options The options containing credentials for the token endpoint. */ - function __construct( + public function __construct( AuthClient $authClient, array $options, - ) - { + ) { $this->authClient = $authClient; $this->options = $options; $this->accessToken = null; @@ -45,8 +44,9 @@ function __construct( * * @return string */ - public function getToken(): string { - if ($this->accessToken !== null){ + public function getToken(): string + { + if ($this->accessToken !== null) { return $this->accessToken; } return $this->refresh(); @@ -57,7 +57,8 @@ public function getToken(): string { * * @return array */ - public function getAuthHeaders(): array { + public function getAuthHeaders(): array + { $token = $this->getToken(); return [ 'Authorization' => "Bearer " . $token, @@ -69,7 +70,8 @@ public function getAuthHeaders(): array { * * @return string */ - private function refresh(): string { + private function refresh(): string + { /** @var array{xApiKey: string, clientId: string, clientSecret: string, audience: 'https://api.example.com', grantType: 'client_credentials', scope?: string|null} $values */ $values = [ 'xApiKey' => $this->options['xApiKey'] ?? '', diff --git a/seed/php-sdk/websocket-inferred-auth/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/websocket-inferred-auth/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/websocket-inferred-auth/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/websocket-inferred-auth/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/websocket-inferred-auth/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/websocket-inferred-auth/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/websocket-inferred-auth/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/websocket-inferred-auth/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/websocket-inferred-auth/src/Core/Types/ArrayType.php b/seed/php-sdk/websocket-inferred-auth/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/websocket-inferred-auth/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/websocket-inferred-auth/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/websocket-inferred-auth/src/Core/Types/Constant.php b/seed/php-sdk/websocket-inferred-auth/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/websocket-inferred-auth/src/Core/Types/Constant.php +++ b/seed/php-sdk/websocket-inferred-auth/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/websocket-inferred-auth/src/Core/Types/Union.php b/seed/php-sdk/websocket-inferred-auth/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/websocket-inferred-auth/src/Core/Types/Union.php +++ b/seed/php-sdk/websocket-inferred-auth/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/websocket-inferred-auth/src/Exceptions/SeedApiException.php b/seed/php-sdk/websocket-inferred-auth/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/websocket-inferred-auth/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/websocket-inferred-auth/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/websocket-inferred-auth/src/Exceptions/SeedException.php b/seed/php-sdk/websocket-inferred-auth/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/websocket-inferred-auth/src/Exceptions/SeedException.php +++ b/seed/php-sdk/websocket-inferred-auth/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/websocket-inferred-auth/src/Realtime/Types/ReceiveEvent.php b/seed/php-sdk/websocket-inferred-auth/src/Realtime/Types/ReceiveEvent.php index 33107fdaec3c..a8b0c5089954 100644 --- a/seed/php-sdk/websocket-inferred-auth/src/Realtime/Types/ReceiveEvent.php +++ b/seed/php-sdk/websocket-inferred-auth/src/Realtime/Types/ReceiveEvent.php @@ -27,15 +27,16 @@ class ReceiveEvent extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->alpha = $values['alpha'];$this->beta = $values['beta']; + ) { + $this->alpha = $values['alpha']; + $this->beta = $values['beta']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/websocket-inferred-auth/src/Realtime/Types/ReceiveEvent2.php b/seed/php-sdk/websocket-inferred-auth/src/Realtime/Types/ReceiveEvent2.php index a78a961c78bf..81df90533a4e 100644 --- a/seed/php-sdk/websocket-inferred-auth/src/Realtime/Types/ReceiveEvent2.php +++ b/seed/php-sdk/websocket-inferred-auth/src/Realtime/Types/ReceiveEvent2.php @@ -34,15 +34,17 @@ class ReceiveEvent2 extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->gamma = $values['gamma'];$this->delta = $values['delta'];$this->epsilon = $values['epsilon']; + ) { + $this->gamma = $values['gamma']; + $this->delta = $values['delta']; + $this->epsilon = $values['epsilon']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/websocket-inferred-auth/src/Realtime/Types/ReceiveEvent3.php b/seed/php-sdk/websocket-inferred-auth/src/Realtime/Types/ReceiveEvent3.php index 9e8e0adea636..1ba38f512ae1 100644 --- a/seed/php-sdk/websocket-inferred-auth/src/Realtime/Types/ReceiveEvent3.php +++ b/seed/php-sdk/websocket-inferred-auth/src/Realtime/Types/ReceiveEvent3.php @@ -20,15 +20,15 @@ class ReceiveEvent3 extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->receiveText3 = $values['receiveText3']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/websocket-inferred-auth/src/Realtime/Types/ReceiveSnakeCase.php b/seed/php-sdk/websocket-inferred-auth/src/Realtime/Types/ReceiveSnakeCase.php index b32f36399189..76e40e11868e 100644 --- a/seed/php-sdk/websocket-inferred-auth/src/Realtime/Types/ReceiveSnakeCase.php +++ b/seed/php-sdk/websocket-inferred-auth/src/Realtime/Types/ReceiveSnakeCase.php @@ -27,15 +27,16 @@ class ReceiveSnakeCase extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->receiveText = $values['receiveText'];$this->receiveInt = $values['receiveInt']; + ) { + $this->receiveText = $values['receiveText']; + $this->receiveInt = $values['receiveInt']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/websocket-inferred-auth/src/Realtime/Types/SendEvent.php b/seed/php-sdk/websocket-inferred-auth/src/Realtime/Types/SendEvent.php index 51bf846db683..dd67bbc75dd1 100644 --- a/seed/php-sdk/websocket-inferred-auth/src/Realtime/Types/SendEvent.php +++ b/seed/php-sdk/websocket-inferred-auth/src/Realtime/Types/SendEvent.php @@ -27,15 +27,16 @@ class SendEvent extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->sendText = $values['sendText'];$this->sendParam = $values['sendParam']; + ) { + $this->sendText = $values['sendText']; + $this->sendParam = $values['sendParam']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/websocket-inferred-auth/src/Realtime/Types/SendEvent2.php b/seed/php-sdk/websocket-inferred-auth/src/Realtime/Types/SendEvent2.php index 626fe0c74e70..90618e890fc4 100644 --- a/seed/php-sdk/websocket-inferred-auth/src/Realtime/Types/SendEvent2.php +++ b/seed/php-sdk/websocket-inferred-auth/src/Realtime/Types/SendEvent2.php @@ -27,15 +27,16 @@ class SendEvent2 extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->sendText2 = $values['sendText2'];$this->sendParam2 = $values['sendParam2']; + ) { + $this->sendText2 = $values['sendText2']; + $this->sendParam2 = $values['sendParam2']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/websocket-inferred-auth/src/Realtime/Types/SendSnakeCase.php b/seed/php-sdk/websocket-inferred-auth/src/Realtime/Types/SendSnakeCase.php index 58006c969776..91086ef5e53f 100644 --- a/seed/php-sdk/websocket-inferred-auth/src/Realtime/Types/SendSnakeCase.php +++ b/seed/php-sdk/websocket-inferred-auth/src/Realtime/Types/SendSnakeCase.php @@ -27,15 +27,16 @@ class SendSnakeCase extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->sendText = $values['sendText'];$this->sendParam = $values['sendParam']; + ) { + $this->sendText = $values['sendText']; + $this->sendParam = $values['sendParam']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/websocket-inferred-auth/src/SeedClient.php b/seed/php-sdk/websocket-inferred-auth/src/SeedClient.php index 7d0ce0f6ce51..ba79114f029e 100644 --- a/seed/php-sdk/websocket-inferred-auth/src/SeedClient.php +++ b/seed/php-sdk/websocket-inferred-auth/src/SeedClient.php @@ -7,7 +7,7 @@ use Seed\Core\Client\RawClient; use Seed\Core\InferredAuthProvider; -class SeedClient +class SeedClient { /** * @var AuthClient $auth @@ -30,6 +30,11 @@ class SeedClient */ private RawClient $client; + /** + * @var InferredAuthProvider $inferredAuthProvider + */ + private InferredAuthProvider $inferredAuthProvider; + /** * @param ?string $clientId * @param ?string $clientSecret @@ -49,20 +54,19 @@ public function __construct( ?string $scope = null, ?string $xApiKey = null, ?array $options = null, - ) - { + ) { $defaultHeaders = [ 'X-Fern-Language' => 'PHP', 'X-Fern-SDK-Name' => 'Seed', 'X-Fern-SDK-Version' => '0.0.1', 'User-Agent' => 'seed/seed/0.0.1', ]; - if ($xApiKey != null){ + if ($xApiKey != null) { $defaultHeaders['X-Api-Key'] = $xApiKey; } - + $this->options = $options ?? []; - + $authRawClient = new RawClient(['headers' => []]); $authClient = new AuthClient($authRawClient); $inferredAuthOptions = [ @@ -73,19 +77,20 @@ public function __construct( 'scope' => $scope ?? '', 'xApiKey' => $xApiKey ?? '', ]; - $inferredAuthProvider = new InferredAuthProvider($authClient, $inferredAuthOptions); - $authHeaders = $inferredAuthProvider->getAuthHeaders(); - - $defaultHeaders = array_merge($defaultHeaders, $authHeaders); + $this->inferredAuthProvider = new InferredAuthProvider($authClient, $inferredAuthOptions); + $this->options['headers'] = array_merge( $defaultHeaders, $this->options['headers'] ?? [], ); - + + $this->options['getAuthHeaders'] = fn () => + $this->inferredAuthProvider->getAuthHeaders(); + $this->client = new RawClient( options: $this->options, ); - + $this->auth = new AuthClient($this->client, $this->options); } } diff --git a/seed/php-sdk/websocket-inferred-auth/src/Utils/File.php b/seed/php-sdk/websocket-inferred-auth/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/websocket-inferred-auth/src/Utils/File.php +++ b/seed/php-sdk/websocket-inferred-auth/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/websocket-inferred-auth/tests/Core/Client/RawClientTest.php b/seed/php-sdk/websocket-inferred-auth/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/websocket-inferred-auth/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/websocket-inferred-auth/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/websocket-inferred-auth/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/websocket-inferred-auth/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/websocket-inferred-auth/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/websocket-inferred-auth/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/websocket-inferred-auth/tests/Core/Json/EnumTest.php b/seed/php-sdk/websocket-inferred-auth/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/websocket-inferred-auth/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/websocket-inferred-auth/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/websocket-inferred-auth/tests/Core/Json/TraitTest.php b/seed/php-sdk/websocket-inferred-auth/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/websocket-inferred-auth/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/websocket-inferred-auth/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/websocket-inferred-auth/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/websocket-inferred-auth/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/websocket-inferred-auth/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/websocket-inferred-auth/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/websocket/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/websocket/src/Core/Client/BaseApiRequest.php index 07266c78bcf8..5e1283e2b6f6 100644 --- a/seed/php-sdk/websocket/src/Core/Client/BaseApiRequest.php +++ b/seed/php-sdk/websocket/src/Core/Client/BaseApiRequest.php @@ -19,4 +19,4 @@ public function __construct( public readonly array $query = [], ) { } -} \ No newline at end of file +} diff --git a/seed/php-sdk/websocket/src/Core/Client/RawClient.php b/seed/php-sdk/websocket/src/Core/Client/RawClient.php index 25a2df44e8fb..a2455e9adab9 100644 --- a/seed/php-sdk/websocket/src/Core/Client/RawClient.php +++ b/seed/php-sdk/websocket/src/Core/Client/RawClient.php @@ -28,20 +28,26 @@ class RawClient */ private array $headers; + /** + * @var ?(callable(): array) $getAuthHeaders + */ + private $getAuthHeaders; + /** * @param ?array{ * baseUrl?: string, * client?: ClientInterface, * headers?: array, + * getAuthHeaders?: callable(): array, * } $options */ public function __construct( public readonly ?array $options = null, - ) - { + ) { $this->client = $this->options['client'] ?? $this->createDefaultClient(); $this->headers = $this->options['headers'] ?? []; + $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null; } /** @@ -69,8 +75,7 @@ private function createDefaultClient(): Client public function sendRequest( BaseApiRequest $request, ?array $options = null, - ): ResponseInterface - { + ): ResponseInterface { $opts = $options ?? []; $httpRequest = $this->buildRequest($request, $opts); return $this->client->send($httpRequest, $this->toGuzzleOptions($opts)); @@ -107,8 +112,7 @@ private function toGuzzleOptions(array $options): array private function buildRequest( BaseApiRequest $request, array $options - ): Request - { + ): Request { $url = $this->buildUrl($request, $options); $headers = $this->encodeHeaders($request, $options); $body = $this->encodeRequestBody($request, $options); @@ -130,8 +134,8 @@ private function buildRequest( private function encodeHeaders( BaseApiRequest $request, array $options, - ): array - { + ): array { + $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : []; return match (get_class($request)) { JsonApiRequest::class => array_merge( [ @@ -139,11 +143,13 @@ private function encodeHeaders( "Accept" => "*/*", ], $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), MultipartApiRequest::class => array_merge( $this->headers, + $authHeaders, $request->headers, $options['headers'] ?? [], ), @@ -161,8 +167,7 @@ private function encodeHeaders( private function encodeRequestBody( BaseApiRequest $request, array $options, - ): ?StreamInterface - { + ): ?StreamInterface { return match (get_class($request)) { JsonApiRequest::class => $request->body === null ? null : Utils::streamFor( json_encode( @@ -187,8 +192,7 @@ private function encodeRequestBody( private function buildJsonBody( mixed $body, array $options, - ): mixed - { + ): mixed { $overrideProperties = $options['bodyProperties'] ?? []; if (is_array($body) && (empty($body) || self::isSequential($body))) { return array_merge($body, $overrideProperties); @@ -220,8 +224,7 @@ private function buildJsonBody( private function buildUrl( BaseApiRequest $request, array $options, - ): string - { + ): string { $baseUrl = $request->baseUrl; $trimmedBaseUrl = rtrim($baseUrl, '/'); $trimmedBasePath = ltrim($request->path, '/'); @@ -280,7 +283,9 @@ private function encodeQueryValue(mixed $value): string */ private static function isSequential(array $arr): bool { - if (empty($arr)) return false; + if (empty($arr)) { + return false; + } $length = count($arr); $keys = array_keys($arr); for ($i = 0; $i < $length; $i++) { diff --git a/seed/php-sdk/websocket/src/Core/Client/RetryMiddleware.php b/seed/php-sdk/websocket/src/Core/Client/RetryMiddleware.php index 1e42cf47577c..5100b0604f70 100644 --- a/seed/php-sdk/websocket/src/Core/Client/RetryMiddleware.php +++ b/seed/php-sdk/websocket/src/Core/Client/RetryMiddleware.php @@ -42,8 +42,7 @@ class RetryMiddleware public function __construct( callable $nextHandler, ?array $options = null, - ) - { + ) { $this->nextHandler = $nextHandler; $this->options = array_merge(self::DEFAULT_RETRY_OPTIONS, $options ?? []); } @@ -99,8 +98,7 @@ private function shouldRetry( int $maxRetries, ?ResponseInterface $response = null, ?Throwable $exception = null - ): bool - { + ): bool { if ($retryAttempt >= $maxRetries) { return false; } diff --git a/seed/php-sdk/websocket/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/websocket/src/Core/Json/JsonApiRequest.php index 6e145becfb72..8fdf493606e6 100644 --- a/seed/php-sdk/websocket/src/Core/Json/JsonApiRequest.php +++ b/seed/php-sdk/websocket/src/Core/Json/JsonApiRequest.php @@ -1,6 +1,7 @@ $contentType] : null; self::addPart( new MultipartFormDataPart( @@ -57,6 +56,6 @@ public function addPart(MultipartFormDataPart $part): void */ public function toArray(): array { - return array_map(fn($part) => $part->toArray(), $this->parts); + return array_map(fn ($part) => $part->toArray(), $this->parts); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/websocket/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/websocket/src/Core/Multipart/MultipartFormDataPart.php index d5f6f1570ea7..c158903d84f1 100644 --- a/seed/php-sdk/websocket/src/Core/Multipart/MultipartFormDataPart.php +++ b/seed/php-sdk/websocket/src/Core/Multipart/MultipartFormDataPart.php @@ -73,4 +73,4 @@ public function toArray(): array return $formData; } -} \ No newline at end of file +} diff --git a/seed/php-sdk/websocket/src/Core/Types/ArrayType.php b/seed/php-sdk/websocket/src/Core/Types/ArrayType.php index fdadae6be5b7..a26d29008ec3 100644 --- a/seed/php-sdk/websocket/src/Core/Types/ArrayType.php +++ b/seed/php-sdk/websocket/src/Core/Types/ArrayType.php @@ -14,4 +14,3 @@ public function __construct(public array $type) { } } - diff --git a/seed/php-sdk/websocket/src/Core/Types/Constant.php b/seed/php-sdk/websocket/src/Core/Types/Constant.php index 487d41f9f93a..5ac4518cc6d6 100644 --- a/seed/php-sdk/websocket/src/Core/Types/Constant.php +++ b/seed/php-sdk/websocket/src/Core/Types/Constant.php @@ -9,4 +9,4 @@ class Constant public const DateFormat = 'Y-m-d'; public const DateDeserializationFormat = "!" . self::DateFormat; public const DateTimeFormat = DateTimeInterface::RFC3339; -} \ No newline at end of file +} diff --git a/seed/php-sdk/websocket/src/Core/Types/Union.php b/seed/php-sdk/websocket/src/Core/Types/Union.php index 525912eaf4b0..d7bacf5921f4 100644 --- a/seed/php-sdk/websocket/src/Core/Types/Union.php +++ b/seed/php-sdk/websocket/src/Core/Types/Union.php @@ -59,4 +59,4 @@ public function __toString(): string } }, $this->types)); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/websocket/src/Exceptions/SeedApiException.php b/seed/php-sdk/websocket/src/Exceptions/SeedApiException.php index fc613cc1517b..41a85392b70b 100644 --- a/seed/php-sdk/websocket/src/Exceptions/SeedApiException.php +++ b/seed/php-sdk/websocket/src/Exceptions/SeedApiException.php @@ -25,8 +25,7 @@ public function __construct( int $statusCode, mixed $body, ?Throwable $previous = null, - ) - { + ) { $this->body = $body; parent::__construct($message, $statusCode, $previous); } @@ -36,15 +35,17 @@ public function __construct( * * @return mixed */ - public function getBody(): mixed { + public function getBody(): mixed + { return $this->body; } /** * @return string */ - public function __toString(): string { - if (empty($this->body)){ + public function __toString(): string + { + if (empty($this->body)) { return "$this->message; Status Code: $this->code\n"; } return "$this->message; Status Code: $this->code; Body: " . $this->body . "\n"; diff --git a/seed/php-sdk/websocket/src/Exceptions/SeedException.php b/seed/php-sdk/websocket/src/Exceptions/SeedException.php index 49c370e8f2f2..457035276737 100644 --- a/seed/php-sdk/websocket/src/Exceptions/SeedException.php +++ b/seed/php-sdk/websocket/src/Exceptions/SeedException.php @@ -9,5 +9,4 @@ */ class SeedException extends Exception { - } diff --git a/seed/php-sdk/websocket/src/Realtime/Types/ReceiveEvent.php b/seed/php-sdk/websocket/src/Realtime/Types/ReceiveEvent.php index 33107fdaec3c..a8b0c5089954 100644 --- a/seed/php-sdk/websocket/src/Realtime/Types/ReceiveEvent.php +++ b/seed/php-sdk/websocket/src/Realtime/Types/ReceiveEvent.php @@ -27,15 +27,16 @@ class ReceiveEvent extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->alpha = $values['alpha'];$this->beta = $values['beta']; + ) { + $this->alpha = $values['alpha']; + $this->beta = $values['beta']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/websocket/src/Realtime/Types/ReceiveEvent2.php b/seed/php-sdk/websocket/src/Realtime/Types/ReceiveEvent2.php index a78a961c78bf..81df90533a4e 100644 --- a/seed/php-sdk/websocket/src/Realtime/Types/ReceiveEvent2.php +++ b/seed/php-sdk/websocket/src/Realtime/Types/ReceiveEvent2.php @@ -34,15 +34,17 @@ class ReceiveEvent2 extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->gamma = $values['gamma'];$this->delta = $values['delta'];$this->epsilon = $values['epsilon']; + ) { + $this->gamma = $values['gamma']; + $this->delta = $values['delta']; + $this->epsilon = $values['epsilon']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/websocket/src/Realtime/Types/ReceiveEvent3.php b/seed/php-sdk/websocket/src/Realtime/Types/ReceiveEvent3.php index 9e8e0adea636..1ba38f512ae1 100644 --- a/seed/php-sdk/websocket/src/Realtime/Types/ReceiveEvent3.php +++ b/seed/php-sdk/websocket/src/Realtime/Types/ReceiveEvent3.php @@ -20,15 +20,15 @@ class ReceiveEvent3 extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->receiveText3 = $values['receiveText3']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/websocket/src/Realtime/Types/ReceiveSnakeCase.php b/seed/php-sdk/websocket/src/Realtime/Types/ReceiveSnakeCase.php index b32f36399189..76e40e11868e 100644 --- a/seed/php-sdk/websocket/src/Realtime/Types/ReceiveSnakeCase.php +++ b/seed/php-sdk/websocket/src/Realtime/Types/ReceiveSnakeCase.php @@ -27,15 +27,16 @@ class ReceiveSnakeCase extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->receiveText = $values['receiveText'];$this->receiveInt = $values['receiveInt']; + ) { + $this->receiveText = $values['receiveText']; + $this->receiveInt = $values['receiveInt']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/websocket/src/Realtime/Types/SendEvent.php b/seed/php-sdk/websocket/src/Realtime/Types/SendEvent.php index 51bf846db683..dd67bbc75dd1 100644 --- a/seed/php-sdk/websocket/src/Realtime/Types/SendEvent.php +++ b/seed/php-sdk/websocket/src/Realtime/Types/SendEvent.php @@ -27,15 +27,16 @@ class SendEvent extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->sendText = $values['sendText'];$this->sendParam = $values['sendParam']; + ) { + $this->sendText = $values['sendText']; + $this->sendParam = $values['sendParam']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/websocket/src/Realtime/Types/SendEvent2.php b/seed/php-sdk/websocket/src/Realtime/Types/SendEvent2.php index 626fe0c74e70..90618e890fc4 100644 --- a/seed/php-sdk/websocket/src/Realtime/Types/SendEvent2.php +++ b/seed/php-sdk/websocket/src/Realtime/Types/SendEvent2.php @@ -27,15 +27,16 @@ class SendEvent2 extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->sendText2 = $values['sendText2'];$this->sendParam2 = $values['sendParam2']; + ) { + $this->sendText2 = $values['sendText2']; + $this->sendParam2 = $values['sendParam2']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/websocket/src/Realtime/Types/SendSnakeCase.php b/seed/php-sdk/websocket/src/Realtime/Types/SendSnakeCase.php index 58006c969776..91086ef5e53f 100644 --- a/seed/php-sdk/websocket/src/Realtime/Types/SendSnakeCase.php +++ b/seed/php-sdk/websocket/src/Realtime/Types/SendSnakeCase.php @@ -27,15 +27,16 @@ class SendSnakeCase extends JsonSerializableType */ public function __construct( array $values, - ) - { - $this->sendText = $values['sendText'];$this->sendParam = $values['sendParam']; + ) { + $this->sendText = $values['sendText']; + $this->sendParam = $values['sendParam']; } /** * @return string */ - public function __toString(): string { + public function __toString(): string + { return $this->toJson(); } } diff --git a/seed/php-sdk/websocket/src/SeedClient.php b/seed/php-sdk/websocket/src/SeedClient.php index efe6e7438b31..37a1c4dd76aa 100644 --- a/seed/php-sdk/websocket/src/SeedClient.php +++ b/seed/php-sdk/websocket/src/SeedClient.php @@ -2,7 +2,6 @@ namespace Seed; -class SeedClient +class SeedClient { - } diff --git a/seed/php-sdk/websocket/src/Utils/File.php b/seed/php-sdk/websocket/src/Utils/File.php index f1fd8a438dd1..b91f2419e183 100644 --- a/seed/php-sdk/websocket/src/Utils/File.php +++ b/seed/php-sdk/websocket/src/Utils/File.php @@ -122,4 +122,4 @@ public function __destruct() { $this->close(); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/websocket/tests/Core/Client/RawClientTest.php b/seed/php-sdk/websocket/tests/Core/Client/RawClientTest.php index 23d4aaaa45a2..74a9e9368812 100644 --- a/seed/php-sdk/websocket/tests/Core/Client/RawClientTest.php +++ b/seed/php-sdk/websocket/tests/Core/Client/RawClientTest.php @@ -18,7 +18,8 @@ use Seed\Core\Json\JsonSerializableType; use Seed\Core\Json\JsonProperty; -class JsonRequest extends JsonSerializableType { +class JsonRequest extends JsonSerializableType +{ /** * @var string */ diff --git a/seed/php-sdk/websocket/tests/Core/Json/AdditionalPropertiesTest.php b/seed/php-sdk/websocket/tests/Core/Json/AdditionalPropertiesTest.php index e4a66046b881..5bcb7ba283a8 100644 --- a/seed/php-sdk/websocket/tests/Core/Json/AdditionalPropertiesTest.php +++ b/seed/php-sdk/websocket/tests/Core/Json/AdditionalPropertiesTest.php @@ -44,8 +44,7 @@ public function getEmail(): ?string */ public function __construct( array $values, - ) - { + ) { $this->name = $values['name']; $this->email = $values['email'] ?? null; } @@ -67,10 +66,11 @@ public function testExtraProperties(): void $person = Person::fromJson($expectedJson); $this->assertEquals('john.doe', $person->getName()); $this->assertEquals('john.doe@example.com', $person->getEmail()); - $this->assertEquals([ + $this->assertEquals( + [ 'age' => 42 ], $person->getAdditionalProperties(), ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/websocket/tests/Core/Json/EnumTest.php b/seed/php-sdk/websocket/tests/Core/Json/EnumTest.php index 2aaafc1ccf33..bf83d5b8ab0f 100644 --- a/seed/php-sdk/websocket/tests/Core/Json/EnumTest.php +++ b/seed/php-sdk/websocket/tests/Core/Json/EnumTest.php @@ -73,4 +73,4 @@ public function testEnumSerialization(): void 'Serialized JSON does not match expected JSON for shape and shapes properties.' ); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/websocket/tests/Core/Json/TraitTest.php b/seed/php-sdk/websocket/tests/Core/Json/TraitTest.php index 70d977ff8464..837f239115f7 100644 --- a/seed/php-sdk/websocket/tests/Core/Json/TraitTest.php +++ b/seed/php-sdk/websocket/tests/Core/Json/TraitTest.php @@ -53,8 +53,8 @@ public function testTraitPropertyAndString(): void $object = TypeWithTrait::fromJson($expectedJson); $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); - + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); } -} \ No newline at end of file +} diff --git a/seed/php-sdk/websocket/tests/Core/Json/UnionPropertyTest.php b/seed/php-sdk/websocket/tests/Core/Json/UnionPropertyTest.php index fe232ce42d40..3119baace627 100644 --- a/seed/php-sdk/websocket/tests/Core/Json/UnionPropertyTest.php +++ b/seed/php-sdk/websocket/tests/Core/Json/UnionPropertyTest.php @@ -9,7 +9,6 @@ class UnionProperty extends JsonSerializableType { - #[Union(new Union('string', 'integer'), 'null', ['integer' => 'integer'], UnionProperty::class)] #[JsonProperty('complexUnion')] public mixed $complexUnion; @@ -21,8 +20,7 @@ class UnionProperty extends JsonSerializableType */ public function __construct( array $values, - ) - { + ) { $this->complexUnion = $values['complexUnion']; } } @@ -63,7 +61,7 @@ public function testWithNestedUnionPropertyType(): void $this->assertInstanceOf(UnionProperty::class, $object->complexUnion, 'complexUnion should be an instance of UnionPropertyType.'); $this->assertEquals('Nested String', $object->complexUnion->complexUnion, 'Nested complexUnion should match the original value.'); - $actualJson= $object->toJson(); + $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } @@ -114,4 +112,4 @@ public function testWithString(): void $actualJson = $object->toJson(); $this->assertJsonStringEqualsJsonString($expectedJson, $actualJson, 'Serialized JSON does not match the original JSON.'); } -} \ No newline at end of file +} diff --git a/seed/postman/inferred-auth-implicit-api-key/collection.json b/seed/postman/inferred-auth-implicit-api-key/collection.json new file mode 100644 index 000000000000..9894ebacc3d6 --- /dev/null +++ b/seed/postman/inferred-auth-implicit-api-key/collection.json @@ -0,0 +1,323 @@ +{ + "info": { + "name": "Inferred Auth Implicit Api Key", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "description": null + }, + "variable": [ + { + "key": "baseUrl", + "value": "", + "type": "string" + } + ], + "auth": null, + "item": [ + { + "_type": "container", + "description": null, + "name": "Auth", + "item": [ + { + "_type": "endpoint", + "name": "Get Token", + "request": { + "description": null, + "url": { + "raw": "{{baseUrl}}/token", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "token" + ], + "query": [], + "variable": [] + }, + "header": [ + { + "key": "X-Api-Key", + "description": null, + "type": "text", + "value": "\"api_key\"" + }, + { + "type": "text", + "key": "Content-Type", + "value": "application/json" + } + ], + "method": "POST", + "auth": null, + "body": null + }, + "response": [ + { + "name": "Success", + "status": "OK", + "code": 200, + "originalRequest": { + "description": null, + "url": { + "raw": "{{baseUrl}}/token", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "token" + ], + "query": [], + "variable": [] + }, + "header": [ + { + "key": "X-Api-Key", + "description": null, + "type": "text", + "value": "\"api_key\"" + }, + { + "type": "text", + "key": "Content-Type", + "value": "application/json" + } + ], + "method": "POST", + "auth": null, + "body": null + }, + "description": null, + "body": "{\n \"access_token\": \"access_token\",\n \"token_type\": \"token_type\",\n \"expires_in\": 1,\n \"scope\": \"scope\"\n}", + "_postman_previewlanguage": "json" + } + ] + } + ] + }, + { + "_type": "container", + "description": null, + "name": "Nested No Auth", + "item": [ + { + "_type": "container", + "description": null, + "name": "Api", + "item": [ + { + "_type": "endpoint", + "name": "Get Something", + "request": { + "description": null, + "url": { + "raw": "{{baseUrl}}/nested-no-auth/get-something", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "nested-no-auth", + "get-something" + ], + "query": [], + "variable": [] + }, + "header": [ + { + "type": "text", + "key": "Content-Type", + "value": "application/json" + } + ], + "method": "GET", + "auth": null, + "body": null + }, + "response": [ + { + "name": "Success", + "status": "OK", + "code": 200, + "originalRequest": { + "description": null, + "url": { + "raw": "{{baseUrl}}/nested-no-auth/get-something", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "nested-no-auth", + "get-something" + ], + "query": [], + "variable": [] + }, + "header": [ + { + "type": "text", + "key": "Content-Type", + "value": "application/json" + } + ], + "method": "GET", + "auth": null, + "body": null + }, + "description": null, + "body": "", + "_postman_previewlanguage": "json" + } + ] + } + ] + } + ] + }, + { + "_type": "container", + "description": null, + "name": "Nested", + "item": [ + { + "_type": "container", + "description": null, + "name": "Api", + "item": [ + { + "_type": "endpoint", + "name": "Get Something", + "request": { + "description": null, + "url": { + "raw": "{{baseUrl}}/nested/get-something", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "nested", + "get-something" + ], + "query": [], + "variable": [] + }, + "header": [ + { + "type": "text", + "key": "Content-Type", + "value": "application/json" + } + ], + "method": "GET", + "auth": null, + "body": null + }, + "response": [ + { + "name": "Success", + "status": "OK", + "code": 200, + "originalRequest": { + "description": null, + "url": { + "raw": "{{baseUrl}}/nested/get-something", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "nested", + "get-something" + ], + "query": [], + "variable": [] + }, + "header": [ + { + "type": "text", + "key": "Content-Type", + "value": "application/json" + } + ], + "method": "GET", + "auth": null, + "body": null + }, + "description": null, + "body": "", + "_postman_previewlanguage": "json" + } + ] + } + ] + } + ] + }, + { + "_type": "container", + "description": null, + "name": "Simple", + "item": [ + { + "_type": "endpoint", + "name": "Get Something", + "request": { + "description": null, + "url": { + "raw": "{{baseUrl}}/get-something", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "get-something" + ], + "query": [], + "variable": [] + }, + "header": [ + { + "type": "text", + "key": "Content-Type", + "value": "application/json" + } + ], + "method": "GET", + "auth": null, + "body": null + }, + "response": [ + { + "name": "Success", + "status": "OK", + "code": 200, + "originalRequest": { + "description": null, + "url": { + "raw": "{{baseUrl}}/get-something", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "get-something" + ], + "query": [], + "variable": [] + }, + "header": [ + { + "type": "text", + "key": "Content-Type", + "value": "application/json" + } + ], + "method": "GET", + "auth": null, + "body": null + }, + "description": null, + "body": "", + "_postman_previewlanguage": "json" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/seed/postman/inferred-auth-implicit-api-key/snippet.json b/seed/postman/inferred-auth-implicit-api-key/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/postman/inferred-auth-implicit-reference/collection.json b/seed/postman/inferred-auth-implicit-reference/collection.json new file mode 100644 index 000000000000..2a1530db7d3a --- /dev/null +++ b/seed/postman/inferred-auth-implicit-reference/collection.json @@ -0,0 +1,407 @@ +{ + "info": { + "name": "Inferred Auth Implicit", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "description": null + }, + "variable": [ + { + "key": "baseUrl", + "value": "", + "type": "string" + } + ], + "auth": null, + "item": [ + { + "_type": "container", + "description": null, + "name": "Auth", + "item": [ + { + "_type": "endpoint", + "name": "Get Token With Client Credentials", + "request": { + "description": null, + "url": { + "raw": "{{baseUrl}}/token", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "token" + ], + "query": [], + "variable": [] + }, + "header": [ + { + "type": "text", + "key": "Content-Type", + "value": "application/json" + } + ], + "method": "POST", + "auth": null, + "body": { + "mode": "raw", + "raw": "{\n \"client_id\": \"client_id\",\n \"client_secret\": \"client_secret\",\n \"audience\": \"https://api.example.com\",\n \"grant_type\": \"client_credentials\",\n \"scope\": \"scope\"\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "response": [ + { + "name": "Success", + "status": "OK", + "code": 200, + "originalRequest": { + "description": null, + "url": { + "raw": "{{baseUrl}}/token", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "token" + ], + "query": [], + "variable": [] + }, + "header": [ + { + "type": "text", + "key": "Content-Type", + "value": "application/json" + } + ], + "method": "POST", + "auth": null, + "body": { + "mode": "raw", + "raw": "{\n \"client_id\": \"client_id\",\n \"client_secret\": \"client_secret\",\n \"audience\": \"https://api.example.com\",\n \"grant_type\": \"client_credentials\",\n \"scope\": \"scope\"\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "description": null, + "body": "{\n \"access_token\": \"access_token\",\n \"expires_in\": 1,\n \"refresh_token\": \"refresh_token\"\n}", + "_postman_previewlanguage": "json" + } + ] + }, + { + "_type": "endpoint", + "name": "Refresh Token", + "request": { + "description": null, + "url": { + "raw": "{{baseUrl}}/token/refresh", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "token", + "refresh" + ], + "query": [], + "variable": [] + }, + "header": [ + { + "type": "text", + "key": "Content-Type", + "value": "application/json" + } + ], + "method": "POST", + "auth": null, + "body": { + "mode": "raw", + "raw": "{\n \"client_id\": \"client_id\",\n \"client_secret\": \"client_secret\",\n \"refresh_token\": \"refresh_token\",\n \"audience\": \"https://api.example.com\",\n \"grant_type\": \"refresh_token\",\n \"scope\": \"scope\"\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "response": [ + { + "name": "Success", + "status": "OK", + "code": 200, + "originalRequest": { + "description": null, + "url": { + "raw": "{{baseUrl}}/token/refresh", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "token", + "refresh" + ], + "query": [], + "variable": [] + }, + "header": [ + { + "type": "text", + "key": "Content-Type", + "value": "application/json" + } + ], + "method": "POST", + "auth": null, + "body": { + "mode": "raw", + "raw": "{\n \"client_id\": \"client_id\",\n \"client_secret\": \"client_secret\",\n \"refresh_token\": \"refresh_token\",\n \"audience\": \"https://api.example.com\",\n \"grant_type\": \"refresh_token\",\n \"scope\": \"scope\"\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "description": null, + "body": "{\n \"access_token\": \"access_token\",\n \"expires_in\": 1,\n \"refresh_token\": \"refresh_token\"\n}", + "_postman_previewlanguage": "json" + } + ] + } + ] + }, + { + "_type": "container", + "description": null, + "name": "Nested No Auth", + "item": [ + { + "_type": "container", + "description": null, + "name": "Api", + "item": [ + { + "_type": "endpoint", + "name": "Get Something", + "request": { + "description": null, + "url": { + "raw": "{{baseUrl}}/nested-no-auth/get-something", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "nested-no-auth", + "get-something" + ], + "query": [], + "variable": [] + }, + "header": [ + { + "type": "text", + "key": "Content-Type", + "value": "application/json" + } + ], + "method": "GET", + "auth": null, + "body": null + }, + "response": [ + { + "name": "Success", + "status": "OK", + "code": 200, + "originalRequest": { + "description": null, + "url": { + "raw": "{{baseUrl}}/nested-no-auth/get-something", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "nested-no-auth", + "get-something" + ], + "query": [], + "variable": [] + }, + "header": [ + { + "type": "text", + "key": "Content-Type", + "value": "application/json" + } + ], + "method": "GET", + "auth": null, + "body": null + }, + "description": null, + "body": "", + "_postman_previewlanguage": "json" + } + ] + } + ] + } + ] + }, + { + "_type": "container", + "description": null, + "name": "Nested", + "item": [ + { + "_type": "container", + "description": null, + "name": "Api", + "item": [ + { + "_type": "endpoint", + "name": "Get Something", + "request": { + "description": null, + "url": { + "raw": "{{baseUrl}}/nested/get-something", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "nested", + "get-something" + ], + "query": [], + "variable": [] + }, + "header": [ + { + "type": "text", + "key": "Content-Type", + "value": "application/json" + } + ], + "method": "GET", + "auth": null, + "body": null + }, + "response": [ + { + "name": "Success", + "status": "OK", + "code": 200, + "originalRequest": { + "description": null, + "url": { + "raw": "{{baseUrl}}/nested/get-something", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "nested", + "get-something" + ], + "query": [], + "variable": [] + }, + "header": [ + { + "type": "text", + "key": "Content-Type", + "value": "application/json" + } + ], + "method": "GET", + "auth": null, + "body": null + }, + "description": null, + "body": "", + "_postman_previewlanguage": "json" + } + ] + } + ] + } + ] + }, + { + "_type": "container", + "description": null, + "name": "Simple", + "item": [ + { + "_type": "endpoint", + "name": "Get Something", + "request": { + "description": null, + "url": { + "raw": "{{baseUrl}}/get-something", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "get-something" + ], + "query": [], + "variable": [] + }, + "header": [ + { + "type": "text", + "key": "Content-Type", + "value": "application/json" + } + ], + "method": "GET", + "auth": null, + "body": null + }, + "response": [ + { + "name": "Success", + "status": "OK", + "code": 200, + "originalRequest": { + "description": null, + "url": { + "raw": "{{baseUrl}}/get-something", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "get-something" + ], + "query": [], + "variable": [] + }, + "header": [ + { + "type": "text", + "key": "Content-Type", + "value": "application/json" + } + ], + "method": "GET", + "auth": null, + "body": null + }, + "description": null, + "body": "", + "_postman_previewlanguage": "json" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/seed/postman/inferred-auth-implicit-reference/snippet.json b/seed/postman/inferred-auth-implicit-reference/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/postman/nullable-allof-extends/collection.json b/seed/postman/nullable-allof-extends/collection.json new file mode 100644 index 000000000000..507c632fe368 --- /dev/null +++ b/seed/postman/nullable-allof-extends/collection.json @@ -0,0 +1,229 @@ +{ + "info": { + "name": "Nullable AllOf Extends Test", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "description": null + }, + "variable": [ + { + "key": "baseUrl", + "value": "https://api.example.com", + "type": "string" + } + ], + "auth": null, + "item": [ + { + "_type": "endpoint", + "name": "Get Test", + "request": { + "description": "Returns a RootObject which inherits from a nullable schema.", + "url": { + "raw": "{{baseUrl}}/test", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "test" + ], + "query": [], + "variable": [] + }, + "header": [ + { + "type": "text", + "key": "Content-Type", + "value": "application/json" + } + ], + "method": "GET", + "auth": null, + "body": null + }, + "response": [ + { + "name": "Success", + "status": "OK", + "code": 200, + "originalRequest": { + "description": "Returns a RootObject which inherits from a nullable schema.", + "url": { + "raw": "{{baseUrl}}/test", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "test" + ], + "query": [], + "variable": [] + }, + "header": [ + { + "type": "text", + "key": "Content-Type", + "value": "application/json" + } + ], + "method": "GET", + "auth": null, + "body": null + }, + "description": "OK", + "body": "{\n \"normalField\": \"normalField\"\n}", + "_postman_previewlanguage": "json" + }, + { + "name": "Success", + "status": "OK", + "code": 200, + "originalRequest": { + "description": "Returns a RootObject which inherits from a nullable schema.", + "url": { + "raw": "{{baseUrl}}/test", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "test" + ], + "query": [], + "variable": [] + }, + "header": [ + { + "type": "text", + "key": "Content-Type", + "value": "application/json" + } + ], + "method": "GET", + "auth": null, + "body": null + }, + "description": "OK", + "body": "{\n \"normalField\": \"normalField\",\n \"nullableField\": \"nullableField\"\n}", + "_postman_previewlanguage": "json" + } + ] + }, + { + "_type": "endpoint", + "name": "Create Test", + "request": { + "description": "Creates a test object with nullable allOf in request body.", + "url": { + "raw": "{{baseUrl}}/test", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "test" + ], + "query": [], + "variable": [] + }, + "header": [ + { + "type": "text", + "key": "Content-Type", + "value": "application/json" + } + ], + "method": "POST", + "auth": null, + "body": { + "mode": "raw", + "raw": "{}", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "response": [ + { + "name": "Success", + "status": "OK", + "code": 200, + "originalRequest": { + "description": "Creates a test object with nullable allOf in request body.", + "url": { + "raw": "{{baseUrl}}/test", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "test" + ], + "query": [], + "variable": [] + }, + "header": [ + { + "type": "text", + "key": "Content-Type", + "value": "application/json" + } + ], + "method": "POST", + "auth": null, + "body": { + "mode": "raw", + "raw": "{}", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "description": "OK", + "body": "{\n \"normalField\": \"normalField\"\n}", + "_postman_previewlanguage": "json" + }, + { + "name": "Success", + "status": "OK", + "code": 200, + "originalRequest": { + "description": "Creates a test object with nullable allOf in request body.", + "url": { + "raw": "{{baseUrl}}/test", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "test" + ], + "query": [], + "variable": [] + }, + "header": [ + { + "type": "text", + "key": "Content-Type", + "value": "application/json" + } + ], + "method": "POST", + "auth": null, + "body": { + "mode": "raw", + "raw": "{\n \"normalField\": \"normalField\",\n \"nullableField\": \"nullableField\"\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "description": "OK", + "body": "{\n \"normalField\": \"normalField\",\n \"nullableField\": \"nullableField\"\n}", + "_postman_previewlanguage": "json" + } + ] + } + ] +} \ No newline at end of file diff --git a/seed/postman/nullable-allof-extends/snippet.json b/seed/postman/nullable-allof-extends/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/postman/oauth-client-credentials-mandatory-auth/collection.json b/seed/postman/oauth-client-credentials-mandatory-auth/collection.json new file mode 100644 index 000000000000..a7a204cccdd8 --- /dev/null +++ b/seed/postman/oauth-client-credentials-mandatory-auth/collection.json @@ -0,0 +1,421 @@ +{ + "info": { + "name": "Oauth Client Credentials Mandatory Auth", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "description": null + }, + "variable": [ + { + "key": "baseUrl", + "value": "", + "type": "string" + }, + { + "key": "token", + "value": "", + "type": "string" + } + ], + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "item": [ + { + "_type": "container", + "description": null, + "name": "Auth", + "item": [ + { + "_type": "endpoint", + "name": "Get Token With Client Credentials", + "request": { + "description": null, + "url": { + "raw": "{{baseUrl}}/token", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "token" + ], + "query": [], + "variable": [] + }, + "header": [ + { + "type": "text", + "key": "Content-Type", + "value": "application/json" + } + ], + "method": "POST", + "auth": null, + "body": { + "mode": "raw", + "raw": "{\n \"client_id\": \"my_oauth_app_123\",\n \"client_secret\": \"sk_live_abcdef123456789\",\n \"audience\": \"https://api.example.com\",\n \"grant_type\": \"client_credentials\",\n \"scope\": \"read:users\"\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "response": [ + { + "name": "Success", + "status": "OK", + "code": 200, + "originalRequest": { + "description": null, + "url": { + "raw": "{{baseUrl}}/token", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "token" + ], + "query": [], + "variable": [] + }, + "header": [ + { + "type": "text", + "key": "Content-Type", + "value": "application/json" + } + ], + "method": "POST", + "auth": null, + "body": { + "mode": "raw", + "raw": "{\n \"client_id\": \"my_oauth_app_123\",\n \"client_secret\": \"sk_live_abcdef123456789\",\n \"audience\": \"https://api.example.com\",\n \"grant_type\": \"client_credentials\",\n \"scope\": \"read:users\"\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "description": null, + "body": "{\n \"access_token\": \"access_token\",\n \"expires_in\": 1,\n \"refresh_token\": \"refresh_token\"\n}", + "_postman_previewlanguage": "json" + }, + { + "name": "Success", + "status": "OK", + "code": 200, + "originalRequest": { + "description": null, + "url": { + "raw": "{{baseUrl}}/token", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "token" + ], + "query": [], + "variable": [] + }, + "header": [ + { + "type": "text", + "key": "Content-Type", + "value": "application/json" + } + ], + "method": "POST", + "auth": null, + "body": { + "mode": "raw", + "raw": "{\n \"client_id\": \"client_id\",\n \"client_secret\": \"client_secret\",\n \"audience\": \"https://api.example.com\",\n \"grant_type\": \"client_credentials\",\n \"scope\": \"scope\"\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "description": null, + "body": "{\n \"access_token\": \"access_token\",\n \"expires_in\": 1,\n \"refresh_token\": \"refresh_token\"\n}", + "_postman_previewlanguage": "json" + } + ] + }, + { + "_type": "endpoint", + "name": "Refresh Token", + "request": { + "description": null, + "url": { + "raw": "{{baseUrl}}/token", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "token" + ], + "query": [], + "variable": [] + }, + "header": [ + { + "type": "text", + "key": "Content-Type", + "value": "application/json" + } + ], + "method": "POST", + "auth": null, + "body": { + "mode": "raw", + "raw": "{\n \"client_id\": \"my_oauth_app_123\",\n \"client_secret\": \"sk_live_abcdef123456789\",\n \"refresh_token\": \"refresh_token\",\n \"audience\": \"https://api.example.com\",\n \"grant_type\": \"refresh_token\",\n \"scope\": \"read:users\"\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "response": [ + { + "name": "Success", + "status": "OK", + "code": 200, + "originalRequest": { + "description": null, + "url": { + "raw": "{{baseUrl}}/token", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "token" + ], + "query": [], + "variable": [] + }, + "header": [ + { + "type": "text", + "key": "Content-Type", + "value": "application/json" + } + ], + "method": "POST", + "auth": null, + "body": { + "mode": "raw", + "raw": "{\n \"client_id\": \"my_oauth_app_123\",\n \"client_secret\": \"sk_live_abcdef123456789\",\n \"refresh_token\": \"refresh_token\",\n \"audience\": \"https://api.example.com\",\n \"grant_type\": \"refresh_token\",\n \"scope\": \"read:users\"\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "description": null, + "body": "{\n \"access_token\": \"access_token\",\n \"expires_in\": 1,\n \"refresh_token\": \"refresh_token\"\n}", + "_postman_previewlanguage": "json" + }, + { + "name": "Success", + "status": "OK", + "code": 200, + "originalRequest": { + "description": null, + "url": { + "raw": "{{baseUrl}}/token", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "token" + ], + "query": [], + "variable": [] + }, + "header": [ + { + "type": "text", + "key": "Content-Type", + "value": "application/json" + } + ], + "method": "POST", + "auth": null, + "body": { + "mode": "raw", + "raw": "{\n \"client_id\": \"client_id\",\n \"client_secret\": \"client_secret\",\n \"refresh_token\": \"refresh_token\",\n \"audience\": \"https://api.example.com\",\n \"grant_type\": \"refresh_token\",\n \"scope\": \"scope\"\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "description": null, + "body": "{\n \"access_token\": \"access_token\",\n \"expires_in\": 1,\n \"refresh_token\": \"refresh_token\"\n}", + "_postman_previewlanguage": "json" + } + ] + } + ] + }, + { + "_type": "container", + "description": null, + "name": "Nested", + "item": [ + { + "_type": "container", + "description": null, + "name": "Api", + "item": [ + { + "_type": "endpoint", + "name": "Get Something", + "request": { + "description": null, + "url": { + "raw": "{{baseUrl}}/nested/get-something", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "nested", + "get-something" + ], + "query": [], + "variable": [] + }, + "header": [ + { + "type": "text", + "key": "Content-Type", + "value": "application/json" + } + ], + "method": "GET", + "auth": null, + "body": null + }, + "response": [ + { + "name": "Success", + "status": "OK", + "code": 200, + "originalRequest": { + "description": null, + "url": { + "raw": "{{baseUrl}}/nested/get-something", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "nested", + "get-something" + ], + "query": [], + "variable": [] + }, + "header": [ + { + "type": "text", + "key": "Content-Type", + "value": "application/json" + } + ], + "method": "GET", + "auth": null, + "body": null + }, + "description": null, + "body": "", + "_postman_previewlanguage": "json" + } + ] + } + ] + } + ] + }, + { + "_type": "container", + "description": null, + "name": "Simple", + "item": [ + { + "_type": "endpoint", + "name": "Get Something", + "request": { + "description": null, + "url": { + "raw": "{{baseUrl}}/get-something", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "get-something" + ], + "query": [], + "variable": [] + }, + "header": [ + { + "type": "text", + "key": "Content-Type", + "value": "application/json" + } + ], + "method": "GET", + "auth": null, + "body": null + }, + "response": [ + { + "name": "Success", + "status": "OK", + "code": 200, + "originalRequest": { + "description": null, + "url": { + "raw": "{{baseUrl}}/get-something", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "get-something" + ], + "query": [], + "variable": [] + }, + "header": [ + { + "type": "text", + "key": "Content-Type", + "value": "application/json" + } + ], + "method": "GET", + "auth": null, + "body": null + }, + "description": null, + "body": "", + "_postman_previewlanguage": "json" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/seed/postman/oauth-client-credentials-mandatory-auth/snippet.json b/seed/postman/oauth-client-credentials-mandatory-auth/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/postman/pagination/collection.json b/seed/postman/pagination/collection.json index 5a43e3ab6ef8..e86d53a4e808 100644 --- a/seed/postman/pagination/collection.json +++ b/seed/postman/pagination/collection.json @@ -2272,6 +2272,160 @@ "_postman_previewlanguage": "json" } ] + }, + { + "_type": "endpoint", + "name": "List With Optional Data", + "request": { + "description": null, + "url": { + "raw": "{{baseUrl}}/users/optional-data?page=1", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + "optional-data" + ], + "query": [ + { + "key": "page", + "description": "Defaults to first page", + "value": "1" + } + ], + "variable": [] + }, + "header": [ + { + "type": "text", + "key": "Content-Type", + "value": "application/json" + } + ], + "method": "GET", + "auth": null, + "body": null + }, + "response": [ + { + "name": "Success", + "status": "OK", + "code": 200, + "originalRequest": { + "description": null, + "url": { + "raw": "{{baseUrl}}/users/optional-data?page=1", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + "optional-data" + ], + "query": [ + { + "key": "page", + "description": "Defaults to first page", + "value": "1" + } + ], + "variable": [] + }, + "header": [ + { + "type": "text", + "key": "Content-Type", + "value": "application/json" + } + ], + "method": "GET", + "auth": null, + "body": null + }, + "description": null, + "body": "{\n \"hasNextPage\": true,\n \"page\": {\n \"page\": 1,\n \"next\": {\n \"page\": 2,\n \"starting_after\": \"next_cursor\"\n },\n \"per_page\": 10,\n \"total_page\": 5\n },\n \"total_count\": 50,\n \"data\": [\n {\n \"name\": \"Alice\",\n \"id\": 1\n },\n {\n \"name\": \"Bob\",\n \"id\": 2\n }\n ]\n}", + "_postman_previewlanguage": "json" + }, + { + "name": "Success", + "status": "OK", + "code": 200, + "originalRequest": { + "description": null, + "url": { + "raw": "{{baseUrl}}/users/optional-data?page=1", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + "optional-data" + ], + "query": [ + { + "key": "page", + "description": "Defaults to first page", + "value": "1" + } + ], + "variable": [] + }, + "header": [ + { + "type": "text", + "key": "Content-Type", + "value": "application/json" + } + ], + "method": "GET", + "auth": null, + "body": null + }, + "description": null, + "body": "{\n \"hasNextPage\": false,\n \"page\": {\n \"page\": 1,\n \"next\": {\n \"page\": 2,\n \"starting_after\": \"next_cursor\"\n },\n \"per_page\": 10,\n \"total_page\": 1\n },\n \"total_count\": 0\n}", + "_postman_previewlanguage": "json" + }, + { + "name": "Success", + "status": "OK", + "code": 200, + "originalRequest": { + "description": null, + "url": { + "raw": "{{baseUrl}}/users/optional-data?page=1", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + "optional-data" + ], + "query": [ + { + "key": "page", + "description": "Defaults to first page", + "value": "1" + } + ], + "variable": [] + }, + "header": [ + { + "type": "text", + "key": "Content-Type", + "value": "application/json" + } + ], + "method": "GET", + "auth": null, + "body": null + }, + "description": null, + "body": "{\n \"hasNextPage\": true,\n \"page\": {\n \"page\": 1,\n \"next\": {\n \"page\": 1,\n \"starting_after\": \"starting_after\"\n },\n \"per_page\": 1,\n \"total_page\": 1\n },\n \"total_count\": 1,\n \"data\": [\n {\n \"name\": \"name\",\n \"id\": 1\n },\n {\n \"name\": \"name\",\n \"id\": 1\n }\n ]\n}", + "_postman_previewlanguage": "json" + } + ] } ] } diff --git a/seed/pydantic/inferred-auth-implicit-api-key/.github/workflows/ci.yml b/seed/pydantic/inferred-auth-implicit-api-key/.github/workflows/ci.yml new file mode 100644 index 000000000000..ffd2d8acab24 --- /dev/null +++ b/seed/pydantic/inferred-auth-implicit-api-key/.github/workflows/ci.yml @@ -0,0 +1,60 @@ +name: ci +on: [push] +jobs: + compile: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + - name: Set up python + uses: actions/setup-python@v4 + with: + python-version: 3.9 + - name: Bootstrap poetry + run: | + curl -sSL https://install.python-poetry.org | python - -y --version 1.5.1 + - name: Install dependencies + run: poetry install + - name: Compile + run: poetry run mypy . + test: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + - name: Set up python + uses: actions/setup-python@v4 + with: + python-version: 3.9 + - name: Bootstrap poetry + run: | + curl -sSL https://install.python-poetry.org | python - -y --version 1.5.1 + - name: Install dependencies + run: poetry install + + - name: Test + run: poetry run pytest -rP -n auto . + + publish: + needs: [compile, test] + if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + - name: Set up python + uses: actions/setup-python@v4 + with: + python-version: 3.9 + - name: Bootstrap poetry + run: | + curl -sSL https://install.python-poetry.org | python - -y --version 1.5.1 + - name: Install dependencies + run: poetry install + - name: Publish to pypi + run: | + poetry config repositories.remote + poetry --no-interaction -v publish --build --repository remote --username "$PYPI_USERNAME" --password "$PYPI_PASSWORD" + env: + PYPI_USERNAME: ${{ secrets.PYPI_USERNAME }} + PYPI_PASSWORD: ${{ secrets.PYPI_PASSWORD }} diff --git a/seed/pydantic/inferred-auth-implicit-api-key/.gitignore b/seed/pydantic/inferred-auth-implicit-api-key/.gitignore new file mode 100644 index 000000000000..d2e4ca808d21 --- /dev/null +++ b/seed/pydantic/inferred-auth-implicit-api-key/.gitignore @@ -0,0 +1,5 @@ +.mypy_cache/ +.ruff_cache/ +__pycache__/ +dist/ +poetry.toml diff --git a/seed/pydantic/inferred-auth-implicit-api-key/README.md b/seed/pydantic/inferred-auth-implicit-api-key/README.md new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/pydantic/inferred-auth-implicit-api-key/poetry.lock b/seed/pydantic/inferred-auth-implicit-api-key/poetry.lock new file mode 100644 index 000000000000..60f7bd567c95 --- /dev/null +++ b/seed/pydantic/inferred-auth-implicit-api-key/poetry.lock @@ -0,0 +1,480 @@ +# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. + +[[package]] +name = "annotated-types" +version = "0.7.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +files = [ + {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, + {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.9\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "exceptiongroup" +version = "1.3.1" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598"}, + {file = "exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""} + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "execnet" +version = "2.1.2" +description = "execnet: rapid multi-Python deployment" +optional = false +python-versions = ">=3.8" +files = [ + {file = "execnet-2.1.2-py3-none-any.whl", hash = "sha256:67fba928dd5a544b783f6056f449e5e3931a5c378b128bc18501f7ea79e296ec"}, + {file = "execnet-2.1.2.tar.gz", hash = "sha256:63d83bfdd9a23e35b9c6a3261412324f964c2ec8dcd8d3c6916ee9373e0befcd"}, +] + +[package.extras] +testing = ["hatch", "pre-commit", "pytest", "tox"] + +[[package]] +name = "iniconfig" +version = "2.1.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.8" +files = [ + {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, + {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, +] + +[[package]] +name = "mypy" +version = "1.13.0" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mypy-1.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6607e0f1dd1fb7f0aca14d936d13fd19eba5e17e1cd2a14f808fa5f8f6d8f60a"}, + {file = "mypy-1.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8a21be69bd26fa81b1f80a61ee7ab05b076c674d9b18fb56239d72e21d9f4c80"}, + {file = "mypy-1.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b2353a44d2179846a096e25691d54d59904559f4232519d420d64da6828a3a7"}, + {file = "mypy-1.13.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0730d1c6a2739d4511dc4253f8274cdd140c55c32dfb0a4cf8b7a43f40abfa6f"}, + {file = "mypy-1.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:c5fc54dbb712ff5e5a0fca797e6e0aa25726c7e72c6a5850cfd2adbc1eb0a372"}, + {file = "mypy-1.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:581665e6f3a8a9078f28d5502f4c334c0c8d802ef55ea0e7276a6e409bc0d82d"}, + {file = "mypy-1.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3ddb5b9bf82e05cc9a627e84707b528e5c7caaa1c55c69e175abb15a761cec2d"}, + {file = "mypy-1.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20c7ee0bc0d5a9595c46f38beb04201f2620065a93755704e141fcac9f59db2b"}, + {file = "mypy-1.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3790ded76f0b34bc9c8ba4def8f919dd6a46db0f5a6610fb994fe8efdd447f73"}, + {file = "mypy-1.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:51f869f4b6b538229c1d1bcc1dd7d119817206e2bc54e8e374b3dfa202defcca"}, + {file = "mypy-1.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5c7051a3461ae84dfb5dd15eff5094640c61c5f22257c8b766794e6dd85e72d5"}, + {file = "mypy-1.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:39bb21c69a5d6342f4ce526e4584bc5c197fd20a60d14a8624d8743fffb9472e"}, + {file = "mypy-1.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:164f28cb9d6367439031f4c81e84d3ccaa1e19232d9d05d37cb0bd880d3f93c2"}, + {file = "mypy-1.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a4c1bfcdbce96ff5d96fc9b08e3831acb30dc44ab02671eca5953eadad07d6d0"}, + {file = "mypy-1.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:a0affb3a79a256b4183ba09811e3577c5163ed06685e4d4b46429a271ba174d2"}, + {file = "mypy-1.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a7b44178c9760ce1a43f544e595d35ed61ac2c3de306599fa59b38a6048e1aa7"}, + {file = "mypy-1.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5d5092efb8516d08440e36626f0153b5006d4088c1d663d88bf79625af3d1d62"}, + {file = "mypy-1.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2904956dac40ced10931ac967ae63c5089bd498542194b436eb097a9f77bc8"}, + {file = "mypy-1.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:7bfd8836970d33c2105562650656b6846149374dc8ed77d98424b40b09340ba7"}, + {file = "mypy-1.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:9f73dba9ec77acb86457a8fc04b5239822df0c14a082564737833d2963677dbc"}, + {file = "mypy-1.13.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:100fac22ce82925f676a734af0db922ecfea991e1d7ec0ceb1e115ebe501301a"}, + {file = "mypy-1.13.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7bcb0bb7f42a978bb323a7c88f1081d1b5dee77ca86f4100735a6f541299d8fb"}, + {file = "mypy-1.13.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bde31fc887c213e223bbfc34328070996061b0833b0a4cfec53745ed61f3519b"}, + {file = "mypy-1.13.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:07de989f89786f62b937851295ed62e51774722e5444a27cecca993fc3f9cd74"}, + {file = "mypy-1.13.0-cp38-cp38-win_amd64.whl", hash = "sha256:4bde84334fbe19bad704b3f5b78c4abd35ff1026f8ba72b29de70dda0916beb6"}, + {file = "mypy-1.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0246bcb1b5de7f08f2826451abd947bf656945209b140d16ed317f65a17dc7dc"}, + {file = "mypy-1.13.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7f5b7deae912cf8b77e990b9280f170381fdfbddf61b4ef80927edd813163732"}, + {file = "mypy-1.13.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7029881ec6ffb8bc233a4fa364736789582c738217b133f1b55967115288a2bc"}, + {file = "mypy-1.13.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3e38b980e5681f28f033f3be86b099a247b13c491f14bb8b1e1e134d23bb599d"}, + {file = "mypy-1.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:a6789be98a2017c912ae6ccb77ea553bbaf13d27605d2ca20a76dfbced631b24"}, + {file = "mypy-1.13.0-py3-none-any.whl", hash = "sha256:9c250883f9fd81d212e0952c92dbfcc96fc237f4b7c92f56ac81fd48460b3e5a"}, + {file = "mypy-1.13.0.tar.gz", hash = "sha256:0291a61b6fbf3e6673e3405cfcc0e7650bebc7939659fdca2702958038bd835e"}, +] + +[package.dependencies] +mypy-extensions = ">=1.0.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = ">=4.6.0" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +faster-cache = ["orjson"] +install-types = ["pip"] +mypyc = ["setuptools (>=50)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.8" +files = [ + {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, + {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, +] + +[[package]] +name = "packaging" +version = "25.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, + {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, +] + +[[package]] +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pydantic" +version = "2.10.6" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584"}, + {file = "pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236"}, +] + +[package.dependencies] +annotated-types = ">=0.6.0" +pydantic-core = "2.27.2" +typing-extensions = ">=4.12.2" + +[package.extras] +email = ["email-validator (>=2.0.0)"] +timezone = ["tzdata"] + +[[package]] +name = "pydantic-core" +version = "2.27.2" +description = "Core functionality for Pydantic validation and serialization" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa"}, + {file = "pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7969e133a6f183be60e9f6f56bfae753585680f3b7307a8e555a948d443cc05a"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3de9961f2a346257caf0aa508a4da705467f53778e9ef6fe744c038119737ef5"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2bb4d3e5873c37bb3dd58714d4cd0b0e6238cebc4177ac8fe878f8b3aa8e74c"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:280d219beebb0752699480fe8f1dc61ab6615c2046d76b7ab7ee38858de0a4e7"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47956ae78b6422cbd46f772f1746799cbb862de838fd8d1fbd34a82e05b0983a"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:14d4a5c49d2f009d62a2a7140d3064f686d17a5d1a268bc641954ba181880236"}, + {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:337b443af21d488716f8d0b6164de833e788aa6bd7e3a39c005febc1284f4962"}, + {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:03d0f86ea3184a12f41a2d23f7ccb79cdb5a18e06993f8a45baa8dfec746f0e9"}, + {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7041c36f5680c6e0f08d922aed302e98b3745d97fe1589db0a3eebf6624523af"}, + {file = "pydantic_core-2.27.2-cp310-cp310-win32.whl", hash = "sha256:50a68f3e3819077be2c98110c1f9dcb3817e93f267ba80a2c05bb4f8799e2ff4"}, + {file = "pydantic_core-2.27.2-cp310-cp310-win_amd64.whl", hash = "sha256:e0fd26b16394ead34a424eecf8a31a1f5137094cabe84a1bcb10fa6ba39d3d31"}, + {file = "pydantic_core-2.27.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8e10c99ef58cfdf2a66fc15d66b16c4a04f62bca39db589ae8cba08bc55331bc"}, + {file = "pydantic_core-2.27.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:26f32e0adf166a84d0cb63be85c562ca8a6fa8de28e5f0d92250c6b7e9e2aff7"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c19d1ea0673cd13cc2f872f6c9ab42acc4e4f492a7ca9d3795ce2b112dd7e15"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e68c4446fe0810e959cdff46ab0a41ce2f2c86d227d96dc3847af0ba7def306"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9640b0059ff4f14d1f37321b94061c6db164fbe49b334b31643e0528d100d99"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40d02e7d45c9f8af700f3452f329ead92da4c5f4317ca9b896de7ce7199ea459"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d81d2068e1c1228a565af076598f9e7451712700b673de8f502f0334f281387d"}, + {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1a4207639fb02ec2dbb76227d7c751a20b1a6b4bc52850568e52260cae64ca3b"}, + {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:3de3ce3c9ddc8bbd88f6e0e304dea0e66d843ec9de1b0042b0911c1663ffd474"}, + {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:30c5f68ded0c36466acede341551106821043e9afaad516adfb6e8fa80a4e6a6"}, + {file = "pydantic_core-2.27.2-cp311-cp311-win32.whl", hash = "sha256:c70c26d2c99f78b125a3459f8afe1aed4d9687c24fd677c6a4436bc042e50d6c"}, + {file = "pydantic_core-2.27.2-cp311-cp311-win_amd64.whl", hash = "sha256:08e125dbdc505fa69ca7d9c499639ab6407cfa909214d500897d02afb816e7cc"}, + {file = "pydantic_core-2.27.2-cp311-cp311-win_arm64.whl", hash = "sha256:26f0d68d4b235a2bae0c3fc585c585b4ecc51382db0e3ba402a22cbc440915e4"}, + {file = "pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0"}, + {file = "pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4"}, + {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3"}, + {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4"}, + {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57"}, + {file = "pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc"}, + {file = "pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9"}, + {file = "pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b"}, + {file = "pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b"}, + {file = "pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4"}, + {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27"}, + {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee"}, + {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1"}, + {file = "pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130"}, + {file = "pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee"}, + {file = "pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b"}, + {file = "pydantic_core-2.27.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d3e8d504bdd3f10835468f29008d72fc8359d95c9c415ce6e767203db6127506"}, + {file = "pydantic_core-2.27.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:521eb9b7f036c9b6187f0b47318ab0d7ca14bd87f776240b90b21c1f4f149320"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85210c4d99a0114f5a9481b44560d7d1e35e32cc5634c656bc48e590b669b145"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d716e2e30c6f140d7560ef1538953a5cd1a87264c737643d481f2779fc247fe1"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f66d89ba397d92f840f8654756196d93804278457b5fbede59598a1f9f90b228"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:669e193c1c576a58f132e3158f9dfa9662969edb1a250c54d8fa52590045f046"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdbe7629b996647b99c01b37f11170a57ae675375b14b8c13b8518b8320ced5"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d262606bf386a5ba0b0af3b97f37c83d7011439e3dc1a9298f21efb292e42f1a"}, + {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cabb9bcb7e0d97f74df8646f34fc76fbf793b7f6dc2438517d7a9e50eee4f14d"}, + {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:d2d63f1215638d28221f664596b1ccb3944f6e25dd18cd3b86b0a4c408d5ebb9"}, + {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bca101c00bff0adb45a833f8451b9105d9df18accb8743b08107d7ada14bd7da"}, + {file = "pydantic_core-2.27.2-cp38-cp38-win32.whl", hash = "sha256:f6f8e111843bbb0dee4cb6594cdc73e79b3329b526037ec242a3e49012495b3b"}, + {file = "pydantic_core-2.27.2-cp38-cp38-win_amd64.whl", hash = "sha256:fd1aea04935a508f62e0d0ef1f5ae968774a32afc306fb8545e06f5ff5cdf3ad"}, + {file = "pydantic_core-2.27.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c10eb4f1659290b523af58fa7cffb452a61ad6ae5613404519aee4bfbf1df993"}, + {file = "pydantic_core-2.27.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ef592d4bad47296fb11f96cd7dc898b92e795032b4894dfb4076cfccd43a9308"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61709a844acc6bf0b7dce7daae75195a10aac96a596ea1b776996414791ede4"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c5f762659e47fdb7b16956c71598292f60a03aa92f8b6351504359dbdba6cf"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c9775e339e42e79ec99c441d9730fccf07414af63eac2f0e48e08fd38a64d76"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57762139821c31847cfb2df63c12f725788bd9f04bc2fb392790959b8f70f118"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d1e85068e818c73e048fe28cfc769040bb1f475524f4745a5dc621f75ac7630"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:097830ed52fd9e427942ff3b9bc17fab52913b2f50f2880dc4a5611446606a54"}, + {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:044a50963a614ecfae59bb1eaf7ea7efc4bc62f49ed594e18fa1e5d953c40e9f"}, + {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:4e0b4220ba5b40d727c7f879eac379b822eee5d8fff418e9d3381ee45b3b0362"}, + {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e4f4bb20d75e9325cc9696c6802657b58bc1dbbe3022f32cc2b2b632c3fbb96"}, + {file = "pydantic_core-2.27.2-cp39-cp39-win32.whl", hash = "sha256:cca63613e90d001b9f2f9a9ceb276c308bfa2a43fafb75c8031c4f66039e8c6e"}, + {file = "pydantic_core-2.27.2-cp39-cp39-win_amd64.whl", hash = "sha256:77d1bca19b0f7021b3a982e6f903dcd5b2b06076def36a652e3907f596e29f67"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2bf14caea37e91198329b828eae1618c068dfb8ef17bb33287a7ad4b61ac314e"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0cb791f5b45307caae8810c2023a184c74605ec3bcbb67d13846c28ff731ff8"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:688d3fd9fcb71f41c4c015c023d12a79d1c4c0732ec9eb35d96e3388a120dcf3"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d591580c34f4d731592f0e9fe40f9cc1b430d297eecc70b962e93c5c668f15f"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:82f986faf4e644ffc189a7f1aafc86e46ef70372bb153e7001e8afccc6e54133"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:bec317a27290e2537f922639cafd54990551725fc844249e64c523301d0822fc"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:0296abcb83a797db256b773f45773da397da75a08f5fcaef41f2044adec05f50"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0d75070718e369e452075a6017fbf187f788e17ed67a3abd47fa934d001863d9"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7e17b560be3c98a8e3aa66ce828bdebb9e9ac6ad5466fba92eb74c4c95cb1151"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c33939a82924da9ed65dab5a65d427205a73181d8098e79b6b426bdf8ad4e656"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:00bad2484fa6bda1e216e7345a798bd37c68fb2d97558edd584942aa41b7d278"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c817e2b40aba42bac6f457498dacabc568c3b7a986fc9ba7c8d9d260b71485fb"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:251136cdad0cb722e93732cb45ca5299fb56e1344a833640bf93b2803f8d1bfd"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2088237af596f0a524d3afc39ab3b036e8adb054ee57cbb1dcf8e09da5b29cc"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d4041c0b966a84b4ae7a09832eb691a35aec90910cd2dbe7a208de59be77965b"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:8083d4e875ebe0b864ffef72a4304827015cff328a1be6e22cc850753bfb122b"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f141ee28a0ad2123b6611b6ceff018039df17f32ada8b534e6aa039545a3efb2"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7d0c8399fcc1848491f00e0314bd59fb34a9c008761bcb422a057670c3f65e35"}, + {file = "pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" + +[[package]] +name = "pytest" +version = "7.4.4" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, + {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-asyncio" +version = "0.23.8" +description = "Pytest support for asyncio" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest_asyncio-0.23.8-py3-none-any.whl", hash = "sha256:50265d892689a5faefb84df80819d1ecef566eb3549cf915dfb33569359d1ce2"}, + {file = "pytest_asyncio-0.23.8.tar.gz", hash = "sha256:759b10b33a6dc61cce40a8bd5205e302978bbbcc00e279a8b61d9a6a3c82e4d3"}, +] + +[package.dependencies] +pytest = ">=7.0.0,<9" + +[package.extras] +docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] +testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] + +[[package]] +name = "pytest-xdist" +version = "3.6.1" +description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7"}, + {file = "pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d"}, +] + +[package.dependencies] +execnet = ">=2.1" +pytest = ">=7.0.0" + +[package.extras] +psutil = ["psutil (>=3.0)"] +setproctitle = ["setproctitle"] +testing = ["filelock"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "ruff" +version = "0.11.5" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.11.5-py3-none-linux_armv6l.whl", hash = "sha256:2561294e108eb648e50f210671cc56aee590fb6167b594144401532138c66c7b"}, + {file = "ruff-0.11.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ac12884b9e005c12d0bd121f56ccf8033e1614f736f766c118ad60780882a077"}, + {file = "ruff-0.11.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:4bfd80a6ec559a5eeb96c33f832418bf0fb96752de0539905cf7b0cc1d31d779"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0947c0a1afa75dcb5db4b34b070ec2bccee869d40e6cc8ab25aca11a7d527794"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ad871ff74b5ec9caa66cb725b85d4ef89b53f8170f47c3406e32ef040400b038"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e6cf918390cfe46d240732d4d72fa6e18e528ca1f60e318a10835cf2fa3dc19f"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:56145ee1478582f61c08f21076dc59153310d606ad663acc00ea3ab5b2125f82"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e5f66f8f1e8c9fc594cbd66fbc5f246a8d91f916cb9667e80208663ec3728304"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80b4df4d335a80315ab9afc81ed1cff62be112bd165e162b5eed8ac55bfc8470"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3068befab73620b8a0cc2431bd46b3cd619bc17d6f7695a3e1bb166b652c382a"}, + {file = "ruff-0.11.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f5da2e710a9641828e09aa98b92c9ebbc60518fdf3921241326ca3e8f8e55b8b"}, + {file = "ruff-0.11.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ef39f19cb8ec98cbc762344921e216f3857a06c47412030374fffd413fb8fd3a"}, + {file = "ruff-0.11.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:b2a7cedf47244f431fd11aa5a7e2806dda2e0c365873bda7834e8f7d785ae159"}, + {file = "ruff-0.11.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:81be52e7519f3d1a0beadcf8e974715b2dfc808ae8ec729ecfc79bddf8dbb783"}, + {file = "ruff-0.11.5-py3-none-win32.whl", hash = "sha256:e268da7b40f56e3eca571508a7e567e794f9bfcc0f412c4b607931d3af9c4afe"}, + {file = "ruff-0.11.5-py3-none-win_amd64.whl", hash = "sha256:6c6dc38af3cfe2863213ea25b6dc616d679205732dc0fb673356c2d69608f800"}, + {file = "ruff-0.11.5-py3-none-win_arm64.whl", hash = "sha256:67e241b4314f4eacf14a601d586026a962f4002a475aa702c69980a38087aa4e"}, + {file = "ruff-0.11.5.tar.gz", hash = "sha256:cae2e2439cb88853e421901ec040a758960b576126dab520fa08e9de431d1bef"}, +] + +[[package]] +name = "six" +version = "1.17.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, +] + +[[package]] +name = "tomli" +version = "2.3.0" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45"}, + {file = "tomli-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:883b1c0d6398a6a9d29b508c331fa56adbcdff647f6ace4dfca0f50e90dfd0ba"}, + {file = "tomli-2.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1381caf13ab9f300e30dd8feadb3de072aeb86f1d34a8569453ff32a7dea4bf"}, + {file = "tomli-2.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0e285d2649b78c0d9027570d4da3425bdb49830a6156121360b3f8511ea3441"}, + {file = "tomli-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a154a9ae14bfcf5d8917a59b51ffd5a3ac1fd149b71b47a3a104ca4edcfa845"}, + {file = "tomli-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:74bf8464ff93e413514fefd2be591c3b0b23231a77f901db1eb30d6f712fc42c"}, + {file = "tomli-2.3.0-cp311-cp311-win32.whl", hash = "sha256:00b5f5d95bbfc7d12f91ad8c593a1659b6387b43f054104cda404be6bda62456"}, + {file = "tomli-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:4dc4ce8483a5d429ab602f111a93a6ab1ed425eae3122032db7e9acf449451be"}, + {file = "tomli-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d7d86942e56ded512a594786a5ba0a5e521d02529b3826e7761a05138341a2ac"}, + {file = "tomli-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:73ee0b47d4dad1c5e996e3cd33b8a76a50167ae5f96a2607cbe8cc773506ab22"}, + {file = "tomli-2.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:792262b94d5d0a466afb5bc63c7daa9d75520110971ee269152083270998316f"}, + {file = "tomli-2.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f195fe57ecceac95a66a75ac24d9d5fbc98ef0962e09b2eddec5d39375aae52"}, + {file = "tomli-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e31d432427dcbf4d86958c184b9bfd1e96b5b71f8eb17e6d02531f434fd335b8"}, + {file = "tomli-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b0882799624980785240ab732537fcfc372601015c00f7fc367c55308c186f6"}, + {file = "tomli-2.3.0-cp312-cp312-win32.whl", hash = "sha256:ff72b71b5d10d22ecb084d345fc26f42b5143c5533db5e2eaba7d2d335358876"}, + {file = "tomli-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:1cb4ed918939151a03f33d4242ccd0aa5f11b3547d0cf30f7c74a408a5b99878"}, + {file = "tomli-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5192f562738228945d7b13d4930baffda67b69425a7f0da96d360b0a3888136b"}, + {file = "tomli-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:be71c93a63d738597996be9528f4abe628d1adf5e6eb11607bc8fe1a510b5dae"}, + {file = "tomli-2.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4665508bcbac83a31ff8ab08f424b665200c0e1e645d2bd9ab3d3e557b6185b"}, + {file = "tomli-2.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4021923f97266babc6ccab9f5068642a0095faa0a51a246a6a02fccbb3514eaf"}, + {file = "tomli-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4ea38c40145a357d513bffad0ed869f13c1773716cf71ccaa83b0fa0cc4e42f"}, + {file = "tomli-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad805ea85eda330dbad64c7ea7a4556259665bdf9d2672f5dccc740eb9d3ca05"}, + {file = "tomli-2.3.0-cp313-cp313-win32.whl", hash = "sha256:97d5eec30149fd3294270e889b4234023f2c69747e555a27bd708828353ab606"}, + {file = "tomli-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0c95ca56fbe89e065c6ead5b593ee64b84a26fca063b5d71a1122bf26e533999"}, + {file = "tomli-2.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cebc6fe843e0733ee827a282aca4999b596241195f43b4cc371d64fc6639da9e"}, + {file = "tomli-2.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4c2ef0244c75aba9355561272009d934953817c49f47d768070c3c94355c2aa3"}, + {file = "tomli-2.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c22a8bf253bacc0cf11f35ad9808b6cb75ada2631c2d97c971122583b129afbc"}, + {file = "tomli-2.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0eea8cc5c5e9f89c9b90c4896a8deefc74f518db5927d0e0e8d4a80953d774d0"}, + {file = "tomli-2.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b74a0e59ec5d15127acdabd75ea17726ac4c5178ae51b85bfe39c4f8a278e879"}, + {file = "tomli-2.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b5870b50c9db823c595983571d1296a6ff3e1b88f734a4c8f6fc6188397de005"}, + {file = "tomli-2.3.0-cp314-cp314-win32.whl", hash = "sha256:feb0dacc61170ed7ab602d3d972a58f14ee3ee60494292d384649a3dc38ef463"}, + {file = "tomli-2.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:b273fcbd7fc64dc3600c098e39136522650c49bca95df2d11cf3b626422392c8"}, + {file = "tomli-2.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:940d56ee0410fa17ee1f12b817b37a4d4e4dc4d27340863cc67236c74f582e77"}, + {file = "tomli-2.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f85209946d1fe94416debbb88d00eb92ce9cd5266775424ff81bc959e001acaf"}, + {file = "tomli-2.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a56212bdcce682e56b0aaf79e869ba5d15a6163f88d5451cbde388d48b13f530"}, + {file = "tomli-2.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5f3ffd1e098dfc032d4d3af5c0ac64f6d286d98bc148698356847b80fa4de1b"}, + {file = "tomli-2.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e01decd096b1530d97d5d85cb4dff4af2d8347bd35686654a004f8dea20fc67"}, + {file = "tomli-2.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8a35dd0e643bb2610f156cca8db95d213a90015c11fee76c946aa62b7ae7e02f"}, + {file = "tomli-2.3.0-cp314-cp314t-win32.whl", hash = "sha256:a1f7f282fe248311650081faafa5f4732bdbfef5d45fe3f2e702fbc6f2d496e0"}, + {file = "tomli-2.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:70a251f8d4ba2d9ac2542eecf008b3c8a9fc5c3f9f02c56a9d7952612be2fdba"}, + {file = "tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b"}, + {file = "tomli-2.3.0.tar.gz", hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549"}, +] + +[[package]] +name = "types-python-dateutil" +version = "2.9.0.20241206" +description = "Typing stubs for python-dateutil" +optional = false +python-versions = ">=3.8" +files = [ + {file = "types_python_dateutil-2.9.0.20241206-py3-none-any.whl", hash = "sha256:e248a4bc70a486d3e3ec84d0dc30eec3a5f979d6e7ee4123ae043eedbb987f53"}, + {file = "types_python_dateutil-2.9.0.20241206.tar.gz", hash = "sha256:18f493414c26ffba692a72369fea7a154c502646301ebfe3d56a04b3767284cb"}, +] + +[[package]] +name = "typing-extensions" +version = "4.13.2" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"}, + {file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"}, +] + +[metadata] +lock-version = "2.0" +python-versions = "^3.8" +content-hash = "cba1461c95c16fa07f656f27197490f8c77343a38b5aee6762b2b596e5d2996c" diff --git a/seed/pydantic/inferred-auth-implicit-api-key/pyproject.toml b/seed/pydantic/inferred-auth-implicit-api-key/pyproject.toml new file mode 100644 index 000000000000..067db85f05db --- /dev/null +++ b/seed/pydantic/inferred-auth-implicit-api-key/pyproject.toml @@ -0,0 +1,89 @@ +[project] +name = "fern_inferred-auth-implicit-api-key" +dynamic = ["version"] + +[tool.poetry] +name = "fern_inferred-auth-implicit-api-key" +version = "0.0.1" +description = "" +readme = "README.md" +authors = [] +keywords = [ + "fern", + "test" +] + +classifiers = [ + "Intended Audience :: Developers", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Operating System :: OS Independent", + "Operating System :: POSIX", + "Operating System :: MacOS", + "Operating System :: POSIX :: Linux", + "Operating System :: Microsoft :: Windows", + "Topic :: Software Development :: Libraries :: Python Modules", + "Typing :: Typed" +] +packages = [ + { include = "seed/inferred_auth_implicit_api_key", from = "src"} +] + +[tool.poetry.urls] +Documentation = 'https://buildwithfern.com/learn' +Homepage = 'https://buildwithfern.com/' +Repository = 'https://github.com/inferred-auth-implicit-api-key/fern' + +[tool.poetry.dependencies] +python = "^3.8" +pydantic = ">= 1.9.2" +pydantic-core = ">=2.18.2" + +[tool.poetry.group.dev.dependencies] +mypy = "==1.13.0" +pytest = "^7.4.0" +pytest-asyncio = "^0.23.5" +pytest-xdist = "^3.6.1" +python-dateutil = "^2.9.0" +types-python-dateutil = "^2.9.0.20240316" +ruff = "==0.11.5" + +[tool.pytest.ini_options] +testpaths = [ "tests" ] +asyncio_mode = "auto" + +[tool.mypy] +plugins = ["pydantic.mypy"] + +[tool.ruff] +line-length = 120 + +[tool.ruff.lint] +select = [ + "E", # pycodestyle errors + "F", # pyflakes + "I", # isort +] +ignore = [ + "E402", # Module level import not at top of file + "E501", # Line too long + "E711", # Comparison to `None` should be `cond is not None` + "E712", # Avoid equality comparisons to `True`; use `if ...:` checks + "E721", # Use `is` and `is not` for type comparisons, or `isinstance()` for insinstance checks + "E722", # Do not use bare `except` + "E731", # Do not assign a `lambda` expression, use a `def` + "F821", # Undefined name + "F841" # Local variable ... is assigned to but never used +] + +[tool.ruff.lint.isort] +section-order = ["future", "standard-library", "third-party", "first-party"] + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/seed/pydantic/inferred-auth-implicit-api-key/requirements.txt b/seed/pydantic/inferred-auth-implicit-api-key/requirements.txt new file mode 100644 index 000000000000..c8f40119ae10 --- /dev/null +++ b/seed/pydantic/inferred-auth-implicit-api-key/requirements.txt @@ -0,0 +1,2 @@ +pydantic>= 1.9.2 +pydantic-core>=2.18.2 diff --git a/seed/pydantic/inferred-auth-implicit-api-key/snippet.json b/seed/pydantic/inferred-auth-implicit-api-key/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/pydantic/inferred-auth-implicit-api-key/src/seed/inferred_auth_implicit_api_key/__init__.py b/seed/pydantic/inferred-auth-implicit-api-key/src/seed/inferred_auth_implicit_api_key/__init__.py new file mode 100644 index 000000000000..f74a0627d8d3 --- /dev/null +++ b/seed/pydantic/inferred-auth-implicit-api-key/src/seed/inferred_auth_implicit_api_key/__init__.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +from .resources import TokenResponse, auth + +__all__ = ["TokenResponse", "auth"] diff --git a/seed/pydantic/inferred-auth-implicit-api-key/src/seed/inferred_auth_implicit_api_key/core/__init__.py b/seed/pydantic/inferred-auth-implicit-api-key/src/seed/inferred_auth_implicit_api_key/core/__init__.py new file mode 100644 index 000000000000..68bb7354679d --- /dev/null +++ b/seed/pydantic/inferred-auth-implicit-api-key/src/seed/inferred_auth_implicit_api_key/core/__init__.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +from .datetime_utils import serialize_datetime +from .pydantic_utilities import ( + IS_PYDANTIC_V2, + UniversalBaseModel, + UniversalRootModel, + parse_obj_as, + universal_field_validator, + universal_root_validator, + update_forward_refs, +) +from .serialization import FieldMetadata + +__all__ = [ + "FieldMetadata", + "IS_PYDANTIC_V2", + "UniversalBaseModel", + "UniversalRootModel", + "parse_obj_as", + "serialize_datetime", + "universal_field_validator", + "universal_root_validator", + "update_forward_refs", +] diff --git a/seed/pydantic/inferred-auth-implicit-api-key/src/seed/inferred_auth_implicit_api_key/core/datetime_utils.py b/seed/pydantic/inferred-auth-implicit-api-key/src/seed/inferred_auth_implicit_api_key/core/datetime_utils.py new file mode 100644 index 000000000000..7c9864a944c2 --- /dev/null +++ b/seed/pydantic/inferred-auth-implicit-api-key/src/seed/inferred_auth_implicit_api_key/core/datetime_utils.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt + + +def serialize_datetime(v: dt.datetime) -> str: + """ + Serialize a datetime including timezone info. + + Uses the timezone info provided if present, otherwise uses the current runtime's timezone info. + + UTC datetimes end in "Z" while all other timezones are represented as offset from UTC, e.g. +05:00. + """ + + def _serialize_zoned_datetime(v: dt.datetime) -> str: + if v.tzinfo is not None and v.tzinfo.tzname(None) == dt.timezone.utc.tzname(None): + # UTC is a special case where we use "Z" at the end instead of "+00:00" + return v.isoformat().replace("+00:00", "Z") + else: + # Delegate to the typical +/- offset format + return v.isoformat() + + if v.tzinfo is not None: + return _serialize_zoned_datetime(v) + else: + local_tz = dt.datetime.now().astimezone().tzinfo + localized_dt = v.replace(tzinfo=local_tz) + return _serialize_zoned_datetime(localized_dt) diff --git a/seed/pydantic/inferred-auth-implicit-api-key/src/seed/inferred_auth_implicit_api_key/core/enum.py b/seed/pydantic/inferred-auth-implicit-api-key/src/seed/inferred_auth_implicit_api_key/core/enum.py new file mode 100644 index 000000000000..a3d17a67b128 --- /dev/null +++ b/seed/pydantic/inferred-auth-implicit-api-key/src/seed/inferred_auth_implicit_api_key/core/enum.py @@ -0,0 +1,20 @@ +# This file was auto-generated by Fern from our API Definition. + +""" +Provides a StrEnum base class that works across Python versions. + +For Python >= 3.11, this re-exports the standard library enum.StrEnum. +For older Python versions, this defines a compatible StrEnum using the +(str, Enum) mixin pattern so that generated SDKs can use a single base +class in all supported Python versions. +""" + +import enum +import sys + +if sys.version_info >= (3, 11): + from enum import StrEnum +else: + + class StrEnum(str, enum.Enum): + pass diff --git a/seed/pydantic/inferred-auth-implicit-api-key/src/seed/inferred_auth_implicit_api_key/core/pydantic_utilities.py b/seed/pydantic/inferred-auth-implicit-api-key/src/seed/inferred_auth_implicit_api_key/core/pydantic_utilities.py new file mode 100644 index 000000000000..1da6f10acdd1 --- /dev/null +++ b/seed/pydantic/inferred-auth-implicit-api-key/src/seed/inferred_auth_implicit_api_key/core/pydantic_utilities.py @@ -0,0 +1,230 @@ +# This file was auto-generated by Fern from our API Definition. + +# nopycln: file +import datetime as dt +from collections import defaultdict +from typing import Any, Callable, Dict, List, Mapping, Tuple, Type, TypeVar, Union, cast + +import pydantic + +IS_PYDANTIC_V2 = pydantic.VERSION.startswith("2.") + +if IS_PYDANTIC_V2: + from pydantic.v1.datetime_parse import parse_date as parse_date + from pydantic.v1.datetime_parse import parse_datetime as parse_datetime + from pydantic.v1.fields import ModelField as ModelField + from pydantic.v1.json import ENCODERS_BY_TYPE as encoders_by_type # type: ignore[attr-defined] + from pydantic.v1.typing import get_args as get_args + from pydantic.v1.typing import get_origin as get_origin + from pydantic.v1.typing import is_literal_type as is_literal_type + from pydantic.v1.typing import is_union as is_union +else: + from pydantic.datetime_parse import parse_date as parse_date # type: ignore[no-redef] + from pydantic.datetime_parse import parse_datetime as parse_datetime # type: ignore[no-redef] + from pydantic.fields import ModelField as ModelField # type: ignore[attr-defined, no-redef] + from pydantic.json import ENCODERS_BY_TYPE as encoders_by_type # type: ignore[no-redef] + from pydantic.typing import get_args as get_args # type: ignore[no-redef] + from pydantic.typing import get_origin as get_origin # type: ignore[no-redef] + from pydantic.typing import is_literal_type as is_literal_type # type: ignore[no-redef] + from pydantic.typing import is_union as is_union # type: ignore[no-redef] + +from .datetime_utils import serialize_datetime +from typing_extensions import TypeAlias + +T = TypeVar("T") +Model = TypeVar("Model", bound=pydantic.BaseModel) + + +def parse_obj_as(type_: Type[T], object_: Any) -> T: + if IS_PYDANTIC_V2: + adapter = pydantic.TypeAdapter(type_) # type: ignore[attr-defined] + return adapter.validate_python(object_) + return pydantic.parse_obj_as(type_, object_) + + +def to_jsonable_with_fallback(obj: Any, fallback_serializer: Callable[[Any], Any]) -> Any: + if IS_PYDANTIC_V2: + from pydantic_core import to_jsonable_python + + return to_jsonable_python(obj, fallback=fallback_serializer) + return fallback_serializer(obj) + + +class UniversalBaseModel(pydantic.BaseModel): + class Config: + populate_by_name = True + smart_union = True + allow_population_by_field_name = True + json_encoders = {dt.datetime: serialize_datetime} + # Allow fields beginning with `model_` to be used in the model + protected_namespaces = () + + def json(self, **kwargs: Any) -> str: + kwargs_with_defaults = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + if IS_PYDANTIC_V2: + return super().model_dump_json(**kwargs_with_defaults) # type: ignore[misc] + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: Any) -> Dict[str, Any]: + """ + Override the default dict method to `exclude_unset` by default. This function patches + `exclude_unset` to work include fields within non-None default values. + """ + # Note: the logic here is multiplexed given the levers exposed in Pydantic V1 vs V2 + # Pydantic V1's .dict can be extremely slow, so we do not want to call it twice. + # + # We'd ideally do the same for Pydantic V2, but it shells out to a library to serialize models + # that we have less control over, and this is less intrusive than custom serializers for now. + if IS_PYDANTIC_V2: + kwargs_with_defaults_exclude_unset = { + **kwargs, + "by_alias": True, + "exclude_unset": True, + "exclude_none": False, + } + kwargs_with_defaults_exclude_none = { + **kwargs, + "by_alias": True, + "exclude_none": True, + "exclude_unset": False, + } + return deep_union_pydantic_dicts( + super().model_dump(**kwargs_with_defaults_exclude_unset), # type: ignore[misc] + super().model_dump(**kwargs_with_defaults_exclude_none), # type: ignore[misc] + ) + + _fields_set = self.__fields_set__.copy() + + fields = _get_model_fields(self.__class__) + for name, field in fields.items(): + if name not in _fields_set: + default = _get_field_default(field) + + # If the default values are non-null act like they've been set + # This effectively allows exclude_unset to work like exclude_none where + # the latter passes through intentionally set none values. + if default is not None or ("exclude_unset" in kwargs and not kwargs["exclude_unset"]): + _fields_set.add(name) + + if default is not None: + self.__fields_set__.add(name) + + kwargs_with_defaults_exclude_unset_include_fields = { + "by_alias": True, + "exclude_unset": True, + "include": _fields_set, + **kwargs, + } + + return super().dict(**kwargs_with_defaults_exclude_unset_include_fields) + + +def _union_list_of_pydantic_dicts(source: List[Any], destination: List[Any]) -> List[Any]: + converted_list: List[Any] = [] + for i, item in enumerate(source): + destination_value = destination[i] + if isinstance(item, dict): + converted_list.append(deep_union_pydantic_dicts(item, destination_value)) + elif isinstance(item, list): + converted_list.append(_union_list_of_pydantic_dicts(item, destination_value)) + else: + converted_list.append(item) + return converted_list + + +def deep_union_pydantic_dicts(source: Dict[str, Any], destination: Dict[str, Any]) -> Dict[str, Any]: + for key, value in source.items(): + node = destination.setdefault(key, {}) + if isinstance(value, dict): + deep_union_pydantic_dicts(value, node) + # Note: we do not do this same processing for sets given we do not have sets of models + # and given the sets are unordered, the processing of the set and matching objects would + # be non-trivial. + elif isinstance(value, list): + destination[key] = _union_list_of_pydantic_dicts(value, node) + else: + destination[key] = value + + return destination + + +if IS_PYDANTIC_V2: + + class V2RootModel(UniversalBaseModel, pydantic.RootModel): # type: ignore[name-defined, type-arg] + pass + + UniversalRootModel: TypeAlias = V2RootModel +else: + UniversalRootModel: TypeAlias = UniversalBaseModel # type: ignore[misc, no-redef] + + +def encode_by_type(o: Any) -> Any: + encoders_by_class_tuples: Dict[Callable[[Any], Any], Tuple[Any, ...]] = defaultdict(tuple) + for type_, encoder in encoders_by_type.items(): + encoders_by_class_tuples[encoder] += (type_,) + + if type(o) in encoders_by_type: + return encoders_by_type[type(o)](o) + for encoder, classes_tuple in encoders_by_class_tuples.items(): + if isinstance(o, classes_tuple): + return encoder(o) + + +def update_forward_refs(model: Type["Model"], **localns: Any) -> None: + if IS_PYDANTIC_V2: + model.model_rebuild(raise_errors=False) # type: ignore[attr-defined] + else: + model.update_forward_refs(**localns) + + +# Mirrors Pydantic's internal typing +AnyCallable = Callable[..., Any] + + +def universal_root_validator( + pre: bool = False, +) -> Callable[[AnyCallable], AnyCallable]: + def decorator(func: AnyCallable) -> AnyCallable: + if IS_PYDANTIC_V2: + # In Pydantic v2, for RootModel we always use "before" mode + # The custom validators transform the input value before the model is created + return cast(AnyCallable, pydantic.model_validator(mode="before")(func)) # type: ignore[attr-defined] + return cast(AnyCallable, pydantic.root_validator(pre=pre)(func)) # type: ignore[call-overload] + + return decorator + + +def universal_field_validator(field_name: str, pre: bool = False) -> Callable[[AnyCallable], AnyCallable]: + def decorator(func: AnyCallable) -> AnyCallable: + if IS_PYDANTIC_V2: + return cast(AnyCallable, pydantic.field_validator(field_name, mode="before" if pre else "after")(func)) # type: ignore[attr-defined] + return cast(AnyCallable, pydantic.validator(field_name, pre=pre)(func)) + + return decorator + + +PydanticField = Union[ModelField, pydantic.fields.FieldInfo] + + +def _get_model_fields(model: Type["Model"]) -> Mapping[str, PydanticField]: + if IS_PYDANTIC_V2: + return cast(Mapping[str, PydanticField], model.model_fields) # type: ignore[attr-defined] + return cast(Mapping[str, PydanticField], model.__fields__) + + +def _get_field_default(field: PydanticField) -> Any: + try: + value = field.get_default() # type: ignore[union-attr] + except: + value = field.default + if IS_PYDANTIC_V2: + from pydantic_core import PydanticUndefined + + if value == PydanticUndefined: + return None + return value + return value diff --git a/seed/pydantic/inferred-auth-implicit-api-key/src/seed/inferred_auth_implicit_api_key/core/serialization.py b/seed/pydantic/inferred-auth-implicit-api-key/src/seed/inferred_auth_implicit_api_key/core/serialization.py new file mode 100644 index 000000000000..c36e865cc729 --- /dev/null +++ b/seed/pydantic/inferred-auth-implicit-api-key/src/seed/inferred_auth_implicit_api_key/core/serialization.py @@ -0,0 +1,276 @@ +# This file was auto-generated by Fern from our API Definition. + +import collections +import inspect +import typing + +import pydantic +import typing_extensions + + +class FieldMetadata: + """ + Metadata class used to annotate fields to provide additional information. + + Example: + class MyDict(TypedDict): + field: typing.Annotated[str, FieldMetadata(alias="field_name")] + + Will serialize: `{"field": "value"}` + To: `{"field_name": "value"}` + """ + + alias: str + + def __init__(self, *, alias: str) -> None: + self.alias = alias + + +def convert_and_respect_annotation_metadata( + *, + object_: typing.Any, + annotation: typing.Any, + inner_type: typing.Optional[typing.Any] = None, + direction: typing.Literal["read", "write"], +) -> typing.Any: + """ + Respect the metadata annotations on a field, such as aliasing. This function effectively + manipulates the dict-form of an object to respect the metadata annotations. This is primarily used for + TypedDicts, which cannot support aliasing out of the box, and can be extended for additional + utilities, such as defaults. + + Parameters + ---------- + object_ : typing.Any + + annotation : type + The type we're looking to apply typing annotations from + + inner_type : typing.Optional[type] + + Returns + ------- + typing.Any + """ + + if object_ is None: + return None + if inner_type is None: + inner_type = annotation + + clean_type = _remove_annotations(inner_type) + # Pydantic models + if ( + inspect.isclass(clean_type) + and issubclass(clean_type, pydantic.BaseModel) + and isinstance(object_, typing.Mapping) + ): + return _convert_mapping(object_, clean_type, direction) + # TypedDicts + if typing_extensions.is_typeddict(clean_type) and isinstance(object_, typing.Mapping): + return _convert_mapping(object_, clean_type, direction) + + if ( + typing_extensions.get_origin(clean_type) == typing.Dict + or typing_extensions.get_origin(clean_type) == dict + or clean_type == typing.Dict + ) and isinstance(object_, typing.Dict): + key_type = typing_extensions.get_args(clean_type)[0] + value_type = typing_extensions.get_args(clean_type)[1] + + return { + key: convert_and_respect_annotation_metadata( + object_=value, + annotation=annotation, + inner_type=value_type, + direction=direction, + ) + for key, value in object_.items() + } + + # If you're iterating on a string, do not bother to coerce it to a sequence. + if not isinstance(object_, str): + if ( + typing_extensions.get_origin(clean_type) == typing.Set + or typing_extensions.get_origin(clean_type) == set + or clean_type == typing.Set + ) and isinstance(object_, typing.Set): + inner_type = typing_extensions.get_args(clean_type)[0] + return { + convert_and_respect_annotation_metadata( + object_=item, + annotation=annotation, + inner_type=inner_type, + direction=direction, + ) + for item in object_ + } + elif ( + ( + typing_extensions.get_origin(clean_type) == typing.List + or typing_extensions.get_origin(clean_type) == list + or clean_type == typing.List + ) + and isinstance(object_, typing.List) + ) or ( + ( + typing_extensions.get_origin(clean_type) == typing.Sequence + or typing_extensions.get_origin(clean_type) == collections.abc.Sequence + or clean_type == typing.Sequence + ) + and isinstance(object_, typing.Sequence) + ): + inner_type = typing_extensions.get_args(clean_type)[0] + return [ + convert_and_respect_annotation_metadata( + object_=item, + annotation=annotation, + inner_type=inner_type, + direction=direction, + ) + for item in object_ + ] + + if typing_extensions.get_origin(clean_type) == typing.Union: + # We should be able to ~relatively~ safely try to convert keys against all + # member types in the union, the edge case here is if one member aliases a field + # of the same name to a different name from another member + # Or if another member aliases a field of the same name that another member does not. + for member in typing_extensions.get_args(clean_type): + object_ = convert_and_respect_annotation_metadata( + object_=object_, + annotation=annotation, + inner_type=member, + direction=direction, + ) + return object_ + + annotated_type = _get_annotation(annotation) + if annotated_type is None: + return object_ + + # If the object is not a TypedDict, a Union, or other container (list, set, sequence, etc.) + # Then we can safely call it on the recursive conversion. + return object_ + + +def _convert_mapping( + object_: typing.Mapping[str, object], + expected_type: typing.Any, + direction: typing.Literal["read", "write"], +) -> typing.Mapping[str, object]: + converted_object: typing.Dict[str, object] = {} + try: + annotations = typing_extensions.get_type_hints(expected_type, include_extras=True) + except NameError: + # The TypedDict contains a circular reference, so + # we use the __annotations__ attribute directly. + annotations = getattr(expected_type, "__annotations__", {}) + aliases_to_field_names = _get_alias_to_field_name(annotations) + for key, value in object_.items(): + if direction == "read" and key in aliases_to_field_names: + dealiased_key = aliases_to_field_names.get(key) + if dealiased_key is not None: + type_ = annotations.get(dealiased_key) + else: + type_ = annotations.get(key) + # Note you can't get the annotation by the field name if you're in read mode, so you must check the aliases map + # + # So this is effectively saying if we're in write mode, and we don't have a type, or if we're in read mode and we don't have an alias + # then we can just pass the value through as is + if type_ is None: + converted_object[key] = value + elif direction == "read" and key not in aliases_to_field_names: + converted_object[key] = convert_and_respect_annotation_metadata( + object_=value, annotation=type_, direction=direction + ) + else: + converted_object[_alias_key(key, type_, direction, aliases_to_field_names)] = ( + convert_and_respect_annotation_metadata(object_=value, annotation=type_, direction=direction) + ) + return converted_object + + +def _get_annotation(type_: typing.Any) -> typing.Optional[typing.Any]: + maybe_annotated_type = typing_extensions.get_origin(type_) + if maybe_annotated_type is None: + return None + + if maybe_annotated_type == typing_extensions.NotRequired: + type_ = typing_extensions.get_args(type_)[0] + maybe_annotated_type = typing_extensions.get_origin(type_) + + if maybe_annotated_type == typing_extensions.Annotated: + return type_ + + return None + + +def _remove_annotations(type_: typing.Any) -> typing.Any: + maybe_annotated_type = typing_extensions.get_origin(type_) + if maybe_annotated_type is None: + return type_ + + if maybe_annotated_type == typing_extensions.NotRequired: + return _remove_annotations(typing_extensions.get_args(type_)[0]) + + if maybe_annotated_type == typing_extensions.Annotated: + return _remove_annotations(typing_extensions.get_args(type_)[0]) + + return type_ + + +def get_alias_to_field_mapping(type_: typing.Any) -> typing.Dict[str, str]: + annotations = typing_extensions.get_type_hints(type_, include_extras=True) + return _get_alias_to_field_name(annotations) + + +def get_field_to_alias_mapping(type_: typing.Any) -> typing.Dict[str, str]: + annotations = typing_extensions.get_type_hints(type_, include_extras=True) + return _get_field_to_alias_name(annotations) + + +def _get_alias_to_field_name( + field_to_hint: typing.Dict[str, typing.Any], +) -> typing.Dict[str, str]: + aliases = {} + for field, hint in field_to_hint.items(): + maybe_alias = _get_alias_from_type(hint) + if maybe_alias is not None: + aliases[maybe_alias] = field + return aliases + + +def _get_field_to_alias_name( + field_to_hint: typing.Dict[str, typing.Any], +) -> typing.Dict[str, str]: + aliases = {} + for field, hint in field_to_hint.items(): + maybe_alias = _get_alias_from_type(hint) + if maybe_alias is not None: + aliases[field] = maybe_alias + return aliases + + +def _get_alias_from_type(type_: typing.Any) -> typing.Optional[str]: + maybe_annotated_type = _get_annotation(type_) + + if maybe_annotated_type is not None: + # The actual annotations are 1 onward, the first is the annotated type + annotations = typing_extensions.get_args(maybe_annotated_type)[1:] + + for annotation in annotations: + if isinstance(annotation, FieldMetadata) and annotation.alias is not None: + return annotation.alias + return None + + +def _alias_key( + key: str, + type_: typing.Any, + direction: typing.Literal["read", "write"], + aliases_to_field_names: typing.Dict[str, str], +) -> str: + if direction == "read": + return aliases_to_field_names.get(key, key) + return _get_alias_from_type(type_=type_) or key diff --git a/seed/pydantic/inferred-auth-implicit-api-key/src/seed/inferred_auth_implicit_api_key/py.typed b/seed/pydantic/inferred-auth-implicit-api-key/src/seed/inferred_auth_implicit_api_key/py.typed new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/pydantic/inferred-auth-implicit-api-key/src/seed/inferred_auth_implicit_api_key/resources/__init__.py b/seed/pydantic/inferred-auth-implicit-api-key/src/seed/inferred_auth_implicit_api_key/resources/__init__.py new file mode 100644 index 000000000000..5c39a1ac0ddd --- /dev/null +++ b/seed/pydantic/inferred-auth-implicit-api-key/src/seed/inferred_auth_implicit_api_key/resources/__init__.py @@ -0,0 +1,8 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +from . import auth +from .auth import TokenResponse + +__all__ = ["TokenResponse", "auth"] diff --git a/seed/pydantic/inferred-auth-implicit-api-key/src/seed/inferred_auth_implicit_api_key/resources/auth/__init__.py b/seed/pydantic/inferred-auth-implicit-api-key/src/seed/inferred_auth_implicit_api_key/resources/auth/__init__.py new file mode 100644 index 000000000000..781e13319304 --- /dev/null +++ b/seed/pydantic/inferred-auth-implicit-api-key/src/seed/inferred_auth_implicit_api_key/resources/auth/__init__.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +from .token_response import TokenResponse + +__all__ = ["TokenResponse"] diff --git a/seed/pydantic/inferred-auth-implicit-api-key/src/seed/inferred_auth_implicit_api_key/resources/auth/token_response.py b/seed/pydantic/inferred-auth-implicit-api-key/src/seed/inferred_auth_implicit_api_key/resources/auth/token_response.py new file mode 100644 index 000000000000..ede31ab131ef --- /dev/null +++ b/seed/pydantic/inferred-auth-implicit-api-key/src/seed/inferred_auth_implicit_api_key/resources/auth/token_response.py @@ -0,0 +1,24 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class TokenResponse(UniversalBaseModel): + """ + An auth token response. + """ + + access_token: str + token_type: str + expires_in: int + scope: typing.Optional[str] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow") # type: ignore # Pydantic v2 + else: + + class Config: + extra = pydantic.Extra.allow diff --git a/seed/pydantic/inferred-auth-implicit-api-key/tests/custom/test_client.py b/seed/pydantic/inferred-auth-implicit-api-key/tests/custom/test_client.py new file mode 100644 index 000000000000..ab04ce6393ef --- /dev/null +++ b/seed/pydantic/inferred-auth-implicit-api-key/tests/custom/test_client.py @@ -0,0 +1,7 @@ +import pytest + + +# Get started with writing tests with pytest at https://docs.pytest.org +@pytest.mark.skip(reason="Unimplemented") +def test_client() -> None: + assert True diff --git a/seed/pydantic/inferred-auth-implicit-reference/.github/workflows/ci.yml b/seed/pydantic/inferred-auth-implicit-reference/.github/workflows/ci.yml new file mode 100644 index 000000000000..ffd2d8acab24 --- /dev/null +++ b/seed/pydantic/inferred-auth-implicit-reference/.github/workflows/ci.yml @@ -0,0 +1,60 @@ +name: ci +on: [push] +jobs: + compile: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + - name: Set up python + uses: actions/setup-python@v4 + with: + python-version: 3.9 + - name: Bootstrap poetry + run: | + curl -sSL https://install.python-poetry.org | python - -y --version 1.5.1 + - name: Install dependencies + run: poetry install + - name: Compile + run: poetry run mypy . + test: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + - name: Set up python + uses: actions/setup-python@v4 + with: + python-version: 3.9 + - name: Bootstrap poetry + run: | + curl -sSL https://install.python-poetry.org | python - -y --version 1.5.1 + - name: Install dependencies + run: poetry install + + - name: Test + run: poetry run pytest -rP -n auto . + + publish: + needs: [compile, test] + if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + - name: Set up python + uses: actions/setup-python@v4 + with: + python-version: 3.9 + - name: Bootstrap poetry + run: | + curl -sSL https://install.python-poetry.org | python - -y --version 1.5.1 + - name: Install dependencies + run: poetry install + - name: Publish to pypi + run: | + poetry config repositories.remote + poetry --no-interaction -v publish --build --repository remote --username "$PYPI_USERNAME" --password "$PYPI_PASSWORD" + env: + PYPI_USERNAME: ${{ secrets.PYPI_USERNAME }} + PYPI_PASSWORD: ${{ secrets.PYPI_PASSWORD }} diff --git a/seed/pydantic/inferred-auth-implicit-reference/.gitignore b/seed/pydantic/inferred-auth-implicit-reference/.gitignore new file mode 100644 index 000000000000..d2e4ca808d21 --- /dev/null +++ b/seed/pydantic/inferred-auth-implicit-reference/.gitignore @@ -0,0 +1,5 @@ +.mypy_cache/ +.ruff_cache/ +__pycache__/ +dist/ +poetry.toml diff --git a/seed/pydantic/inferred-auth-implicit-reference/README.md b/seed/pydantic/inferred-auth-implicit-reference/README.md new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/pydantic/inferred-auth-implicit-reference/poetry.lock b/seed/pydantic/inferred-auth-implicit-reference/poetry.lock new file mode 100644 index 000000000000..60f7bd567c95 --- /dev/null +++ b/seed/pydantic/inferred-auth-implicit-reference/poetry.lock @@ -0,0 +1,480 @@ +# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. + +[[package]] +name = "annotated-types" +version = "0.7.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +files = [ + {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, + {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.9\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "exceptiongroup" +version = "1.3.1" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598"}, + {file = "exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""} + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "execnet" +version = "2.1.2" +description = "execnet: rapid multi-Python deployment" +optional = false +python-versions = ">=3.8" +files = [ + {file = "execnet-2.1.2-py3-none-any.whl", hash = "sha256:67fba928dd5a544b783f6056f449e5e3931a5c378b128bc18501f7ea79e296ec"}, + {file = "execnet-2.1.2.tar.gz", hash = "sha256:63d83bfdd9a23e35b9c6a3261412324f964c2ec8dcd8d3c6916ee9373e0befcd"}, +] + +[package.extras] +testing = ["hatch", "pre-commit", "pytest", "tox"] + +[[package]] +name = "iniconfig" +version = "2.1.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.8" +files = [ + {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, + {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, +] + +[[package]] +name = "mypy" +version = "1.13.0" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mypy-1.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6607e0f1dd1fb7f0aca14d936d13fd19eba5e17e1cd2a14f808fa5f8f6d8f60a"}, + {file = "mypy-1.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8a21be69bd26fa81b1f80a61ee7ab05b076c674d9b18fb56239d72e21d9f4c80"}, + {file = "mypy-1.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b2353a44d2179846a096e25691d54d59904559f4232519d420d64da6828a3a7"}, + {file = "mypy-1.13.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0730d1c6a2739d4511dc4253f8274cdd140c55c32dfb0a4cf8b7a43f40abfa6f"}, + {file = "mypy-1.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:c5fc54dbb712ff5e5a0fca797e6e0aa25726c7e72c6a5850cfd2adbc1eb0a372"}, + {file = "mypy-1.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:581665e6f3a8a9078f28d5502f4c334c0c8d802ef55ea0e7276a6e409bc0d82d"}, + {file = "mypy-1.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3ddb5b9bf82e05cc9a627e84707b528e5c7caaa1c55c69e175abb15a761cec2d"}, + {file = "mypy-1.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20c7ee0bc0d5a9595c46f38beb04201f2620065a93755704e141fcac9f59db2b"}, + {file = "mypy-1.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3790ded76f0b34bc9c8ba4def8f919dd6a46db0f5a6610fb994fe8efdd447f73"}, + {file = "mypy-1.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:51f869f4b6b538229c1d1bcc1dd7d119817206e2bc54e8e374b3dfa202defcca"}, + {file = "mypy-1.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5c7051a3461ae84dfb5dd15eff5094640c61c5f22257c8b766794e6dd85e72d5"}, + {file = "mypy-1.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:39bb21c69a5d6342f4ce526e4584bc5c197fd20a60d14a8624d8743fffb9472e"}, + {file = "mypy-1.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:164f28cb9d6367439031f4c81e84d3ccaa1e19232d9d05d37cb0bd880d3f93c2"}, + {file = "mypy-1.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a4c1bfcdbce96ff5d96fc9b08e3831acb30dc44ab02671eca5953eadad07d6d0"}, + {file = "mypy-1.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:a0affb3a79a256b4183ba09811e3577c5163ed06685e4d4b46429a271ba174d2"}, + {file = "mypy-1.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a7b44178c9760ce1a43f544e595d35ed61ac2c3de306599fa59b38a6048e1aa7"}, + {file = "mypy-1.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5d5092efb8516d08440e36626f0153b5006d4088c1d663d88bf79625af3d1d62"}, + {file = "mypy-1.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2904956dac40ced10931ac967ae63c5089bd498542194b436eb097a9f77bc8"}, + {file = "mypy-1.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:7bfd8836970d33c2105562650656b6846149374dc8ed77d98424b40b09340ba7"}, + {file = "mypy-1.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:9f73dba9ec77acb86457a8fc04b5239822df0c14a082564737833d2963677dbc"}, + {file = "mypy-1.13.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:100fac22ce82925f676a734af0db922ecfea991e1d7ec0ceb1e115ebe501301a"}, + {file = "mypy-1.13.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7bcb0bb7f42a978bb323a7c88f1081d1b5dee77ca86f4100735a6f541299d8fb"}, + {file = "mypy-1.13.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bde31fc887c213e223bbfc34328070996061b0833b0a4cfec53745ed61f3519b"}, + {file = "mypy-1.13.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:07de989f89786f62b937851295ed62e51774722e5444a27cecca993fc3f9cd74"}, + {file = "mypy-1.13.0-cp38-cp38-win_amd64.whl", hash = "sha256:4bde84334fbe19bad704b3f5b78c4abd35ff1026f8ba72b29de70dda0916beb6"}, + {file = "mypy-1.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0246bcb1b5de7f08f2826451abd947bf656945209b140d16ed317f65a17dc7dc"}, + {file = "mypy-1.13.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7f5b7deae912cf8b77e990b9280f170381fdfbddf61b4ef80927edd813163732"}, + {file = "mypy-1.13.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7029881ec6ffb8bc233a4fa364736789582c738217b133f1b55967115288a2bc"}, + {file = "mypy-1.13.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3e38b980e5681f28f033f3be86b099a247b13c491f14bb8b1e1e134d23bb599d"}, + {file = "mypy-1.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:a6789be98a2017c912ae6ccb77ea553bbaf13d27605d2ca20a76dfbced631b24"}, + {file = "mypy-1.13.0-py3-none-any.whl", hash = "sha256:9c250883f9fd81d212e0952c92dbfcc96fc237f4b7c92f56ac81fd48460b3e5a"}, + {file = "mypy-1.13.0.tar.gz", hash = "sha256:0291a61b6fbf3e6673e3405cfcc0e7650bebc7939659fdca2702958038bd835e"}, +] + +[package.dependencies] +mypy-extensions = ">=1.0.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = ">=4.6.0" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +faster-cache = ["orjson"] +install-types = ["pip"] +mypyc = ["setuptools (>=50)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.8" +files = [ + {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, + {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, +] + +[[package]] +name = "packaging" +version = "25.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, + {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, +] + +[[package]] +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pydantic" +version = "2.10.6" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584"}, + {file = "pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236"}, +] + +[package.dependencies] +annotated-types = ">=0.6.0" +pydantic-core = "2.27.2" +typing-extensions = ">=4.12.2" + +[package.extras] +email = ["email-validator (>=2.0.0)"] +timezone = ["tzdata"] + +[[package]] +name = "pydantic-core" +version = "2.27.2" +description = "Core functionality for Pydantic validation and serialization" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa"}, + {file = "pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7969e133a6f183be60e9f6f56bfae753585680f3b7307a8e555a948d443cc05a"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3de9961f2a346257caf0aa508a4da705467f53778e9ef6fe744c038119737ef5"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2bb4d3e5873c37bb3dd58714d4cd0b0e6238cebc4177ac8fe878f8b3aa8e74c"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:280d219beebb0752699480fe8f1dc61ab6615c2046d76b7ab7ee38858de0a4e7"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47956ae78b6422cbd46f772f1746799cbb862de838fd8d1fbd34a82e05b0983a"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:14d4a5c49d2f009d62a2a7140d3064f686d17a5d1a268bc641954ba181880236"}, + {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:337b443af21d488716f8d0b6164de833e788aa6bd7e3a39c005febc1284f4962"}, + {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:03d0f86ea3184a12f41a2d23f7ccb79cdb5a18e06993f8a45baa8dfec746f0e9"}, + {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7041c36f5680c6e0f08d922aed302e98b3745d97fe1589db0a3eebf6624523af"}, + {file = "pydantic_core-2.27.2-cp310-cp310-win32.whl", hash = "sha256:50a68f3e3819077be2c98110c1f9dcb3817e93f267ba80a2c05bb4f8799e2ff4"}, + {file = "pydantic_core-2.27.2-cp310-cp310-win_amd64.whl", hash = "sha256:e0fd26b16394ead34a424eecf8a31a1f5137094cabe84a1bcb10fa6ba39d3d31"}, + {file = "pydantic_core-2.27.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8e10c99ef58cfdf2a66fc15d66b16c4a04f62bca39db589ae8cba08bc55331bc"}, + {file = "pydantic_core-2.27.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:26f32e0adf166a84d0cb63be85c562ca8a6fa8de28e5f0d92250c6b7e9e2aff7"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c19d1ea0673cd13cc2f872f6c9ab42acc4e4f492a7ca9d3795ce2b112dd7e15"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e68c4446fe0810e959cdff46ab0a41ce2f2c86d227d96dc3847af0ba7def306"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9640b0059ff4f14d1f37321b94061c6db164fbe49b334b31643e0528d100d99"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40d02e7d45c9f8af700f3452f329ead92da4c5f4317ca9b896de7ce7199ea459"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d81d2068e1c1228a565af076598f9e7451712700b673de8f502f0334f281387d"}, + {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1a4207639fb02ec2dbb76227d7c751a20b1a6b4bc52850568e52260cae64ca3b"}, + {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:3de3ce3c9ddc8bbd88f6e0e304dea0e66d843ec9de1b0042b0911c1663ffd474"}, + {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:30c5f68ded0c36466acede341551106821043e9afaad516adfb6e8fa80a4e6a6"}, + {file = "pydantic_core-2.27.2-cp311-cp311-win32.whl", hash = "sha256:c70c26d2c99f78b125a3459f8afe1aed4d9687c24fd677c6a4436bc042e50d6c"}, + {file = "pydantic_core-2.27.2-cp311-cp311-win_amd64.whl", hash = "sha256:08e125dbdc505fa69ca7d9c499639ab6407cfa909214d500897d02afb816e7cc"}, + {file = "pydantic_core-2.27.2-cp311-cp311-win_arm64.whl", hash = "sha256:26f0d68d4b235a2bae0c3fc585c585b4ecc51382db0e3ba402a22cbc440915e4"}, + {file = "pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0"}, + {file = "pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4"}, + {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3"}, + {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4"}, + {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57"}, + {file = "pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc"}, + {file = "pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9"}, + {file = "pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b"}, + {file = "pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b"}, + {file = "pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4"}, + {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27"}, + {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee"}, + {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1"}, + {file = "pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130"}, + {file = "pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee"}, + {file = "pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b"}, + {file = "pydantic_core-2.27.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d3e8d504bdd3f10835468f29008d72fc8359d95c9c415ce6e767203db6127506"}, + {file = "pydantic_core-2.27.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:521eb9b7f036c9b6187f0b47318ab0d7ca14bd87f776240b90b21c1f4f149320"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85210c4d99a0114f5a9481b44560d7d1e35e32cc5634c656bc48e590b669b145"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d716e2e30c6f140d7560ef1538953a5cd1a87264c737643d481f2779fc247fe1"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f66d89ba397d92f840f8654756196d93804278457b5fbede59598a1f9f90b228"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:669e193c1c576a58f132e3158f9dfa9662969edb1a250c54d8fa52590045f046"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdbe7629b996647b99c01b37f11170a57ae675375b14b8c13b8518b8320ced5"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d262606bf386a5ba0b0af3b97f37c83d7011439e3dc1a9298f21efb292e42f1a"}, + {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cabb9bcb7e0d97f74df8646f34fc76fbf793b7f6dc2438517d7a9e50eee4f14d"}, + {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:d2d63f1215638d28221f664596b1ccb3944f6e25dd18cd3b86b0a4c408d5ebb9"}, + {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bca101c00bff0adb45a833f8451b9105d9df18accb8743b08107d7ada14bd7da"}, + {file = "pydantic_core-2.27.2-cp38-cp38-win32.whl", hash = "sha256:f6f8e111843bbb0dee4cb6594cdc73e79b3329b526037ec242a3e49012495b3b"}, + {file = "pydantic_core-2.27.2-cp38-cp38-win_amd64.whl", hash = "sha256:fd1aea04935a508f62e0d0ef1f5ae968774a32afc306fb8545e06f5ff5cdf3ad"}, + {file = "pydantic_core-2.27.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c10eb4f1659290b523af58fa7cffb452a61ad6ae5613404519aee4bfbf1df993"}, + {file = "pydantic_core-2.27.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ef592d4bad47296fb11f96cd7dc898b92e795032b4894dfb4076cfccd43a9308"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61709a844acc6bf0b7dce7daae75195a10aac96a596ea1b776996414791ede4"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c5f762659e47fdb7b16956c71598292f60a03aa92f8b6351504359dbdba6cf"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c9775e339e42e79ec99c441d9730fccf07414af63eac2f0e48e08fd38a64d76"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57762139821c31847cfb2df63c12f725788bd9f04bc2fb392790959b8f70f118"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d1e85068e818c73e048fe28cfc769040bb1f475524f4745a5dc621f75ac7630"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:097830ed52fd9e427942ff3b9bc17fab52913b2f50f2880dc4a5611446606a54"}, + {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:044a50963a614ecfae59bb1eaf7ea7efc4bc62f49ed594e18fa1e5d953c40e9f"}, + {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:4e0b4220ba5b40d727c7f879eac379b822eee5d8fff418e9d3381ee45b3b0362"}, + {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e4f4bb20d75e9325cc9696c6802657b58bc1dbbe3022f32cc2b2b632c3fbb96"}, + {file = "pydantic_core-2.27.2-cp39-cp39-win32.whl", hash = "sha256:cca63613e90d001b9f2f9a9ceb276c308bfa2a43fafb75c8031c4f66039e8c6e"}, + {file = "pydantic_core-2.27.2-cp39-cp39-win_amd64.whl", hash = "sha256:77d1bca19b0f7021b3a982e6f903dcd5b2b06076def36a652e3907f596e29f67"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2bf14caea37e91198329b828eae1618c068dfb8ef17bb33287a7ad4b61ac314e"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0cb791f5b45307caae8810c2023a184c74605ec3bcbb67d13846c28ff731ff8"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:688d3fd9fcb71f41c4c015c023d12a79d1c4c0732ec9eb35d96e3388a120dcf3"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d591580c34f4d731592f0e9fe40f9cc1b430d297eecc70b962e93c5c668f15f"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:82f986faf4e644ffc189a7f1aafc86e46ef70372bb153e7001e8afccc6e54133"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:bec317a27290e2537f922639cafd54990551725fc844249e64c523301d0822fc"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:0296abcb83a797db256b773f45773da397da75a08f5fcaef41f2044adec05f50"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0d75070718e369e452075a6017fbf187f788e17ed67a3abd47fa934d001863d9"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7e17b560be3c98a8e3aa66ce828bdebb9e9ac6ad5466fba92eb74c4c95cb1151"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c33939a82924da9ed65dab5a65d427205a73181d8098e79b6b426bdf8ad4e656"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:00bad2484fa6bda1e216e7345a798bd37c68fb2d97558edd584942aa41b7d278"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c817e2b40aba42bac6f457498dacabc568c3b7a986fc9ba7c8d9d260b71485fb"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:251136cdad0cb722e93732cb45ca5299fb56e1344a833640bf93b2803f8d1bfd"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2088237af596f0a524d3afc39ab3b036e8adb054ee57cbb1dcf8e09da5b29cc"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d4041c0b966a84b4ae7a09832eb691a35aec90910cd2dbe7a208de59be77965b"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:8083d4e875ebe0b864ffef72a4304827015cff328a1be6e22cc850753bfb122b"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f141ee28a0ad2123b6611b6ceff018039df17f32ada8b534e6aa039545a3efb2"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7d0c8399fcc1848491f00e0314bd59fb34a9c008761bcb422a057670c3f65e35"}, + {file = "pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" + +[[package]] +name = "pytest" +version = "7.4.4" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, + {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-asyncio" +version = "0.23.8" +description = "Pytest support for asyncio" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest_asyncio-0.23.8-py3-none-any.whl", hash = "sha256:50265d892689a5faefb84df80819d1ecef566eb3549cf915dfb33569359d1ce2"}, + {file = "pytest_asyncio-0.23.8.tar.gz", hash = "sha256:759b10b33a6dc61cce40a8bd5205e302978bbbcc00e279a8b61d9a6a3c82e4d3"}, +] + +[package.dependencies] +pytest = ">=7.0.0,<9" + +[package.extras] +docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] +testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] + +[[package]] +name = "pytest-xdist" +version = "3.6.1" +description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7"}, + {file = "pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d"}, +] + +[package.dependencies] +execnet = ">=2.1" +pytest = ">=7.0.0" + +[package.extras] +psutil = ["psutil (>=3.0)"] +setproctitle = ["setproctitle"] +testing = ["filelock"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "ruff" +version = "0.11.5" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.11.5-py3-none-linux_armv6l.whl", hash = "sha256:2561294e108eb648e50f210671cc56aee590fb6167b594144401532138c66c7b"}, + {file = "ruff-0.11.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ac12884b9e005c12d0bd121f56ccf8033e1614f736f766c118ad60780882a077"}, + {file = "ruff-0.11.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:4bfd80a6ec559a5eeb96c33f832418bf0fb96752de0539905cf7b0cc1d31d779"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0947c0a1afa75dcb5db4b34b070ec2bccee869d40e6cc8ab25aca11a7d527794"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ad871ff74b5ec9caa66cb725b85d4ef89b53f8170f47c3406e32ef040400b038"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e6cf918390cfe46d240732d4d72fa6e18e528ca1f60e318a10835cf2fa3dc19f"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:56145ee1478582f61c08f21076dc59153310d606ad663acc00ea3ab5b2125f82"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e5f66f8f1e8c9fc594cbd66fbc5f246a8d91f916cb9667e80208663ec3728304"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80b4df4d335a80315ab9afc81ed1cff62be112bd165e162b5eed8ac55bfc8470"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3068befab73620b8a0cc2431bd46b3cd619bc17d6f7695a3e1bb166b652c382a"}, + {file = "ruff-0.11.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f5da2e710a9641828e09aa98b92c9ebbc60518fdf3921241326ca3e8f8e55b8b"}, + {file = "ruff-0.11.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ef39f19cb8ec98cbc762344921e216f3857a06c47412030374fffd413fb8fd3a"}, + {file = "ruff-0.11.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:b2a7cedf47244f431fd11aa5a7e2806dda2e0c365873bda7834e8f7d785ae159"}, + {file = "ruff-0.11.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:81be52e7519f3d1a0beadcf8e974715b2dfc808ae8ec729ecfc79bddf8dbb783"}, + {file = "ruff-0.11.5-py3-none-win32.whl", hash = "sha256:e268da7b40f56e3eca571508a7e567e794f9bfcc0f412c4b607931d3af9c4afe"}, + {file = "ruff-0.11.5-py3-none-win_amd64.whl", hash = "sha256:6c6dc38af3cfe2863213ea25b6dc616d679205732dc0fb673356c2d69608f800"}, + {file = "ruff-0.11.5-py3-none-win_arm64.whl", hash = "sha256:67e241b4314f4eacf14a601d586026a962f4002a475aa702c69980a38087aa4e"}, + {file = "ruff-0.11.5.tar.gz", hash = "sha256:cae2e2439cb88853e421901ec040a758960b576126dab520fa08e9de431d1bef"}, +] + +[[package]] +name = "six" +version = "1.17.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, +] + +[[package]] +name = "tomli" +version = "2.3.0" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45"}, + {file = "tomli-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:883b1c0d6398a6a9d29b508c331fa56adbcdff647f6ace4dfca0f50e90dfd0ba"}, + {file = "tomli-2.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1381caf13ab9f300e30dd8feadb3de072aeb86f1d34a8569453ff32a7dea4bf"}, + {file = "tomli-2.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0e285d2649b78c0d9027570d4da3425bdb49830a6156121360b3f8511ea3441"}, + {file = "tomli-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a154a9ae14bfcf5d8917a59b51ffd5a3ac1fd149b71b47a3a104ca4edcfa845"}, + {file = "tomli-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:74bf8464ff93e413514fefd2be591c3b0b23231a77f901db1eb30d6f712fc42c"}, + {file = "tomli-2.3.0-cp311-cp311-win32.whl", hash = "sha256:00b5f5d95bbfc7d12f91ad8c593a1659b6387b43f054104cda404be6bda62456"}, + {file = "tomli-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:4dc4ce8483a5d429ab602f111a93a6ab1ed425eae3122032db7e9acf449451be"}, + {file = "tomli-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d7d86942e56ded512a594786a5ba0a5e521d02529b3826e7761a05138341a2ac"}, + {file = "tomli-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:73ee0b47d4dad1c5e996e3cd33b8a76a50167ae5f96a2607cbe8cc773506ab22"}, + {file = "tomli-2.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:792262b94d5d0a466afb5bc63c7daa9d75520110971ee269152083270998316f"}, + {file = "tomli-2.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f195fe57ecceac95a66a75ac24d9d5fbc98ef0962e09b2eddec5d39375aae52"}, + {file = "tomli-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e31d432427dcbf4d86958c184b9bfd1e96b5b71f8eb17e6d02531f434fd335b8"}, + {file = "tomli-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b0882799624980785240ab732537fcfc372601015c00f7fc367c55308c186f6"}, + {file = "tomli-2.3.0-cp312-cp312-win32.whl", hash = "sha256:ff72b71b5d10d22ecb084d345fc26f42b5143c5533db5e2eaba7d2d335358876"}, + {file = "tomli-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:1cb4ed918939151a03f33d4242ccd0aa5f11b3547d0cf30f7c74a408a5b99878"}, + {file = "tomli-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5192f562738228945d7b13d4930baffda67b69425a7f0da96d360b0a3888136b"}, + {file = "tomli-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:be71c93a63d738597996be9528f4abe628d1adf5e6eb11607bc8fe1a510b5dae"}, + {file = "tomli-2.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4665508bcbac83a31ff8ab08f424b665200c0e1e645d2bd9ab3d3e557b6185b"}, + {file = "tomli-2.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4021923f97266babc6ccab9f5068642a0095faa0a51a246a6a02fccbb3514eaf"}, + {file = "tomli-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4ea38c40145a357d513bffad0ed869f13c1773716cf71ccaa83b0fa0cc4e42f"}, + {file = "tomli-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad805ea85eda330dbad64c7ea7a4556259665bdf9d2672f5dccc740eb9d3ca05"}, + {file = "tomli-2.3.0-cp313-cp313-win32.whl", hash = "sha256:97d5eec30149fd3294270e889b4234023f2c69747e555a27bd708828353ab606"}, + {file = "tomli-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0c95ca56fbe89e065c6ead5b593ee64b84a26fca063b5d71a1122bf26e533999"}, + {file = "tomli-2.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cebc6fe843e0733ee827a282aca4999b596241195f43b4cc371d64fc6639da9e"}, + {file = "tomli-2.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4c2ef0244c75aba9355561272009d934953817c49f47d768070c3c94355c2aa3"}, + {file = "tomli-2.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c22a8bf253bacc0cf11f35ad9808b6cb75ada2631c2d97c971122583b129afbc"}, + {file = "tomli-2.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0eea8cc5c5e9f89c9b90c4896a8deefc74f518db5927d0e0e8d4a80953d774d0"}, + {file = "tomli-2.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b74a0e59ec5d15127acdabd75ea17726ac4c5178ae51b85bfe39c4f8a278e879"}, + {file = "tomli-2.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b5870b50c9db823c595983571d1296a6ff3e1b88f734a4c8f6fc6188397de005"}, + {file = "tomli-2.3.0-cp314-cp314-win32.whl", hash = "sha256:feb0dacc61170ed7ab602d3d972a58f14ee3ee60494292d384649a3dc38ef463"}, + {file = "tomli-2.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:b273fcbd7fc64dc3600c098e39136522650c49bca95df2d11cf3b626422392c8"}, + {file = "tomli-2.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:940d56ee0410fa17ee1f12b817b37a4d4e4dc4d27340863cc67236c74f582e77"}, + {file = "tomli-2.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f85209946d1fe94416debbb88d00eb92ce9cd5266775424ff81bc959e001acaf"}, + {file = "tomli-2.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a56212bdcce682e56b0aaf79e869ba5d15a6163f88d5451cbde388d48b13f530"}, + {file = "tomli-2.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5f3ffd1e098dfc032d4d3af5c0ac64f6d286d98bc148698356847b80fa4de1b"}, + {file = "tomli-2.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e01decd096b1530d97d5d85cb4dff4af2d8347bd35686654a004f8dea20fc67"}, + {file = "tomli-2.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8a35dd0e643bb2610f156cca8db95d213a90015c11fee76c946aa62b7ae7e02f"}, + {file = "tomli-2.3.0-cp314-cp314t-win32.whl", hash = "sha256:a1f7f282fe248311650081faafa5f4732bdbfef5d45fe3f2e702fbc6f2d496e0"}, + {file = "tomli-2.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:70a251f8d4ba2d9ac2542eecf008b3c8a9fc5c3f9f02c56a9d7952612be2fdba"}, + {file = "tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b"}, + {file = "tomli-2.3.0.tar.gz", hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549"}, +] + +[[package]] +name = "types-python-dateutil" +version = "2.9.0.20241206" +description = "Typing stubs for python-dateutil" +optional = false +python-versions = ">=3.8" +files = [ + {file = "types_python_dateutil-2.9.0.20241206-py3-none-any.whl", hash = "sha256:e248a4bc70a486d3e3ec84d0dc30eec3a5f979d6e7ee4123ae043eedbb987f53"}, + {file = "types_python_dateutil-2.9.0.20241206.tar.gz", hash = "sha256:18f493414c26ffba692a72369fea7a154c502646301ebfe3d56a04b3767284cb"}, +] + +[[package]] +name = "typing-extensions" +version = "4.13.2" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"}, + {file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"}, +] + +[metadata] +lock-version = "2.0" +python-versions = "^3.8" +content-hash = "cba1461c95c16fa07f656f27197490f8c77343a38b5aee6762b2b596e5d2996c" diff --git a/seed/pydantic/inferred-auth-implicit-reference/pyproject.toml b/seed/pydantic/inferred-auth-implicit-reference/pyproject.toml new file mode 100644 index 000000000000..4ccb0d2a0193 --- /dev/null +++ b/seed/pydantic/inferred-auth-implicit-reference/pyproject.toml @@ -0,0 +1,89 @@ +[project] +name = "fern_inferred-auth-implicit-reference" +dynamic = ["version"] + +[tool.poetry] +name = "fern_inferred-auth-implicit-reference" +version = "0.0.1" +description = "" +readme = "README.md" +authors = [] +keywords = [ + "fern", + "test" +] + +classifiers = [ + "Intended Audience :: Developers", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Operating System :: OS Independent", + "Operating System :: POSIX", + "Operating System :: MacOS", + "Operating System :: POSIX :: Linux", + "Operating System :: Microsoft :: Windows", + "Topic :: Software Development :: Libraries :: Python Modules", + "Typing :: Typed" +] +packages = [ + { include = "seed/inferred_auth_implicit", from = "src"} +] + +[tool.poetry.urls] +Documentation = 'https://buildwithfern.com/learn' +Homepage = 'https://buildwithfern.com/' +Repository = 'https://github.com/inferred-auth-implicit-reference/fern' + +[tool.poetry.dependencies] +python = "^3.8" +pydantic = ">= 1.9.2" +pydantic-core = ">=2.18.2" + +[tool.poetry.group.dev.dependencies] +mypy = "==1.13.0" +pytest = "^7.4.0" +pytest-asyncio = "^0.23.5" +pytest-xdist = "^3.6.1" +python-dateutil = "^2.9.0" +types-python-dateutil = "^2.9.0.20240316" +ruff = "==0.11.5" + +[tool.pytest.ini_options] +testpaths = [ "tests" ] +asyncio_mode = "auto" + +[tool.mypy] +plugins = ["pydantic.mypy"] + +[tool.ruff] +line-length = 120 + +[tool.ruff.lint] +select = [ + "E", # pycodestyle errors + "F", # pyflakes + "I", # isort +] +ignore = [ + "E402", # Module level import not at top of file + "E501", # Line too long + "E711", # Comparison to `None` should be `cond is not None` + "E712", # Avoid equality comparisons to `True`; use `if ...:` checks + "E721", # Use `is` and `is not` for type comparisons, or `isinstance()` for insinstance checks + "E722", # Do not use bare `except` + "E731", # Do not assign a `lambda` expression, use a `def` + "F821", # Undefined name + "F841" # Local variable ... is assigned to but never used +] + +[tool.ruff.lint.isort] +section-order = ["future", "standard-library", "third-party", "first-party"] + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/seed/pydantic/inferred-auth-implicit-reference/requirements.txt b/seed/pydantic/inferred-auth-implicit-reference/requirements.txt new file mode 100644 index 000000000000..c8f40119ae10 --- /dev/null +++ b/seed/pydantic/inferred-auth-implicit-reference/requirements.txt @@ -0,0 +1,2 @@ +pydantic>= 1.9.2 +pydantic-core>=2.18.2 diff --git a/seed/pydantic/inferred-auth-implicit-reference/snippet.json b/seed/pydantic/inferred-auth-implicit-reference/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/pydantic/inferred-auth-implicit-reference/src/seed/inferred_auth_implicit/__init__.py b/seed/pydantic/inferred-auth-implicit-reference/src/seed/inferred_auth_implicit/__init__.py new file mode 100644 index 000000000000..4fe1ac73e887 --- /dev/null +++ b/seed/pydantic/inferred-auth-implicit-reference/src/seed/inferred_auth_implicit/__init__.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +from .resources import GetTokenRequest, RefreshTokenRequest, TokenResponse, auth + +__all__ = ["GetTokenRequest", "RefreshTokenRequest", "TokenResponse", "auth"] diff --git a/seed/pydantic/inferred-auth-implicit-reference/src/seed/inferred_auth_implicit/core/__init__.py b/seed/pydantic/inferred-auth-implicit-reference/src/seed/inferred_auth_implicit/core/__init__.py new file mode 100644 index 000000000000..68bb7354679d --- /dev/null +++ b/seed/pydantic/inferred-auth-implicit-reference/src/seed/inferred_auth_implicit/core/__init__.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +from .datetime_utils import serialize_datetime +from .pydantic_utilities import ( + IS_PYDANTIC_V2, + UniversalBaseModel, + UniversalRootModel, + parse_obj_as, + universal_field_validator, + universal_root_validator, + update_forward_refs, +) +from .serialization import FieldMetadata + +__all__ = [ + "FieldMetadata", + "IS_PYDANTIC_V2", + "UniversalBaseModel", + "UniversalRootModel", + "parse_obj_as", + "serialize_datetime", + "universal_field_validator", + "universal_root_validator", + "update_forward_refs", +] diff --git a/seed/pydantic/inferred-auth-implicit-reference/src/seed/inferred_auth_implicit/core/datetime_utils.py b/seed/pydantic/inferred-auth-implicit-reference/src/seed/inferred_auth_implicit/core/datetime_utils.py new file mode 100644 index 000000000000..7c9864a944c2 --- /dev/null +++ b/seed/pydantic/inferred-auth-implicit-reference/src/seed/inferred_auth_implicit/core/datetime_utils.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt + + +def serialize_datetime(v: dt.datetime) -> str: + """ + Serialize a datetime including timezone info. + + Uses the timezone info provided if present, otherwise uses the current runtime's timezone info. + + UTC datetimes end in "Z" while all other timezones are represented as offset from UTC, e.g. +05:00. + """ + + def _serialize_zoned_datetime(v: dt.datetime) -> str: + if v.tzinfo is not None and v.tzinfo.tzname(None) == dt.timezone.utc.tzname(None): + # UTC is a special case where we use "Z" at the end instead of "+00:00" + return v.isoformat().replace("+00:00", "Z") + else: + # Delegate to the typical +/- offset format + return v.isoformat() + + if v.tzinfo is not None: + return _serialize_zoned_datetime(v) + else: + local_tz = dt.datetime.now().astimezone().tzinfo + localized_dt = v.replace(tzinfo=local_tz) + return _serialize_zoned_datetime(localized_dt) diff --git a/seed/pydantic/inferred-auth-implicit-reference/src/seed/inferred_auth_implicit/core/enum.py b/seed/pydantic/inferred-auth-implicit-reference/src/seed/inferred_auth_implicit/core/enum.py new file mode 100644 index 000000000000..a3d17a67b128 --- /dev/null +++ b/seed/pydantic/inferred-auth-implicit-reference/src/seed/inferred_auth_implicit/core/enum.py @@ -0,0 +1,20 @@ +# This file was auto-generated by Fern from our API Definition. + +""" +Provides a StrEnum base class that works across Python versions. + +For Python >= 3.11, this re-exports the standard library enum.StrEnum. +For older Python versions, this defines a compatible StrEnum using the +(str, Enum) mixin pattern so that generated SDKs can use a single base +class in all supported Python versions. +""" + +import enum +import sys + +if sys.version_info >= (3, 11): + from enum import StrEnum +else: + + class StrEnum(str, enum.Enum): + pass diff --git a/seed/pydantic/inferred-auth-implicit-reference/src/seed/inferred_auth_implicit/core/pydantic_utilities.py b/seed/pydantic/inferred-auth-implicit-reference/src/seed/inferred_auth_implicit/core/pydantic_utilities.py new file mode 100644 index 000000000000..1da6f10acdd1 --- /dev/null +++ b/seed/pydantic/inferred-auth-implicit-reference/src/seed/inferred_auth_implicit/core/pydantic_utilities.py @@ -0,0 +1,230 @@ +# This file was auto-generated by Fern from our API Definition. + +# nopycln: file +import datetime as dt +from collections import defaultdict +from typing import Any, Callable, Dict, List, Mapping, Tuple, Type, TypeVar, Union, cast + +import pydantic + +IS_PYDANTIC_V2 = pydantic.VERSION.startswith("2.") + +if IS_PYDANTIC_V2: + from pydantic.v1.datetime_parse import parse_date as parse_date + from pydantic.v1.datetime_parse import parse_datetime as parse_datetime + from pydantic.v1.fields import ModelField as ModelField + from pydantic.v1.json import ENCODERS_BY_TYPE as encoders_by_type # type: ignore[attr-defined] + from pydantic.v1.typing import get_args as get_args + from pydantic.v1.typing import get_origin as get_origin + from pydantic.v1.typing import is_literal_type as is_literal_type + from pydantic.v1.typing import is_union as is_union +else: + from pydantic.datetime_parse import parse_date as parse_date # type: ignore[no-redef] + from pydantic.datetime_parse import parse_datetime as parse_datetime # type: ignore[no-redef] + from pydantic.fields import ModelField as ModelField # type: ignore[attr-defined, no-redef] + from pydantic.json import ENCODERS_BY_TYPE as encoders_by_type # type: ignore[no-redef] + from pydantic.typing import get_args as get_args # type: ignore[no-redef] + from pydantic.typing import get_origin as get_origin # type: ignore[no-redef] + from pydantic.typing import is_literal_type as is_literal_type # type: ignore[no-redef] + from pydantic.typing import is_union as is_union # type: ignore[no-redef] + +from .datetime_utils import serialize_datetime +from typing_extensions import TypeAlias + +T = TypeVar("T") +Model = TypeVar("Model", bound=pydantic.BaseModel) + + +def parse_obj_as(type_: Type[T], object_: Any) -> T: + if IS_PYDANTIC_V2: + adapter = pydantic.TypeAdapter(type_) # type: ignore[attr-defined] + return adapter.validate_python(object_) + return pydantic.parse_obj_as(type_, object_) + + +def to_jsonable_with_fallback(obj: Any, fallback_serializer: Callable[[Any], Any]) -> Any: + if IS_PYDANTIC_V2: + from pydantic_core import to_jsonable_python + + return to_jsonable_python(obj, fallback=fallback_serializer) + return fallback_serializer(obj) + + +class UniversalBaseModel(pydantic.BaseModel): + class Config: + populate_by_name = True + smart_union = True + allow_population_by_field_name = True + json_encoders = {dt.datetime: serialize_datetime} + # Allow fields beginning with `model_` to be used in the model + protected_namespaces = () + + def json(self, **kwargs: Any) -> str: + kwargs_with_defaults = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + if IS_PYDANTIC_V2: + return super().model_dump_json(**kwargs_with_defaults) # type: ignore[misc] + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: Any) -> Dict[str, Any]: + """ + Override the default dict method to `exclude_unset` by default. This function patches + `exclude_unset` to work include fields within non-None default values. + """ + # Note: the logic here is multiplexed given the levers exposed in Pydantic V1 vs V2 + # Pydantic V1's .dict can be extremely slow, so we do not want to call it twice. + # + # We'd ideally do the same for Pydantic V2, but it shells out to a library to serialize models + # that we have less control over, and this is less intrusive than custom serializers for now. + if IS_PYDANTIC_V2: + kwargs_with_defaults_exclude_unset = { + **kwargs, + "by_alias": True, + "exclude_unset": True, + "exclude_none": False, + } + kwargs_with_defaults_exclude_none = { + **kwargs, + "by_alias": True, + "exclude_none": True, + "exclude_unset": False, + } + return deep_union_pydantic_dicts( + super().model_dump(**kwargs_with_defaults_exclude_unset), # type: ignore[misc] + super().model_dump(**kwargs_with_defaults_exclude_none), # type: ignore[misc] + ) + + _fields_set = self.__fields_set__.copy() + + fields = _get_model_fields(self.__class__) + for name, field in fields.items(): + if name not in _fields_set: + default = _get_field_default(field) + + # If the default values are non-null act like they've been set + # This effectively allows exclude_unset to work like exclude_none where + # the latter passes through intentionally set none values. + if default is not None or ("exclude_unset" in kwargs and not kwargs["exclude_unset"]): + _fields_set.add(name) + + if default is not None: + self.__fields_set__.add(name) + + kwargs_with_defaults_exclude_unset_include_fields = { + "by_alias": True, + "exclude_unset": True, + "include": _fields_set, + **kwargs, + } + + return super().dict(**kwargs_with_defaults_exclude_unset_include_fields) + + +def _union_list_of_pydantic_dicts(source: List[Any], destination: List[Any]) -> List[Any]: + converted_list: List[Any] = [] + for i, item in enumerate(source): + destination_value = destination[i] + if isinstance(item, dict): + converted_list.append(deep_union_pydantic_dicts(item, destination_value)) + elif isinstance(item, list): + converted_list.append(_union_list_of_pydantic_dicts(item, destination_value)) + else: + converted_list.append(item) + return converted_list + + +def deep_union_pydantic_dicts(source: Dict[str, Any], destination: Dict[str, Any]) -> Dict[str, Any]: + for key, value in source.items(): + node = destination.setdefault(key, {}) + if isinstance(value, dict): + deep_union_pydantic_dicts(value, node) + # Note: we do not do this same processing for sets given we do not have sets of models + # and given the sets are unordered, the processing of the set and matching objects would + # be non-trivial. + elif isinstance(value, list): + destination[key] = _union_list_of_pydantic_dicts(value, node) + else: + destination[key] = value + + return destination + + +if IS_PYDANTIC_V2: + + class V2RootModel(UniversalBaseModel, pydantic.RootModel): # type: ignore[name-defined, type-arg] + pass + + UniversalRootModel: TypeAlias = V2RootModel +else: + UniversalRootModel: TypeAlias = UniversalBaseModel # type: ignore[misc, no-redef] + + +def encode_by_type(o: Any) -> Any: + encoders_by_class_tuples: Dict[Callable[[Any], Any], Tuple[Any, ...]] = defaultdict(tuple) + for type_, encoder in encoders_by_type.items(): + encoders_by_class_tuples[encoder] += (type_,) + + if type(o) in encoders_by_type: + return encoders_by_type[type(o)](o) + for encoder, classes_tuple in encoders_by_class_tuples.items(): + if isinstance(o, classes_tuple): + return encoder(o) + + +def update_forward_refs(model: Type["Model"], **localns: Any) -> None: + if IS_PYDANTIC_V2: + model.model_rebuild(raise_errors=False) # type: ignore[attr-defined] + else: + model.update_forward_refs(**localns) + + +# Mirrors Pydantic's internal typing +AnyCallable = Callable[..., Any] + + +def universal_root_validator( + pre: bool = False, +) -> Callable[[AnyCallable], AnyCallable]: + def decorator(func: AnyCallable) -> AnyCallable: + if IS_PYDANTIC_V2: + # In Pydantic v2, for RootModel we always use "before" mode + # The custom validators transform the input value before the model is created + return cast(AnyCallable, pydantic.model_validator(mode="before")(func)) # type: ignore[attr-defined] + return cast(AnyCallable, pydantic.root_validator(pre=pre)(func)) # type: ignore[call-overload] + + return decorator + + +def universal_field_validator(field_name: str, pre: bool = False) -> Callable[[AnyCallable], AnyCallable]: + def decorator(func: AnyCallable) -> AnyCallable: + if IS_PYDANTIC_V2: + return cast(AnyCallable, pydantic.field_validator(field_name, mode="before" if pre else "after")(func)) # type: ignore[attr-defined] + return cast(AnyCallable, pydantic.validator(field_name, pre=pre)(func)) + + return decorator + + +PydanticField = Union[ModelField, pydantic.fields.FieldInfo] + + +def _get_model_fields(model: Type["Model"]) -> Mapping[str, PydanticField]: + if IS_PYDANTIC_V2: + return cast(Mapping[str, PydanticField], model.model_fields) # type: ignore[attr-defined] + return cast(Mapping[str, PydanticField], model.__fields__) + + +def _get_field_default(field: PydanticField) -> Any: + try: + value = field.get_default() # type: ignore[union-attr] + except: + value = field.default + if IS_PYDANTIC_V2: + from pydantic_core import PydanticUndefined + + if value == PydanticUndefined: + return None + return value + return value diff --git a/seed/pydantic/inferred-auth-implicit-reference/src/seed/inferred_auth_implicit/core/serialization.py b/seed/pydantic/inferred-auth-implicit-reference/src/seed/inferred_auth_implicit/core/serialization.py new file mode 100644 index 000000000000..c36e865cc729 --- /dev/null +++ b/seed/pydantic/inferred-auth-implicit-reference/src/seed/inferred_auth_implicit/core/serialization.py @@ -0,0 +1,276 @@ +# This file was auto-generated by Fern from our API Definition. + +import collections +import inspect +import typing + +import pydantic +import typing_extensions + + +class FieldMetadata: + """ + Metadata class used to annotate fields to provide additional information. + + Example: + class MyDict(TypedDict): + field: typing.Annotated[str, FieldMetadata(alias="field_name")] + + Will serialize: `{"field": "value"}` + To: `{"field_name": "value"}` + """ + + alias: str + + def __init__(self, *, alias: str) -> None: + self.alias = alias + + +def convert_and_respect_annotation_metadata( + *, + object_: typing.Any, + annotation: typing.Any, + inner_type: typing.Optional[typing.Any] = None, + direction: typing.Literal["read", "write"], +) -> typing.Any: + """ + Respect the metadata annotations on a field, such as aliasing. This function effectively + manipulates the dict-form of an object to respect the metadata annotations. This is primarily used for + TypedDicts, which cannot support aliasing out of the box, and can be extended for additional + utilities, such as defaults. + + Parameters + ---------- + object_ : typing.Any + + annotation : type + The type we're looking to apply typing annotations from + + inner_type : typing.Optional[type] + + Returns + ------- + typing.Any + """ + + if object_ is None: + return None + if inner_type is None: + inner_type = annotation + + clean_type = _remove_annotations(inner_type) + # Pydantic models + if ( + inspect.isclass(clean_type) + and issubclass(clean_type, pydantic.BaseModel) + and isinstance(object_, typing.Mapping) + ): + return _convert_mapping(object_, clean_type, direction) + # TypedDicts + if typing_extensions.is_typeddict(clean_type) and isinstance(object_, typing.Mapping): + return _convert_mapping(object_, clean_type, direction) + + if ( + typing_extensions.get_origin(clean_type) == typing.Dict + or typing_extensions.get_origin(clean_type) == dict + or clean_type == typing.Dict + ) and isinstance(object_, typing.Dict): + key_type = typing_extensions.get_args(clean_type)[0] + value_type = typing_extensions.get_args(clean_type)[1] + + return { + key: convert_and_respect_annotation_metadata( + object_=value, + annotation=annotation, + inner_type=value_type, + direction=direction, + ) + for key, value in object_.items() + } + + # If you're iterating on a string, do not bother to coerce it to a sequence. + if not isinstance(object_, str): + if ( + typing_extensions.get_origin(clean_type) == typing.Set + or typing_extensions.get_origin(clean_type) == set + or clean_type == typing.Set + ) and isinstance(object_, typing.Set): + inner_type = typing_extensions.get_args(clean_type)[0] + return { + convert_and_respect_annotation_metadata( + object_=item, + annotation=annotation, + inner_type=inner_type, + direction=direction, + ) + for item in object_ + } + elif ( + ( + typing_extensions.get_origin(clean_type) == typing.List + or typing_extensions.get_origin(clean_type) == list + or clean_type == typing.List + ) + and isinstance(object_, typing.List) + ) or ( + ( + typing_extensions.get_origin(clean_type) == typing.Sequence + or typing_extensions.get_origin(clean_type) == collections.abc.Sequence + or clean_type == typing.Sequence + ) + and isinstance(object_, typing.Sequence) + ): + inner_type = typing_extensions.get_args(clean_type)[0] + return [ + convert_and_respect_annotation_metadata( + object_=item, + annotation=annotation, + inner_type=inner_type, + direction=direction, + ) + for item in object_ + ] + + if typing_extensions.get_origin(clean_type) == typing.Union: + # We should be able to ~relatively~ safely try to convert keys against all + # member types in the union, the edge case here is if one member aliases a field + # of the same name to a different name from another member + # Or if another member aliases a field of the same name that another member does not. + for member in typing_extensions.get_args(clean_type): + object_ = convert_and_respect_annotation_metadata( + object_=object_, + annotation=annotation, + inner_type=member, + direction=direction, + ) + return object_ + + annotated_type = _get_annotation(annotation) + if annotated_type is None: + return object_ + + # If the object is not a TypedDict, a Union, or other container (list, set, sequence, etc.) + # Then we can safely call it on the recursive conversion. + return object_ + + +def _convert_mapping( + object_: typing.Mapping[str, object], + expected_type: typing.Any, + direction: typing.Literal["read", "write"], +) -> typing.Mapping[str, object]: + converted_object: typing.Dict[str, object] = {} + try: + annotations = typing_extensions.get_type_hints(expected_type, include_extras=True) + except NameError: + # The TypedDict contains a circular reference, so + # we use the __annotations__ attribute directly. + annotations = getattr(expected_type, "__annotations__", {}) + aliases_to_field_names = _get_alias_to_field_name(annotations) + for key, value in object_.items(): + if direction == "read" and key in aliases_to_field_names: + dealiased_key = aliases_to_field_names.get(key) + if dealiased_key is not None: + type_ = annotations.get(dealiased_key) + else: + type_ = annotations.get(key) + # Note you can't get the annotation by the field name if you're in read mode, so you must check the aliases map + # + # So this is effectively saying if we're in write mode, and we don't have a type, or if we're in read mode and we don't have an alias + # then we can just pass the value through as is + if type_ is None: + converted_object[key] = value + elif direction == "read" and key not in aliases_to_field_names: + converted_object[key] = convert_and_respect_annotation_metadata( + object_=value, annotation=type_, direction=direction + ) + else: + converted_object[_alias_key(key, type_, direction, aliases_to_field_names)] = ( + convert_and_respect_annotation_metadata(object_=value, annotation=type_, direction=direction) + ) + return converted_object + + +def _get_annotation(type_: typing.Any) -> typing.Optional[typing.Any]: + maybe_annotated_type = typing_extensions.get_origin(type_) + if maybe_annotated_type is None: + return None + + if maybe_annotated_type == typing_extensions.NotRequired: + type_ = typing_extensions.get_args(type_)[0] + maybe_annotated_type = typing_extensions.get_origin(type_) + + if maybe_annotated_type == typing_extensions.Annotated: + return type_ + + return None + + +def _remove_annotations(type_: typing.Any) -> typing.Any: + maybe_annotated_type = typing_extensions.get_origin(type_) + if maybe_annotated_type is None: + return type_ + + if maybe_annotated_type == typing_extensions.NotRequired: + return _remove_annotations(typing_extensions.get_args(type_)[0]) + + if maybe_annotated_type == typing_extensions.Annotated: + return _remove_annotations(typing_extensions.get_args(type_)[0]) + + return type_ + + +def get_alias_to_field_mapping(type_: typing.Any) -> typing.Dict[str, str]: + annotations = typing_extensions.get_type_hints(type_, include_extras=True) + return _get_alias_to_field_name(annotations) + + +def get_field_to_alias_mapping(type_: typing.Any) -> typing.Dict[str, str]: + annotations = typing_extensions.get_type_hints(type_, include_extras=True) + return _get_field_to_alias_name(annotations) + + +def _get_alias_to_field_name( + field_to_hint: typing.Dict[str, typing.Any], +) -> typing.Dict[str, str]: + aliases = {} + for field, hint in field_to_hint.items(): + maybe_alias = _get_alias_from_type(hint) + if maybe_alias is not None: + aliases[maybe_alias] = field + return aliases + + +def _get_field_to_alias_name( + field_to_hint: typing.Dict[str, typing.Any], +) -> typing.Dict[str, str]: + aliases = {} + for field, hint in field_to_hint.items(): + maybe_alias = _get_alias_from_type(hint) + if maybe_alias is not None: + aliases[field] = maybe_alias + return aliases + + +def _get_alias_from_type(type_: typing.Any) -> typing.Optional[str]: + maybe_annotated_type = _get_annotation(type_) + + if maybe_annotated_type is not None: + # The actual annotations are 1 onward, the first is the annotated type + annotations = typing_extensions.get_args(maybe_annotated_type)[1:] + + for annotation in annotations: + if isinstance(annotation, FieldMetadata) and annotation.alias is not None: + return annotation.alias + return None + + +def _alias_key( + key: str, + type_: typing.Any, + direction: typing.Literal["read", "write"], + aliases_to_field_names: typing.Dict[str, str], +) -> str: + if direction == "read": + return aliases_to_field_names.get(key, key) + return _get_alias_from_type(type_=type_) or key diff --git a/seed/pydantic/inferred-auth-implicit-reference/src/seed/inferred_auth_implicit/py.typed b/seed/pydantic/inferred-auth-implicit-reference/src/seed/inferred_auth_implicit/py.typed new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/pydantic/inferred-auth-implicit-reference/src/seed/inferred_auth_implicit/resources/__init__.py b/seed/pydantic/inferred-auth-implicit-reference/src/seed/inferred_auth_implicit/resources/__init__.py new file mode 100644 index 000000000000..012b33c014b3 --- /dev/null +++ b/seed/pydantic/inferred-auth-implicit-reference/src/seed/inferred_auth_implicit/resources/__init__.py @@ -0,0 +1,8 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +from . import auth +from .auth import GetTokenRequest, RefreshTokenRequest, TokenResponse + +__all__ = ["GetTokenRequest", "RefreshTokenRequest", "TokenResponse", "auth"] diff --git a/seed/pydantic/inferred-auth-implicit-reference/src/seed/inferred_auth_implicit/resources/auth/__init__.py b/seed/pydantic/inferred-auth-implicit-reference/src/seed/inferred_auth_implicit/resources/auth/__init__.py new file mode 100644 index 000000000000..114b17974de8 --- /dev/null +++ b/seed/pydantic/inferred-auth-implicit-reference/src/seed/inferred_auth_implicit/resources/auth/__init__.py @@ -0,0 +1,9 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +from .get_token_request import GetTokenRequest +from .refresh_token_request import RefreshTokenRequest +from .token_response import TokenResponse + +__all__ = ["GetTokenRequest", "RefreshTokenRequest", "TokenResponse"] diff --git a/seed/pydantic/inferred-auth-implicit-reference/src/seed/inferred_auth_implicit/resources/auth/get_token_request.py b/seed/pydantic/inferred-auth-implicit-reference/src/seed/inferred_auth_implicit/resources/auth/get_token_request.py new file mode 100644 index 000000000000..05c343f440b3 --- /dev/null +++ b/seed/pydantic/inferred-auth-implicit-reference/src/seed/inferred_auth_implicit/resources/auth/get_token_request.py @@ -0,0 +1,25 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class GetTokenRequest(UniversalBaseModel): + """ + A request to obtain an OAuth token. + """ + + client_id: str + client_secret: str + audience: typing.Literal["https://api.example.com"] = "https://api.example.com" + grant_type: typing.Literal["client_credentials"] = "client_credentials" + scope: typing.Optional[str] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow") # type: ignore # Pydantic v2 + else: + + class Config: + extra = pydantic.Extra.allow diff --git a/seed/pydantic/inferred-auth-implicit-reference/src/seed/inferred_auth_implicit/resources/auth/refresh_token_request.py b/seed/pydantic/inferred-auth-implicit-reference/src/seed/inferred_auth_implicit/resources/auth/refresh_token_request.py new file mode 100644 index 000000000000..bd4ebadb6a1f --- /dev/null +++ b/seed/pydantic/inferred-auth-implicit-reference/src/seed/inferred_auth_implicit/resources/auth/refresh_token_request.py @@ -0,0 +1,26 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class RefreshTokenRequest(UniversalBaseModel): + """ + A request to refresh an OAuth token. + """ + + client_id: str + client_secret: str + refresh_token: str + audience: typing.Literal["https://api.example.com"] = "https://api.example.com" + grant_type: typing.Literal["refresh_token"] = "refresh_token" + scope: typing.Optional[str] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow") # type: ignore # Pydantic v2 + else: + + class Config: + extra = pydantic.Extra.allow diff --git a/seed/pydantic/inferred-auth-implicit-reference/src/seed/inferred_auth_implicit/resources/auth/token_response.py b/seed/pydantic/inferred-auth-implicit-reference/src/seed/inferred_auth_implicit/resources/auth/token_response.py new file mode 100644 index 000000000000..57a3d47837b2 --- /dev/null +++ b/seed/pydantic/inferred-auth-implicit-reference/src/seed/inferred_auth_implicit/resources/auth/token_response.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class TokenResponse(UniversalBaseModel): + """ + An OAuth token response. + """ + + access_token: str + expires_in: int + refresh_token: typing.Optional[str] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow") # type: ignore # Pydantic v2 + else: + + class Config: + extra = pydantic.Extra.allow diff --git a/seed/pydantic/inferred-auth-implicit-reference/tests/custom/test_client.py b/seed/pydantic/inferred-auth-implicit-reference/tests/custom/test_client.py new file mode 100644 index 000000000000..ab04ce6393ef --- /dev/null +++ b/seed/pydantic/inferred-auth-implicit-reference/tests/custom/test_client.py @@ -0,0 +1,7 @@ +import pytest + + +# Get started with writing tests with pytest at https://docs.pytest.org +@pytest.mark.skip(reason="Unimplemented") +def test_client() -> None: + assert True diff --git a/seed/python-sdk/accept-header/src/seed/core/http_client.py b/seed/python-sdk/accept-header/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/accept-header/src/seed/core/http_client.py +++ b/seed/python-sdk/accept-header/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/accept-header/tests/utils/test_http_client.py b/seed/python-sdk/accept-header/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/accept-header/tests/utils/test_http_client.py +++ b/seed/python-sdk/accept-header/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/alias-extends/no-custom-config/src/seed/core/http_client.py b/seed/python-sdk/alias-extends/no-custom-config/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/alias-extends/no-custom-config/src/seed/core/http_client.py +++ b/seed/python-sdk/alias-extends/no-custom-config/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/alias-extends/no-custom-config/tests/utils/test_http_client.py b/seed/python-sdk/alias-extends/no-custom-config/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/alias-extends/no-custom-config/tests/utils/test_http_client.py +++ b/seed/python-sdk/alias-extends/no-custom-config/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/alias-extends/no-inheritance-for-extended-models/src/seed/core/http_client.py b/seed/python-sdk/alias-extends/no-inheritance-for-extended-models/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/alias-extends/no-inheritance-for-extended-models/src/seed/core/http_client.py +++ b/seed/python-sdk/alias-extends/no-inheritance-for-extended-models/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/alias-extends/no-inheritance-for-extended-models/tests/utils/test_http_client.py b/seed/python-sdk/alias-extends/no-inheritance-for-extended-models/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/alias-extends/no-inheritance-for-extended-models/tests/utils/test_http_client.py +++ b/seed/python-sdk/alias-extends/no-inheritance-for-extended-models/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/alias/src/seed/core/http_client.py b/seed/python-sdk/alias/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/alias/src/seed/core/http_client.py +++ b/seed/python-sdk/alias/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/alias/tests/utils/test_http_client.py b/seed/python-sdk/alias/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/alias/tests/utils/test_http_client.py +++ b/seed/python-sdk/alias/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/any-auth/src/seed/core/http_client.py b/seed/python-sdk/any-auth/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/any-auth/src/seed/core/http_client.py +++ b/seed/python-sdk/any-auth/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/any-auth/tests/utils/test_http_client.py b/seed/python-sdk/any-auth/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/any-auth/tests/utils/test_http_client.py +++ b/seed/python-sdk/any-auth/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/api-wide-base-path/src/seed/core/http_client.py b/seed/python-sdk/api-wide-base-path/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/api-wide-base-path/src/seed/core/http_client.py +++ b/seed/python-sdk/api-wide-base-path/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/api-wide-base-path/tests/utils/test_http_client.py b/seed/python-sdk/api-wide-base-path/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/api-wide-base-path/tests/utils/test_http_client.py +++ b/seed/python-sdk/api-wide-base-path/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/audiences/src/seed/core/http_client.py b/seed/python-sdk/audiences/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/audiences/src/seed/core/http_client.py +++ b/seed/python-sdk/audiences/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/audiences/tests/utils/test_http_client.py b/seed/python-sdk/audiences/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/audiences/tests/utils/test_http_client.py +++ b/seed/python-sdk/audiences/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/basic-auth-environment-variables/src/seed/core/http_client.py b/seed/python-sdk/basic-auth-environment-variables/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/basic-auth-environment-variables/src/seed/core/http_client.py +++ b/seed/python-sdk/basic-auth-environment-variables/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/basic-auth-environment-variables/tests/utils/test_http_client.py b/seed/python-sdk/basic-auth-environment-variables/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/basic-auth-environment-variables/tests/utils/test_http_client.py +++ b/seed/python-sdk/basic-auth-environment-variables/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/basic-auth/src/seed/core/http_client.py b/seed/python-sdk/basic-auth/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/basic-auth/src/seed/core/http_client.py +++ b/seed/python-sdk/basic-auth/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/basic-auth/tests/utils/test_http_client.py b/seed/python-sdk/basic-auth/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/basic-auth/tests/utils/test_http_client.py +++ b/seed/python-sdk/basic-auth/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/bearer-token-environment-variable/src/seed/core/http_client.py b/seed/python-sdk/bearer-token-environment-variable/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/bearer-token-environment-variable/src/seed/core/http_client.py +++ b/seed/python-sdk/bearer-token-environment-variable/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/bearer-token-environment-variable/tests/utils/test_http_client.py b/seed/python-sdk/bearer-token-environment-variable/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/bearer-token-environment-variable/tests/utils/test_http_client.py +++ b/seed/python-sdk/bearer-token-environment-variable/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/bytes-download/src/seed/core/http_client.py b/seed/python-sdk/bytes-download/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/bytes-download/src/seed/core/http_client.py +++ b/seed/python-sdk/bytes-download/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/bytes-download/tests/utils/test_http_client.py b/seed/python-sdk/bytes-download/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/bytes-download/tests/utils/test_http_client.py +++ b/seed/python-sdk/bytes-download/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/bytes-upload/src/seed/core/http_client.py b/seed/python-sdk/bytes-upload/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/bytes-upload/src/seed/core/http_client.py +++ b/seed/python-sdk/bytes-upload/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/bytes-upload/tests/utils/test_http_client.py b/seed/python-sdk/bytes-upload/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/bytes-upload/tests/utils/test_http_client.py +++ b/seed/python-sdk/bytes-upload/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/circular-references-advanced/no-inheritance-for-extended-models/src/seed/core/http_client.py b/seed/python-sdk/circular-references-advanced/no-inheritance-for-extended-models/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/circular-references-advanced/no-inheritance-for-extended-models/src/seed/core/http_client.py +++ b/seed/python-sdk/circular-references-advanced/no-inheritance-for-extended-models/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/circular-references-advanced/no-inheritance-for-extended-models/tests/utils/test_http_client.py b/seed/python-sdk/circular-references-advanced/no-inheritance-for-extended-models/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/circular-references-advanced/no-inheritance-for-extended-models/tests/utils/test_http_client.py +++ b/seed/python-sdk/circular-references-advanced/no-inheritance-for-extended-models/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/circular-references/no-custom-config/src/seed/core/http_client.py b/seed/python-sdk/circular-references/no-custom-config/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/circular-references/no-custom-config/src/seed/core/http_client.py +++ b/seed/python-sdk/circular-references/no-custom-config/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/circular-references/no-custom-config/tests/utils/test_http_client.py b/seed/python-sdk/circular-references/no-custom-config/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/circular-references/no-custom-config/tests/utils/test_http_client.py +++ b/seed/python-sdk/circular-references/no-custom-config/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/circular-references/no-inheritance-for-extended-models/src/seed/core/http_client.py b/seed/python-sdk/circular-references/no-inheritance-for-extended-models/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/circular-references/no-inheritance-for-extended-models/src/seed/core/http_client.py +++ b/seed/python-sdk/circular-references/no-inheritance-for-extended-models/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/circular-references/no-inheritance-for-extended-models/tests/utils/test_http_client.py b/seed/python-sdk/circular-references/no-inheritance-for-extended-models/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/circular-references/no-inheritance-for-extended-models/tests/utils/test_http_client.py +++ b/seed/python-sdk/circular-references/no-inheritance-for-extended-models/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/client-side-params/src/seed/core/http_client.py b/seed/python-sdk/client-side-params/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/client-side-params/src/seed/core/http_client.py +++ b/seed/python-sdk/client-side-params/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/client-side-params/tests/utils/test_http_client.py b/seed/python-sdk/client-side-params/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/client-side-params/tests/utils/test_http_client.py +++ b/seed/python-sdk/client-side-params/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/content-type/src/seed/core/http_client.py b/seed/python-sdk/content-type/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/content-type/src/seed/core/http_client.py +++ b/seed/python-sdk/content-type/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/content-type/tests/utils/test_http_client.py b/seed/python-sdk/content-type/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/content-type/tests/utils/test_http_client.py +++ b/seed/python-sdk/content-type/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/cross-package-type-names/src/seed/core/http_client.py b/seed/python-sdk/cross-package-type-names/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/cross-package-type-names/src/seed/core/http_client.py +++ b/seed/python-sdk/cross-package-type-names/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/cross-package-type-names/tests/utils/test_http_client.py b/seed/python-sdk/cross-package-type-names/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/cross-package-type-names/tests/utils/test_http_client.py +++ b/seed/python-sdk/cross-package-type-names/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/empty-clients/src/seed/core/http_client.py b/seed/python-sdk/empty-clients/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/empty-clients/src/seed/core/http_client.py +++ b/seed/python-sdk/empty-clients/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/empty-clients/tests/utils/test_http_client.py b/seed/python-sdk/empty-clients/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/empty-clients/tests/utils/test_http_client.py +++ b/seed/python-sdk/empty-clients/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/enum/no-custom-config/src/seed/core/http_client.py b/seed/python-sdk/enum/no-custom-config/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/enum/no-custom-config/src/seed/core/http_client.py +++ b/seed/python-sdk/enum/no-custom-config/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/enum/no-custom-config/tests/utils/test_http_client.py b/seed/python-sdk/enum/no-custom-config/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/enum/no-custom-config/tests/utils/test_http_client.py +++ b/seed/python-sdk/enum/no-custom-config/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/enum/real-enum-forward-compat/src/seed/core/http_client.py b/seed/python-sdk/enum/real-enum-forward-compat/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/enum/real-enum-forward-compat/src/seed/core/http_client.py +++ b/seed/python-sdk/enum/real-enum-forward-compat/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/enum/real-enum-forward-compat/tests/utils/test_http_client.py b/seed/python-sdk/enum/real-enum-forward-compat/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/enum/real-enum-forward-compat/tests/utils/test_http_client.py +++ b/seed/python-sdk/enum/real-enum-forward-compat/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/enum/real-enum/src/seed/core/http_client.py b/seed/python-sdk/enum/real-enum/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/enum/real-enum/src/seed/core/http_client.py +++ b/seed/python-sdk/enum/real-enum/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/enum/real-enum/tests/utils/test_http_client.py b/seed/python-sdk/enum/real-enum/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/enum/real-enum/tests/utils/test_http_client.py +++ b/seed/python-sdk/enum/real-enum/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/enum/strenum/src/seed/core/http_client.py b/seed/python-sdk/enum/strenum/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/enum/strenum/src/seed/core/http_client.py +++ b/seed/python-sdk/enum/strenum/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/enum/strenum/tests/utils/test_http_client.py b/seed/python-sdk/enum/strenum/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/enum/strenum/tests/utils/test_http_client.py +++ b/seed/python-sdk/enum/strenum/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/error-property/src/seed/core/http_client.py b/seed/python-sdk/error-property/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/error-property/src/seed/core/http_client.py +++ b/seed/python-sdk/error-property/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/error-property/tests/utils/test_http_client.py b/seed/python-sdk/error-property/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/error-property/tests/utils/test_http_client.py +++ b/seed/python-sdk/error-property/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/errors/src/seed/core/http_client.py b/seed/python-sdk/errors/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/errors/src/seed/core/http_client.py +++ b/seed/python-sdk/errors/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/errors/tests/utils/test_http_client.py b/seed/python-sdk/errors/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/errors/tests/utils/test_http_client.py +++ b/seed/python-sdk/errors/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/examples/client-filename/src/seed/core/http_client.py b/seed/python-sdk/examples/client-filename/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/examples/client-filename/src/seed/core/http_client.py +++ b/seed/python-sdk/examples/client-filename/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/examples/client-filename/tests/utils/test_http_client.py b/seed/python-sdk/examples/client-filename/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/examples/client-filename/tests/utils/test_http_client.py +++ b/seed/python-sdk/examples/client-filename/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/examples/legacy-wire-tests/src/seed/core/http_client.py b/seed/python-sdk/examples/legacy-wire-tests/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/examples/legacy-wire-tests/src/seed/core/http_client.py +++ b/seed/python-sdk/examples/legacy-wire-tests/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/examples/legacy-wire-tests/tests/utils/test_http_client.py b/seed/python-sdk/examples/legacy-wire-tests/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/examples/legacy-wire-tests/tests/utils/test_http_client.py +++ b/seed/python-sdk/examples/legacy-wire-tests/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/examples/no-custom-config/src/seed/core/http_client.py b/seed/python-sdk/examples/no-custom-config/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/examples/no-custom-config/src/seed/core/http_client.py +++ b/seed/python-sdk/examples/no-custom-config/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/examples/no-custom-config/tests/utils/test_http_client.py b/seed/python-sdk/examples/no-custom-config/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/examples/no-custom-config/tests/utils/test_http_client.py +++ b/seed/python-sdk/examples/no-custom-config/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/examples/readme/src/seed/core/http_client.py b/seed/python-sdk/examples/readme/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/examples/readme/src/seed/core/http_client.py +++ b/seed/python-sdk/examples/readme/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/examples/readme/tests/utils/test_http_client.py b/seed/python-sdk/examples/readme/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/examples/readme/tests/utils/test_http_client.py +++ b/seed/python-sdk/examples/readme/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/exhaustive/additional_init_exports/src/seed/core/http_client.py b/seed/python-sdk/exhaustive/additional_init_exports/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/exhaustive/additional_init_exports/src/seed/core/http_client.py +++ b/seed/python-sdk/exhaustive/additional_init_exports/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/exhaustive/additional_init_exports/tests/utils/test_http_client.py b/seed/python-sdk/exhaustive/additional_init_exports/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/exhaustive/additional_init_exports/tests/utils/test_http_client.py +++ b/seed/python-sdk/exhaustive/additional_init_exports/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/exhaustive/aliases_with_validation/src/seed/core/http_client.py b/seed/python-sdk/exhaustive/aliases_with_validation/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/exhaustive/aliases_with_validation/src/seed/core/http_client.py +++ b/seed/python-sdk/exhaustive/aliases_with_validation/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/exhaustive/aliases_with_validation/tests/utils/test_http_client.py b/seed/python-sdk/exhaustive/aliases_with_validation/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/exhaustive/aliases_with_validation/tests/utils/test_http_client.py +++ b/seed/python-sdk/exhaustive/aliases_with_validation/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/exhaustive/aliases_without_validation/src/seed/core/http_client.py b/seed/python-sdk/exhaustive/aliases_without_validation/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/exhaustive/aliases_without_validation/src/seed/core/http_client.py +++ b/seed/python-sdk/exhaustive/aliases_without_validation/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/exhaustive/aliases_without_validation/tests/utils/test_http_client.py b/seed/python-sdk/exhaustive/aliases_without_validation/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/exhaustive/aliases_without_validation/tests/utils/test_http_client.py +++ b/seed/python-sdk/exhaustive/aliases_without_validation/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/exhaustive/eager-imports/src/seed/core/http_client.py b/seed/python-sdk/exhaustive/eager-imports/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/exhaustive/eager-imports/src/seed/core/http_client.py +++ b/seed/python-sdk/exhaustive/eager-imports/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/exhaustive/eager-imports/tests/utils/test_http_client.py b/seed/python-sdk/exhaustive/eager-imports/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/exhaustive/eager-imports/tests/utils/test_http_client.py +++ b/seed/python-sdk/exhaustive/eager-imports/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/exhaustive/extra_dependencies/src/seed/core/http_client.py b/seed/python-sdk/exhaustive/extra_dependencies/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/exhaustive/extra_dependencies/src/seed/core/http_client.py +++ b/seed/python-sdk/exhaustive/extra_dependencies/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/exhaustive/extra_dependencies/tests/utils/test_http_client.py b/seed/python-sdk/exhaustive/extra_dependencies/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/exhaustive/extra_dependencies/tests/utils/test_http_client.py +++ b/seed/python-sdk/exhaustive/extra_dependencies/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/exhaustive/extra_dev_dependencies/src/seed/core/http_client.py b/seed/python-sdk/exhaustive/extra_dev_dependencies/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/exhaustive/extra_dev_dependencies/src/seed/core/http_client.py +++ b/seed/python-sdk/exhaustive/extra_dev_dependencies/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/exhaustive/extra_dev_dependencies/tests/utils/test_http_client.py b/seed/python-sdk/exhaustive/extra_dev_dependencies/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/exhaustive/extra_dev_dependencies/tests/utils/test_http_client.py +++ b/seed/python-sdk/exhaustive/extra_dev_dependencies/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/exhaustive/five-second-timeout/src/seed/core/http_client.py b/seed/python-sdk/exhaustive/five-second-timeout/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/exhaustive/five-second-timeout/src/seed/core/http_client.py +++ b/seed/python-sdk/exhaustive/five-second-timeout/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/exhaustive/five-second-timeout/tests/utils/test_http_client.py b/seed/python-sdk/exhaustive/five-second-timeout/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/exhaustive/five-second-timeout/tests/utils/test_http_client.py +++ b/seed/python-sdk/exhaustive/five-second-timeout/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/exhaustive/follow_redirects_by_default/src/seed/core/http_client.py b/seed/python-sdk/exhaustive/follow_redirects_by_default/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/exhaustive/follow_redirects_by_default/src/seed/core/http_client.py +++ b/seed/python-sdk/exhaustive/follow_redirects_by_default/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/exhaustive/follow_redirects_by_default/tests/utils/test_http_client.py b/seed/python-sdk/exhaustive/follow_redirects_by_default/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/exhaustive/follow_redirects_by_default/tests/utils/test_http_client.py +++ b/seed/python-sdk/exhaustive/follow_redirects_by_default/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/exhaustive/improved_imports/src/seed/core/http_client.py b/seed/python-sdk/exhaustive/improved_imports/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/exhaustive/improved_imports/src/seed/core/http_client.py +++ b/seed/python-sdk/exhaustive/improved_imports/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/exhaustive/improved_imports/tests/utils/test_http_client.py b/seed/python-sdk/exhaustive/improved_imports/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/exhaustive/improved_imports/tests/utils/test_http_client.py +++ b/seed/python-sdk/exhaustive/improved_imports/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/exhaustive/infinite-timeout/src/seed/core/http_client.py b/seed/python-sdk/exhaustive/infinite-timeout/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/exhaustive/infinite-timeout/src/seed/core/http_client.py +++ b/seed/python-sdk/exhaustive/infinite-timeout/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/exhaustive/infinite-timeout/tests/utils/test_http_client.py b/seed/python-sdk/exhaustive/infinite-timeout/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/exhaustive/infinite-timeout/tests/utils/test_http_client.py +++ b/seed/python-sdk/exhaustive/infinite-timeout/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/exhaustive/inline-path-params/src/seed/core/http_client.py b/seed/python-sdk/exhaustive/inline-path-params/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/exhaustive/inline-path-params/src/seed/core/http_client.py +++ b/seed/python-sdk/exhaustive/inline-path-params/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/exhaustive/inline-path-params/tests/utils/test_http_client.py b/seed/python-sdk/exhaustive/inline-path-params/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/exhaustive/inline-path-params/tests/utils/test_http_client.py +++ b/seed/python-sdk/exhaustive/inline-path-params/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/exhaustive/inline_request_params/src/seed/core/http_client.py b/seed/python-sdk/exhaustive/inline_request_params/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/exhaustive/inline_request_params/src/seed/core/http_client.py +++ b/seed/python-sdk/exhaustive/inline_request_params/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/exhaustive/inline_request_params/tests/utils/test_http_client.py b/seed/python-sdk/exhaustive/inline_request_params/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/exhaustive/inline_request_params/tests/utils/test_http_client.py +++ b/seed/python-sdk/exhaustive/inline_request_params/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/exhaustive/no-custom-config/src/seed/core/http_client.py b/seed/python-sdk/exhaustive/no-custom-config/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/exhaustive/no-custom-config/src/seed/core/http_client.py +++ b/seed/python-sdk/exhaustive/no-custom-config/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/exhaustive/no-custom-config/tests/utils/test_http_client.py b/seed/python-sdk/exhaustive/no-custom-config/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/exhaustive/no-custom-config/tests/utils/test_http_client.py +++ b/seed/python-sdk/exhaustive/no-custom-config/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/exhaustive/package-path/src/seed/matryoshka/doll/structure/core/http_client.py b/seed/python-sdk/exhaustive/package-path/src/seed/matryoshka/doll/structure/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/exhaustive/package-path/src/seed/matryoshka/doll/structure/core/http_client.py +++ b/seed/python-sdk/exhaustive/package-path/src/seed/matryoshka/doll/structure/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/exhaustive/package-path/tests/utils/test_http_client.py b/seed/python-sdk/exhaustive/package-path/tests/utils/test_http_client.py index 89dbbe6129d0..cbb7e3692dab 100644 --- a/seed/python-sdk/exhaustive/package-path/tests/utils/test_http_client.py +++ b/seed/python-sdk/exhaustive/package-path/tests/utils/test_http_client.py @@ -1,9 +1,48 @@ # This file was auto-generated by Fern from our API Definition. -from seed.matryoshka.doll.structure.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.matryoshka.doll.structure.core.http_client import ( + AsyncHttpClient, + HttpClient, + get_request_body, + remove_none_from_dict, +) from seed.matryoshka.doll.structure.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +146,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/exhaustive/pydantic-extra-fields/src/seed/core/http_client.py b/seed/python-sdk/exhaustive/pydantic-extra-fields/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/exhaustive/pydantic-extra-fields/src/seed/core/http_client.py +++ b/seed/python-sdk/exhaustive/pydantic-extra-fields/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/exhaustive/pydantic-extra-fields/tests/utils/test_http_client.py b/seed/python-sdk/exhaustive/pydantic-extra-fields/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/exhaustive/pydantic-extra-fields/tests/utils/test_http_client.py +++ b/seed/python-sdk/exhaustive/pydantic-extra-fields/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/exhaustive/pydantic-ignore-fields/src/seed/core/http_client.py b/seed/python-sdk/exhaustive/pydantic-ignore-fields/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/exhaustive/pydantic-ignore-fields/src/seed/core/http_client.py +++ b/seed/python-sdk/exhaustive/pydantic-ignore-fields/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/exhaustive/pydantic-ignore-fields/tests/utils/test_http_client.py b/seed/python-sdk/exhaustive/pydantic-ignore-fields/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/exhaustive/pydantic-ignore-fields/tests/utils/test_http_client.py +++ b/seed/python-sdk/exhaustive/pydantic-ignore-fields/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/exhaustive/pydantic-v1-with-utils/src/seed/core/http_client.py b/seed/python-sdk/exhaustive/pydantic-v1-with-utils/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/exhaustive/pydantic-v1-with-utils/src/seed/core/http_client.py +++ b/seed/python-sdk/exhaustive/pydantic-v1-with-utils/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/exhaustive/pydantic-v1-with-utils/tests/utils/test_http_client.py b/seed/python-sdk/exhaustive/pydantic-v1-with-utils/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/exhaustive/pydantic-v1-with-utils/tests/utils/test_http_client.py +++ b/seed/python-sdk/exhaustive/pydantic-v1-with-utils/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/exhaustive/pydantic-v1-wrapped/src/seed/core/http_client.py b/seed/python-sdk/exhaustive/pydantic-v1-wrapped/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/exhaustive/pydantic-v1-wrapped/src/seed/core/http_client.py +++ b/seed/python-sdk/exhaustive/pydantic-v1-wrapped/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/exhaustive/pydantic-v1-wrapped/tests/utils/test_http_client.py b/seed/python-sdk/exhaustive/pydantic-v1-wrapped/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/exhaustive/pydantic-v1-wrapped/tests/utils/test_http_client.py +++ b/seed/python-sdk/exhaustive/pydantic-v1-wrapped/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/exhaustive/pydantic-v1/src/seed/core/http_client.py b/seed/python-sdk/exhaustive/pydantic-v1/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/exhaustive/pydantic-v1/src/seed/core/http_client.py +++ b/seed/python-sdk/exhaustive/pydantic-v1/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/exhaustive/pydantic-v1/tests/utils/test_http_client.py b/seed/python-sdk/exhaustive/pydantic-v1/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/exhaustive/pydantic-v1/tests/utils/test_http_client.py +++ b/seed/python-sdk/exhaustive/pydantic-v1/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/exhaustive/pydantic-v2-wrapped/src/seed/core/http_client.py b/seed/python-sdk/exhaustive/pydantic-v2-wrapped/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/exhaustive/pydantic-v2-wrapped/src/seed/core/http_client.py +++ b/seed/python-sdk/exhaustive/pydantic-v2-wrapped/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/exhaustive/pydantic-v2-wrapped/tests/utils/test_http_client.py b/seed/python-sdk/exhaustive/pydantic-v2-wrapped/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/exhaustive/pydantic-v2-wrapped/tests/utils/test_http_client.py +++ b/seed/python-sdk/exhaustive/pydantic-v2-wrapped/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/exhaustive/pyproject_extras/src/seed/core/http_client.py b/seed/python-sdk/exhaustive/pyproject_extras/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/exhaustive/pyproject_extras/src/seed/core/http_client.py +++ b/seed/python-sdk/exhaustive/pyproject_extras/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/exhaustive/pyproject_extras/tests/utils/test_http_client.py b/seed/python-sdk/exhaustive/pyproject_extras/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/exhaustive/pyproject_extras/tests/utils/test_http_client.py +++ b/seed/python-sdk/exhaustive/pyproject_extras/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/exhaustive/skip-pydantic-validation/src/seed/core/http_client.py b/seed/python-sdk/exhaustive/skip-pydantic-validation/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/exhaustive/skip-pydantic-validation/src/seed/core/http_client.py +++ b/seed/python-sdk/exhaustive/skip-pydantic-validation/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/exhaustive/skip-pydantic-validation/tests/utils/test_http_client.py b/seed/python-sdk/exhaustive/skip-pydantic-validation/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/exhaustive/skip-pydantic-validation/tests/utils/test_http_client.py +++ b/seed/python-sdk/exhaustive/skip-pydantic-validation/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/exhaustive/union-utils/src/seed/core/http_client.py b/seed/python-sdk/exhaustive/union-utils/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/exhaustive/union-utils/src/seed/core/http_client.py +++ b/seed/python-sdk/exhaustive/union-utils/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/exhaustive/union-utils/tests/utils/test_http_client.py b/seed/python-sdk/exhaustive/union-utils/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/exhaustive/union-utils/tests/utils/test_http_client.py +++ b/seed/python-sdk/exhaustive/union-utils/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/extends/src/seed/core/http_client.py b/seed/python-sdk/extends/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/extends/src/seed/core/http_client.py +++ b/seed/python-sdk/extends/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/extends/tests/utils/test_http_client.py b/seed/python-sdk/extends/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/extends/tests/utils/test_http_client.py +++ b/seed/python-sdk/extends/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/extra-properties/src/seed/core/http_client.py b/seed/python-sdk/extra-properties/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/extra-properties/src/seed/core/http_client.py +++ b/seed/python-sdk/extra-properties/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/extra-properties/tests/utils/test_http_client.py b/seed/python-sdk/extra-properties/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/extra-properties/tests/utils/test_http_client.py +++ b/seed/python-sdk/extra-properties/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/file-download/default-chunk-size/src/seed/core/http_client.py b/seed/python-sdk/file-download/default-chunk-size/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/file-download/default-chunk-size/src/seed/core/http_client.py +++ b/seed/python-sdk/file-download/default-chunk-size/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/file-download/default-chunk-size/tests/utils/test_http_client.py b/seed/python-sdk/file-download/default-chunk-size/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/file-download/default-chunk-size/tests/utils/test_http_client.py +++ b/seed/python-sdk/file-download/default-chunk-size/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/file-download/no-custom-config/src/seed/core/http_client.py b/seed/python-sdk/file-download/no-custom-config/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/file-download/no-custom-config/src/seed/core/http_client.py +++ b/seed/python-sdk/file-download/no-custom-config/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/file-download/no-custom-config/tests/utils/test_http_client.py b/seed/python-sdk/file-download/no-custom-config/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/file-download/no-custom-config/tests/utils/test_http_client.py +++ b/seed/python-sdk/file-download/no-custom-config/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/file-upload-openapi/src/seed/core/http_client.py b/seed/python-sdk/file-upload-openapi/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/file-upload-openapi/src/seed/core/http_client.py +++ b/seed/python-sdk/file-upload-openapi/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/file-upload-openapi/tests/utils/test_http_client.py b/seed/python-sdk/file-upload-openapi/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/file-upload-openapi/tests/utils/test_http_client.py +++ b/seed/python-sdk/file-upload-openapi/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/file-upload/exclude_types_from_init_exports/src/seed/core/http_client.py b/seed/python-sdk/file-upload/exclude_types_from_init_exports/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/file-upload/exclude_types_from_init_exports/src/seed/core/http_client.py +++ b/seed/python-sdk/file-upload/exclude_types_from_init_exports/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/file-upload/exclude_types_from_init_exports/tests/utils/test_http_client.py b/seed/python-sdk/file-upload/exclude_types_from_init_exports/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/file-upload/exclude_types_from_init_exports/tests/utils/test_http_client.py +++ b/seed/python-sdk/file-upload/exclude_types_from_init_exports/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/file-upload/no-custom-config/src/seed/core/http_client.py b/seed/python-sdk/file-upload/no-custom-config/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/file-upload/no-custom-config/src/seed/core/http_client.py +++ b/seed/python-sdk/file-upload/no-custom-config/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/file-upload/no-custom-config/tests/utils/test_http_client.py b/seed/python-sdk/file-upload/no-custom-config/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/file-upload/no-custom-config/tests/utils/test_http_client.py +++ b/seed/python-sdk/file-upload/no-custom-config/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/file-upload/use_typeddict_requests/src/seed/core/http_client.py b/seed/python-sdk/file-upload/use_typeddict_requests/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/file-upload/use_typeddict_requests/src/seed/core/http_client.py +++ b/seed/python-sdk/file-upload/use_typeddict_requests/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/file-upload/use_typeddict_requests/tests/utils/test_http_client.py b/seed/python-sdk/file-upload/use_typeddict_requests/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/file-upload/use_typeddict_requests/tests/utils/test_http_client.py +++ b/seed/python-sdk/file-upload/use_typeddict_requests/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/folders/src/seed/core/http_client.py b/seed/python-sdk/folders/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/folders/src/seed/core/http_client.py +++ b/seed/python-sdk/folders/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/folders/tests/utils/test_http_client.py b/seed/python-sdk/folders/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/folders/tests/utils/test_http_client.py +++ b/seed/python-sdk/folders/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/header-auth-environment-variable/src/seed/core/http_client.py b/seed/python-sdk/header-auth-environment-variable/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/header-auth-environment-variable/src/seed/core/http_client.py +++ b/seed/python-sdk/header-auth-environment-variable/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/header-auth-environment-variable/tests/utils/test_http_client.py b/seed/python-sdk/header-auth-environment-variable/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/header-auth-environment-variable/tests/utils/test_http_client.py +++ b/seed/python-sdk/header-auth-environment-variable/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/header-auth/src/seed/core/http_client.py b/seed/python-sdk/header-auth/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/header-auth/src/seed/core/http_client.py +++ b/seed/python-sdk/header-auth/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/header-auth/tests/utils/test_http_client.py b/seed/python-sdk/header-auth/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/header-auth/tests/utils/test_http_client.py +++ b/seed/python-sdk/header-auth/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/http-head/src/seed/core/http_client.py b/seed/python-sdk/http-head/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/http-head/src/seed/core/http_client.py +++ b/seed/python-sdk/http-head/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/http-head/tests/utils/test_http_client.py b/seed/python-sdk/http-head/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/http-head/tests/utils/test_http_client.py +++ b/seed/python-sdk/http-head/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/idempotency-headers/src/seed/core/http_client.py b/seed/python-sdk/idempotency-headers/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/idempotency-headers/src/seed/core/http_client.py +++ b/seed/python-sdk/idempotency-headers/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/idempotency-headers/tests/utils/test_http_client.py b/seed/python-sdk/idempotency-headers/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/idempotency-headers/tests/utils/test_http_client.py +++ b/seed/python-sdk/idempotency-headers/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/imdb/src/seed/core/http_client.py b/seed/python-sdk/imdb/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/imdb/src/seed/core/http_client.py +++ b/seed/python-sdk/imdb/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/imdb/tests/utils/test_http_client.py b/seed/python-sdk/imdb/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/imdb/tests/utils/test_http_client.py +++ b/seed/python-sdk/imdb/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/inferred-auth-explicit/src/seed/core/http_client.py b/seed/python-sdk/inferred-auth-explicit/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/inferred-auth-explicit/src/seed/core/http_client.py +++ b/seed/python-sdk/inferred-auth-explicit/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/inferred-auth-explicit/tests/utils/test_http_client.py b/seed/python-sdk/inferred-auth-explicit/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/inferred-auth-explicit/tests/utils/test_http_client.py +++ b/seed/python-sdk/inferred-auth-explicit/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/.fern/metadata.json b/seed/python-sdk/inferred-auth-implicit-api-key/.fern/metadata.json new file mode 100644 index 000000000000..623048546df1 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/.fern/metadata.json @@ -0,0 +1,5 @@ +{ + "cliVersion": "DUMMY", + "generatorName": "fernapi/fern-python-sdk", + "generatorVersion": "latest" +} \ No newline at end of file diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/.github/workflows/ci.yml b/seed/python-sdk/inferred-auth-implicit-api-key/.github/workflows/ci.yml new file mode 100644 index 000000000000..ffd2d8acab24 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/.github/workflows/ci.yml @@ -0,0 +1,60 @@ +name: ci +on: [push] +jobs: + compile: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + - name: Set up python + uses: actions/setup-python@v4 + with: + python-version: 3.9 + - name: Bootstrap poetry + run: | + curl -sSL https://install.python-poetry.org | python - -y --version 1.5.1 + - name: Install dependencies + run: poetry install + - name: Compile + run: poetry run mypy . + test: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + - name: Set up python + uses: actions/setup-python@v4 + with: + python-version: 3.9 + - name: Bootstrap poetry + run: | + curl -sSL https://install.python-poetry.org | python - -y --version 1.5.1 + - name: Install dependencies + run: poetry install + + - name: Test + run: poetry run pytest -rP -n auto . + + publish: + needs: [compile, test] + if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + - name: Set up python + uses: actions/setup-python@v4 + with: + python-version: 3.9 + - name: Bootstrap poetry + run: | + curl -sSL https://install.python-poetry.org | python - -y --version 1.5.1 + - name: Install dependencies + run: poetry install + - name: Publish to pypi + run: | + poetry config repositories.remote + poetry --no-interaction -v publish --build --repository remote --username "$PYPI_USERNAME" --password "$PYPI_PASSWORD" + env: + PYPI_USERNAME: ${{ secrets.PYPI_USERNAME }} + PYPI_PASSWORD: ${{ secrets.PYPI_PASSWORD }} diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/.gitignore b/seed/python-sdk/inferred-auth-implicit-api-key/.gitignore new file mode 100644 index 000000000000..d2e4ca808d21 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/.gitignore @@ -0,0 +1,5 @@ +.mypy_cache/ +.ruff_cache/ +__pycache__/ +dist/ +poetry.toml diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/README.md b/seed/python-sdk/inferred-auth-implicit-api-key/README.md new file mode 100644 index 000000000000..90c3f28158b3 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/README.md @@ -0,0 +1,169 @@ +# Seed Python Library + +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) +[![pypi](https://img.shields.io/pypi/v/fern_inferred-auth-implicit-api-key)](https://pypi.python.org/pypi/fern_inferred-auth-implicit-api-key) + +The Seed Python library provides convenient access to the Seed APIs from Python. + +## Table of Contents + +- [Installation](#installation) +- [Reference](#reference) +- [Usage](#usage) +- [Async Client](#async-client) +- [Exception Handling](#exception-handling) +- [Advanced](#advanced) + - [Access Raw Response Data](#access-raw-response-data) + - [Retries](#retries) + - [Timeouts](#timeouts) + - [Custom Client](#custom-client) +- [Contributing](#contributing) + +## Installation + +```sh +pip install fern_inferred-auth-implicit-api-key +``` + +## Reference + +A full reference for this library is available [here](./reference.md). + +## Usage + +Instantiate and use the client with the following: + +```python +from seed import SeedInferredAuthImplicitApiKey + +client = SeedInferredAuthImplicitApiKey( + base_url="https://yourhost.com/path/to/api", +) +client.auth.get_token( + api_key="api_key", +) +``` + +## Async Client + +The SDK also exports an `async` client so that you can make non-blocking calls to our API. Note that if you are constructing an Async httpx client class to pass into this client, use `httpx.AsyncClient()` instead of `httpx.Client()` (e.g. for the `httpx_client` parameter of this client). + +```python +import asyncio + +from seed import AsyncSeedInferredAuthImplicitApiKey + +client = AsyncSeedInferredAuthImplicitApiKey( + base_url="https://yourhost.com/path/to/api", +) + + +async def main() -> None: + await client.auth.get_token( + api_key="api_key", + ) + + +asyncio.run(main()) +``` + +## Exception Handling + +When the API returns a non-success status code (4xx or 5xx response), a subclass of the following error +will be thrown. + +```python +from seed.core.api_error import ApiError + +try: + client.auth.get_token(...) +except ApiError as e: + print(e.status_code) + print(e.body) +``` + +## Advanced + +### Access Raw Response Data + +The SDK provides access to raw response data, including headers, through the `.with_raw_response` property. +The `.with_raw_response` property returns a "raw" client that can be used to access the `.headers` and `.data` attributes. + +```python +from seed import SeedInferredAuthImplicitApiKey + +client = SeedInferredAuthImplicitApiKey( + ..., +) +response = client.auth.with_raw_response.get_token(...) +print(response.headers) # access the response headers +print(response.data) # access the underlying object +``` + +### Retries + +The SDK is instrumented with automatic retries with exponential backoff. A request will be retried as long +as the request is deemed retryable and the number of retry attempts has not grown larger than the configured +retry limit (default: 2). + +A request is deemed retryable when any of the following HTTP status codes is returned: + +- [408](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/408) (Timeout) +- [429](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429) (Too Many Requests) +- [5XX](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500) (Internal Server Errors) + +Use the `max_retries` request option to configure this behavior. + +```python +client.auth.get_token(..., request_options={ + "max_retries": 1 +}) +``` + +### Timeouts + +The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. + +```python + +from seed import SeedInferredAuthImplicitApiKey + +client = SeedInferredAuthImplicitApiKey( + ..., + timeout=20.0, +) + + +# Override timeout for a specific method +client.auth.get_token(..., request_options={ + "timeout_in_seconds": 1 +}) +``` + +### Custom Client + +You can override the `httpx` client to customize it for your use-case. Some common use-cases include support for proxies +and transports. + +```python +import httpx +from seed import SeedInferredAuthImplicitApiKey + +client = SeedInferredAuthImplicitApiKey( + ..., + httpx_client=httpx.Client( + proxy="http://my.test.proxy.example.com", + transport=httpx.HTTPTransport(local_address="0.0.0.0"), + ), +) +``` + +## Contributing + +While we value open-source contributions to this SDK, this library is generated programmatically. +Additions made directly to this library would have to be moved over to our generation code, +otherwise they would be overwritten upon the next generated release. Feel free to open a PR as +a proof of concept, but know that we will not be able to merge it as-is. We suggest opening +an issue first to discuss with us! + +On the other hand, contributions to the README are always very welcome! diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/poetry.lock b/seed/python-sdk/inferred-auth-implicit-api-key/poetry.lock new file mode 100644 index 000000000000..9decbf87e177 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/poetry.lock @@ -0,0 +1,594 @@ +# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. + +[[package]] +name = "annotated-types" +version = "0.7.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +files = [ + {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, + {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.9\""} + +[[package]] +name = "anyio" +version = "4.5.2" +description = "High level compatibility layer for multiple asynchronous event loop implementations" +optional = false +python-versions = ">=3.8" +files = [ + {file = "anyio-4.5.2-py3-none-any.whl", hash = "sha256:c011ee36bc1e8ba40e5a81cb9df91925c218fe9b778554e0b56a21e1b5d4716f"}, + {file = "anyio-4.5.2.tar.gz", hash = "sha256:23009af4ed04ce05991845451e11ef02fc7c5ed29179ac9a420e5ad0ac7ddc5b"}, +] + +[package.dependencies] +exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} +idna = ">=2.8" +sniffio = ">=1.1" +typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""} + +[package.extras] +doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21.0b1)"] +trio = ["trio (>=0.26.1)"] + +[[package]] +name = "certifi" +version = "2025.11.12" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.7" +files = [ + {file = "certifi-2025.11.12-py3-none-any.whl", hash = "sha256:97de8790030bbd5c2d96b7ec782fc2f7820ef8dba6db909ccf95449f2d062d4b"}, + {file = "certifi-2025.11.12.tar.gz", hash = "sha256:d8ab5478f2ecd78af242878415affce761ca6bc54a22a27e026d7c25357c3316"}, +] + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "exceptiongroup" +version = "1.3.1" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598"}, + {file = "exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""} + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "execnet" +version = "2.1.2" +description = "execnet: rapid multi-Python deployment" +optional = false +python-versions = ">=3.8" +files = [ + {file = "execnet-2.1.2-py3-none-any.whl", hash = "sha256:67fba928dd5a544b783f6056f449e5e3931a5c378b128bc18501f7ea79e296ec"}, + {file = "execnet-2.1.2.tar.gz", hash = "sha256:63d83bfdd9a23e35b9c6a3261412324f964c2ec8dcd8d3c6916ee9373e0befcd"}, +] + +[package.extras] +testing = ["hatch", "pre-commit", "pytest", "tox"] + +[[package]] +name = "h11" +version = "0.16.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +optional = false +python-versions = ">=3.8" +files = [ + {file = "h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86"}, + {file = "h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1"}, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +description = "A minimal low-level HTTP client." +optional = false +python-versions = ">=3.8" +files = [ + {file = "httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55"}, + {file = "httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8"}, +] + +[package.dependencies] +certifi = "*" +h11 = ">=0.16" + +[package.extras] +asyncio = ["anyio (>=4.0,<5.0)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] +trio = ["trio (>=0.22.0,<1.0)"] + +[[package]] +name = "httpx" +version = "0.28.1" +description = "The next generation HTTP client." +optional = false +python-versions = ">=3.8" +files = [ + {file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"}, + {file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"}, +] + +[package.dependencies] +anyio = "*" +certifi = "*" +httpcore = "==1.*" +idna = "*" + +[package.extras] +brotli = ["brotli", "brotlicffi"] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "idna" +version = "3.11" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea"}, + {file = "idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902"}, +] + +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + +[[package]] +name = "iniconfig" +version = "2.1.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.8" +files = [ + {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, + {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, +] + +[[package]] +name = "mypy" +version = "1.13.0" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mypy-1.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6607e0f1dd1fb7f0aca14d936d13fd19eba5e17e1cd2a14f808fa5f8f6d8f60a"}, + {file = "mypy-1.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8a21be69bd26fa81b1f80a61ee7ab05b076c674d9b18fb56239d72e21d9f4c80"}, + {file = "mypy-1.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b2353a44d2179846a096e25691d54d59904559f4232519d420d64da6828a3a7"}, + {file = "mypy-1.13.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0730d1c6a2739d4511dc4253f8274cdd140c55c32dfb0a4cf8b7a43f40abfa6f"}, + {file = "mypy-1.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:c5fc54dbb712ff5e5a0fca797e6e0aa25726c7e72c6a5850cfd2adbc1eb0a372"}, + {file = "mypy-1.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:581665e6f3a8a9078f28d5502f4c334c0c8d802ef55ea0e7276a6e409bc0d82d"}, + {file = "mypy-1.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3ddb5b9bf82e05cc9a627e84707b528e5c7caaa1c55c69e175abb15a761cec2d"}, + {file = "mypy-1.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20c7ee0bc0d5a9595c46f38beb04201f2620065a93755704e141fcac9f59db2b"}, + {file = "mypy-1.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3790ded76f0b34bc9c8ba4def8f919dd6a46db0f5a6610fb994fe8efdd447f73"}, + {file = "mypy-1.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:51f869f4b6b538229c1d1bcc1dd7d119817206e2bc54e8e374b3dfa202defcca"}, + {file = "mypy-1.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5c7051a3461ae84dfb5dd15eff5094640c61c5f22257c8b766794e6dd85e72d5"}, + {file = "mypy-1.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:39bb21c69a5d6342f4ce526e4584bc5c197fd20a60d14a8624d8743fffb9472e"}, + {file = "mypy-1.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:164f28cb9d6367439031f4c81e84d3ccaa1e19232d9d05d37cb0bd880d3f93c2"}, + {file = "mypy-1.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a4c1bfcdbce96ff5d96fc9b08e3831acb30dc44ab02671eca5953eadad07d6d0"}, + {file = "mypy-1.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:a0affb3a79a256b4183ba09811e3577c5163ed06685e4d4b46429a271ba174d2"}, + {file = "mypy-1.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a7b44178c9760ce1a43f544e595d35ed61ac2c3de306599fa59b38a6048e1aa7"}, + {file = "mypy-1.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5d5092efb8516d08440e36626f0153b5006d4088c1d663d88bf79625af3d1d62"}, + {file = "mypy-1.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2904956dac40ced10931ac967ae63c5089bd498542194b436eb097a9f77bc8"}, + {file = "mypy-1.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:7bfd8836970d33c2105562650656b6846149374dc8ed77d98424b40b09340ba7"}, + {file = "mypy-1.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:9f73dba9ec77acb86457a8fc04b5239822df0c14a082564737833d2963677dbc"}, + {file = "mypy-1.13.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:100fac22ce82925f676a734af0db922ecfea991e1d7ec0ceb1e115ebe501301a"}, + {file = "mypy-1.13.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7bcb0bb7f42a978bb323a7c88f1081d1b5dee77ca86f4100735a6f541299d8fb"}, + {file = "mypy-1.13.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bde31fc887c213e223bbfc34328070996061b0833b0a4cfec53745ed61f3519b"}, + {file = "mypy-1.13.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:07de989f89786f62b937851295ed62e51774722e5444a27cecca993fc3f9cd74"}, + {file = "mypy-1.13.0-cp38-cp38-win_amd64.whl", hash = "sha256:4bde84334fbe19bad704b3f5b78c4abd35ff1026f8ba72b29de70dda0916beb6"}, + {file = "mypy-1.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0246bcb1b5de7f08f2826451abd947bf656945209b140d16ed317f65a17dc7dc"}, + {file = "mypy-1.13.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7f5b7deae912cf8b77e990b9280f170381fdfbddf61b4ef80927edd813163732"}, + {file = "mypy-1.13.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7029881ec6ffb8bc233a4fa364736789582c738217b133f1b55967115288a2bc"}, + {file = "mypy-1.13.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3e38b980e5681f28f033f3be86b099a247b13c491f14bb8b1e1e134d23bb599d"}, + {file = "mypy-1.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:a6789be98a2017c912ae6ccb77ea553bbaf13d27605d2ca20a76dfbced631b24"}, + {file = "mypy-1.13.0-py3-none-any.whl", hash = "sha256:9c250883f9fd81d212e0952c92dbfcc96fc237f4b7c92f56ac81fd48460b3e5a"}, + {file = "mypy-1.13.0.tar.gz", hash = "sha256:0291a61b6fbf3e6673e3405cfcc0e7650bebc7939659fdca2702958038bd835e"}, +] + +[package.dependencies] +mypy-extensions = ">=1.0.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = ">=4.6.0" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +faster-cache = ["orjson"] +install-types = ["pip"] +mypyc = ["setuptools (>=50)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.8" +files = [ + {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, + {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, +] + +[[package]] +name = "packaging" +version = "25.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, + {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, +] + +[[package]] +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pydantic" +version = "2.10.6" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584"}, + {file = "pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236"}, +] + +[package.dependencies] +annotated-types = ">=0.6.0" +pydantic-core = "2.27.2" +typing-extensions = ">=4.12.2" + +[package.extras] +email = ["email-validator (>=2.0.0)"] +timezone = ["tzdata"] + +[[package]] +name = "pydantic-core" +version = "2.27.2" +description = "Core functionality for Pydantic validation and serialization" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa"}, + {file = "pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7969e133a6f183be60e9f6f56bfae753585680f3b7307a8e555a948d443cc05a"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3de9961f2a346257caf0aa508a4da705467f53778e9ef6fe744c038119737ef5"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2bb4d3e5873c37bb3dd58714d4cd0b0e6238cebc4177ac8fe878f8b3aa8e74c"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:280d219beebb0752699480fe8f1dc61ab6615c2046d76b7ab7ee38858de0a4e7"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47956ae78b6422cbd46f772f1746799cbb862de838fd8d1fbd34a82e05b0983a"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:14d4a5c49d2f009d62a2a7140d3064f686d17a5d1a268bc641954ba181880236"}, + {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:337b443af21d488716f8d0b6164de833e788aa6bd7e3a39c005febc1284f4962"}, + {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:03d0f86ea3184a12f41a2d23f7ccb79cdb5a18e06993f8a45baa8dfec746f0e9"}, + {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7041c36f5680c6e0f08d922aed302e98b3745d97fe1589db0a3eebf6624523af"}, + {file = "pydantic_core-2.27.2-cp310-cp310-win32.whl", hash = "sha256:50a68f3e3819077be2c98110c1f9dcb3817e93f267ba80a2c05bb4f8799e2ff4"}, + {file = "pydantic_core-2.27.2-cp310-cp310-win_amd64.whl", hash = "sha256:e0fd26b16394ead34a424eecf8a31a1f5137094cabe84a1bcb10fa6ba39d3d31"}, + {file = "pydantic_core-2.27.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8e10c99ef58cfdf2a66fc15d66b16c4a04f62bca39db589ae8cba08bc55331bc"}, + {file = "pydantic_core-2.27.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:26f32e0adf166a84d0cb63be85c562ca8a6fa8de28e5f0d92250c6b7e9e2aff7"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c19d1ea0673cd13cc2f872f6c9ab42acc4e4f492a7ca9d3795ce2b112dd7e15"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e68c4446fe0810e959cdff46ab0a41ce2f2c86d227d96dc3847af0ba7def306"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9640b0059ff4f14d1f37321b94061c6db164fbe49b334b31643e0528d100d99"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40d02e7d45c9f8af700f3452f329ead92da4c5f4317ca9b896de7ce7199ea459"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d81d2068e1c1228a565af076598f9e7451712700b673de8f502f0334f281387d"}, + {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1a4207639fb02ec2dbb76227d7c751a20b1a6b4bc52850568e52260cae64ca3b"}, + {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:3de3ce3c9ddc8bbd88f6e0e304dea0e66d843ec9de1b0042b0911c1663ffd474"}, + {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:30c5f68ded0c36466acede341551106821043e9afaad516adfb6e8fa80a4e6a6"}, + {file = "pydantic_core-2.27.2-cp311-cp311-win32.whl", hash = "sha256:c70c26d2c99f78b125a3459f8afe1aed4d9687c24fd677c6a4436bc042e50d6c"}, + {file = "pydantic_core-2.27.2-cp311-cp311-win_amd64.whl", hash = "sha256:08e125dbdc505fa69ca7d9c499639ab6407cfa909214d500897d02afb816e7cc"}, + {file = "pydantic_core-2.27.2-cp311-cp311-win_arm64.whl", hash = "sha256:26f0d68d4b235a2bae0c3fc585c585b4ecc51382db0e3ba402a22cbc440915e4"}, + {file = "pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0"}, + {file = "pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4"}, + {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3"}, + {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4"}, + {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57"}, + {file = "pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc"}, + {file = "pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9"}, + {file = "pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b"}, + {file = "pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b"}, + {file = "pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4"}, + {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27"}, + {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee"}, + {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1"}, + {file = "pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130"}, + {file = "pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee"}, + {file = "pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b"}, + {file = "pydantic_core-2.27.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d3e8d504bdd3f10835468f29008d72fc8359d95c9c415ce6e767203db6127506"}, + {file = "pydantic_core-2.27.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:521eb9b7f036c9b6187f0b47318ab0d7ca14bd87f776240b90b21c1f4f149320"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85210c4d99a0114f5a9481b44560d7d1e35e32cc5634c656bc48e590b669b145"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d716e2e30c6f140d7560ef1538953a5cd1a87264c737643d481f2779fc247fe1"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f66d89ba397d92f840f8654756196d93804278457b5fbede59598a1f9f90b228"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:669e193c1c576a58f132e3158f9dfa9662969edb1a250c54d8fa52590045f046"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdbe7629b996647b99c01b37f11170a57ae675375b14b8c13b8518b8320ced5"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d262606bf386a5ba0b0af3b97f37c83d7011439e3dc1a9298f21efb292e42f1a"}, + {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cabb9bcb7e0d97f74df8646f34fc76fbf793b7f6dc2438517d7a9e50eee4f14d"}, + {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:d2d63f1215638d28221f664596b1ccb3944f6e25dd18cd3b86b0a4c408d5ebb9"}, + {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bca101c00bff0adb45a833f8451b9105d9df18accb8743b08107d7ada14bd7da"}, + {file = "pydantic_core-2.27.2-cp38-cp38-win32.whl", hash = "sha256:f6f8e111843bbb0dee4cb6594cdc73e79b3329b526037ec242a3e49012495b3b"}, + {file = "pydantic_core-2.27.2-cp38-cp38-win_amd64.whl", hash = "sha256:fd1aea04935a508f62e0d0ef1f5ae968774a32afc306fb8545e06f5ff5cdf3ad"}, + {file = "pydantic_core-2.27.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c10eb4f1659290b523af58fa7cffb452a61ad6ae5613404519aee4bfbf1df993"}, + {file = "pydantic_core-2.27.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ef592d4bad47296fb11f96cd7dc898b92e795032b4894dfb4076cfccd43a9308"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61709a844acc6bf0b7dce7daae75195a10aac96a596ea1b776996414791ede4"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c5f762659e47fdb7b16956c71598292f60a03aa92f8b6351504359dbdba6cf"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c9775e339e42e79ec99c441d9730fccf07414af63eac2f0e48e08fd38a64d76"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57762139821c31847cfb2df63c12f725788bd9f04bc2fb392790959b8f70f118"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d1e85068e818c73e048fe28cfc769040bb1f475524f4745a5dc621f75ac7630"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:097830ed52fd9e427942ff3b9bc17fab52913b2f50f2880dc4a5611446606a54"}, + {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:044a50963a614ecfae59bb1eaf7ea7efc4bc62f49ed594e18fa1e5d953c40e9f"}, + {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:4e0b4220ba5b40d727c7f879eac379b822eee5d8fff418e9d3381ee45b3b0362"}, + {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e4f4bb20d75e9325cc9696c6802657b58bc1dbbe3022f32cc2b2b632c3fbb96"}, + {file = "pydantic_core-2.27.2-cp39-cp39-win32.whl", hash = "sha256:cca63613e90d001b9f2f9a9ceb276c308bfa2a43fafb75c8031c4f66039e8c6e"}, + {file = "pydantic_core-2.27.2-cp39-cp39-win_amd64.whl", hash = "sha256:77d1bca19b0f7021b3a982e6f903dcd5b2b06076def36a652e3907f596e29f67"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2bf14caea37e91198329b828eae1618c068dfb8ef17bb33287a7ad4b61ac314e"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0cb791f5b45307caae8810c2023a184c74605ec3bcbb67d13846c28ff731ff8"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:688d3fd9fcb71f41c4c015c023d12a79d1c4c0732ec9eb35d96e3388a120dcf3"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d591580c34f4d731592f0e9fe40f9cc1b430d297eecc70b962e93c5c668f15f"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:82f986faf4e644ffc189a7f1aafc86e46ef70372bb153e7001e8afccc6e54133"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:bec317a27290e2537f922639cafd54990551725fc844249e64c523301d0822fc"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:0296abcb83a797db256b773f45773da397da75a08f5fcaef41f2044adec05f50"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0d75070718e369e452075a6017fbf187f788e17ed67a3abd47fa934d001863d9"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7e17b560be3c98a8e3aa66ce828bdebb9e9ac6ad5466fba92eb74c4c95cb1151"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c33939a82924da9ed65dab5a65d427205a73181d8098e79b6b426bdf8ad4e656"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:00bad2484fa6bda1e216e7345a798bd37c68fb2d97558edd584942aa41b7d278"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c817e2b40aba42bac6f457498dacabc568c3b7a986fc9ba7c8d9d260b71485fb"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:251136cdad0cb722e93732cb45ca5299fb56e1344a833640bf93b2803f8d1bfd"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2088237af596f0a524d3afc39ab3b036e8adb054ee57cbb1dcf8e09da5b29cc"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d4041c0b966a84b4ae7a09832eb691a35aec90910cd2dbe7a208de59be77965b"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:8083d4e875ebe0b864ffef72a4304827015cff328a1be6e22cc850753bfb122b"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f141ee28a0ad2123b6611b6ceff018039df17f32ada8b534e6aa039545a3efb2"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7d0c8399fcc1848491f00e0314bd59fb34a9c008761bcb422a057670c3f65e35"}, + {file = "pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" + +[[package]] +name = "pytest" +version = "7.4.4" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, + {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-asyncio" +version = "0.23.8" +description = "Pytest support for asyncio" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest_asyncio-0.23.8-py3-none-any.whl", hash = "sha256:50265d892689a5faefb84df80819d1ecef566eb3549cf915dfb33569359d1ce2"}, + {file = "pytest_asyncio-0.23.8.tar.gz", hash = "sha256:759b10b33a6dc61cce40a8bd5205e302978bbbcc00e279a8b61d9a6a3c82e4d3"}, +] + +[package.dependencies] +pytest = ">=7.0.0,<9" + +[package.extras] +docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] +testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] + +[[package]] +name = "pytest-xdist" +version = "3.6.1" +description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7"}, + {file = "pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d"}, +] + +[package.dependencies] +execnet = ">=2.1" +pytest = ">=7.0.0" + +[package.extras] +psutil = ["psutil (>=3.0)"] +setproctitle = ["setproctitle"] +testing = ["filelock"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "ruff" +version = "0.11.5" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.11.5-py3-none-linux_armv6l.whl", hash = "sha256:2561294e108eb648e50f210671cc56aee590fb6167b594144401532138c66c7b"}, + {file = "ruff-0.11.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ac12884b9e005c12d0bd121f56ccf8033e1614f736f766c118ad60780882a077"}, + {file = "ruff-0.11.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:4bfd80a6ec559a5eeb96c33f832418bf0fb96752de0539905cf7b0cc1d31d779"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0947c0a1afa75dcb5db4b34b070ec2bccee869d40e6cc8ab25aca11a7d527794"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ad871ff74b5ec9caa66cb725b85d4ef89b53f8170f47c3406e32ef040400b038"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e6cf918390cfe46d240732d4d72fa6e18e528ca1f60e318a10835cf2fa3dc19f"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:56145ee1478582f61c08f21076dc59153310d606ad663acc00ea3ab5b2125f82"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e5f66f8f1e8c9fc594cbd66fbc5f246a8d91f916cb9667e80208663ec3728304"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80b4df4d335a80315ab9afc81ed1cff62be112bd165e162b5eed8ac55bfc8470"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3068befab73620b8a0cc2431bd46b3cd619bc17d6f7695a3e1bb166b652c382a"}, + {file = "ruff-0.11.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f5da2e710a9641828e09aa98b92c9ebbc60518fdf3921241326ca3e8f8e55b8b"}, + {file = "ruff-0.11.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ef39f19cb8ec98cbc762344921e216f3857a06c47412030374fffd413fb8fd3a"}, + {file = "ruff-0.11.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:b2a7cedf47244f431fd11aa5a7e2806dda2e0c365873bda7834e8f7d785ae159"}, + {file = "ruff-0.11.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:81be52e7519f3d1a0beadcf8e974715b2dfc808ae8ec729ecfc79bddf8dbb783"}, + {file = "ruff-0.11.5-py3-none-win32.whl", hash = "sha256:e268da7b40f56e3eca571508a7e567e794f9bfcc0f412c4b607931d3af9c4afe"}, + {file = "ruff-0.11.5-py3-none-win_amd64.whl", hash = "sha256:6c6dc38af3cfe2863213ea25b6dc616d679205732dc0fb673356c2d69608f800"}, + {file = "ruff-0.11.5-py3-none-win_arm64.whl", hash = "sha256:67e241b4314f4eacf14a601d586026a962f4002a475aa702c69980a38087aa4e"}, + {file = "ruff-0.11.5.tar.gz", hash = "sha256:cae2e2439cb88853e421901ec040a758960b576126dab520fa08e9de431d1bef"}, +] + +[[package]] +name = "six" +version = "1.17.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +description = "Sniff out which async library your code is running under" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, + {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, +] + +[[package]] +name = "tomli" +version = "2.3.0" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45"}, + {file = "tomli-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:883b1c0d6398a6a9d29b508c331fa56adbcdff647f6ace4dfca0f50e90dfd0ba"}, + {file = "tomli-2.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1381caf13ab9f300e30dd8feadb3de072aeb86f1d34a8569453ff32a7dea4bf"}, + {file = "tomli-2.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0e285d2649b78c0d9027570d4da3425bdb49830a6156121360b3f8511ea3441"}, + {file = "tomli-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a154a9ae14bfcf5d8917a59b51ffd5a3ac1fd149b71b47a3a104ca4edcfa845"}, + {file = "tomli-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:74bf8464ff93e413514fefd2be591c3b0b23231a77f901db1eb30d6f712fc42c"}, + {file = "tomli-2.3.0-cp311-cp311-win32.whl", hash = "sha256:00b5f5d95bbfc7d12f91ad8c593a1659b6387b43f054104cda404be6bda62456"}, + {file = "tomli-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:4dc4ce8483a5d429ab602f111a93a6ab1ed425eae3122032db7e9acf449451be"}, + {file = "tomli-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d7d86942e56ded512a594786a5ba0a5e521d02529b3826e7761a05138341a2ac"}, + {file = "tomli-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:73ee0b47d4dad1c5e996e3cd33b8a76a50167ae5f96a2607cbe8cc773506ab22"}, + {file = "tomli-2.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:792262b94d5d0a466afb5bc63c7daa9d75520110971ee269152083270998316f"}, + {file = "tomli-2.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f195fe57ecceac95a66a75ac24d9d5fbc98ef0962e09b2eddec5d39375aae52"}, + {file = "tomli-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e31d432427dcbf4d86958c184b9bfd1e96b5b71f8eb17e6d02531f434fd335b8"}, + {file = "tomli-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b0882799624980785240ab732537fcfc372601015c00f7fc367c55308c186f6"}, + {file = "tomli-2.3.0-cp312-cp312-win32.whl", hash = "sha256:ff72b71b5d10d22ecb084d345fc26f42b5143c5533db5e2eaba7d2d335358876"}, + {file = "tomli-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:1cb4ed918939151a03f33d4242ccd0aa5f11b3547d0cf30f7c74a408a5b99878"}, + {file = "tomli-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5192f562738228945d7b13d4930baffda67b69425a7f0da96d360b0a3888136b"}, + {file = "tomli-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:be71c93a63d738597996be9528f4abe628d1adf5e6eb11607bc8fe1a510b5dae"}, + {file = "tomli-2.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4665508bcbac83a31ff8ab08f424b665200c0e1e645d2bd9ab3d3e557b6185b"}, + {file = "tomli-2.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4021923f97266babc6ccab9f5068642a0095faa0a51a246a6a02fccbb3514eaf"}, + {file = "tomli-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4ea38c40145a357d513bffad0ed869f13c1773716cf71ccaa83b0fa0cc4e42f"}, + {file = "tomli-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad805ea85eda330dbad64c7ea7a4556259665bdf9d2672f5dccc740eb9d3ca05"}, + {file = "tomli-2.3.0-cp313-cp313-win32.whl", hash = "sha256:97d5eec30149fd3294270e889b4234023f2c69747e555a27bd708828353ab606"}, + {file = "tomli-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0c95ca56fbe89e065c6ead5b593ee64b84a26fca063b5d71a1122bf26e533999"}, + {file = "tomli-2.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cebc6fe843e0733ee827a282aca4999b596241195f43b4cc371d64fc6639da9e"}, + {file = "tomli-2.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4c2ef0244c75aba9355561272009d934953817c49f47d768070c3c94355c2aa3"}, + {file = "tomli-2.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c22a8bf253bacc0cf11f35ad9808b6cb75ada2631c2d97c971122583b129afbc"}, + {file = "tomli-2.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0eea8cc5c5e9f89c9b90c4896a8deefc74f518db5927d0e0e8d4a80953d774d0"}, + {file = "tomli-2.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b74a0e59ec5d15127acdabd75ea17726ac4c5178ae51b85bfe39c4f8a278e879"}, + {file = "tomli-2.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b5870b50c9db823c595983571d1296a6ff3e1b88f734a4c8f6fc6188397de005"}, + {file = "tomli-2.3.0-cp314-cp314-win32.whl", hash = "sha256:feb0dacc61170ed7ab602d3d972a58f14ee3ee60494292d384649a3dc38ef463"}, + {file = "tomli-2.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:b273fcbd7fc64dc3600c098e39136522650c49bca95df2d11cf3b626422392c8"}, + {file = "tomli-2.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:940d56ee0410fa17ee1f12b817b37a4d4e4dc4d27340863cc67236c74f582e77"}, + {file = "tomli-2.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f85209946d1fe94416debbb88d00eb92ce9cd5266775424ff81bc959e001acaf"}, + {file = "tomli-2.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a56212bdcce682e56b0aaf79e869ba5d15a6163f88d5451cbde388d48b13f530"}, + {file = "tomli-2.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5f3ffd1e098dfc032d4d3af5c0ac64f6d286d98bc148698356847b80fa4de1b"}, + {file = "tomli-2.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e01decd096b1530d97d5d85cb4dff4af2d8347bd35686654a004f8dea20fc67"}, + {file = "tomli-2.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8a35dd0e643bb2610f156cca8db95d213a90015c11fee76c946aa62b7ae7e02f"}, + {file = "tomli-2.3.0-cp314-cp314t-win32.whl", hash = "sha256:a1f7f282fe248311650081faafa5f4732bdbfef5d45fe3f2e702fbc6f2d496e0"}, + {file = "tomli-2.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:70a251f8d4ba2d9ac2542eecf008b3c8a9fc5c3f9f02c56a9d7952612be2fdba"}, + {file = "tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b"}, + {file = "tomli-2.3.0.tar.gz", hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549"}, +] + +[[package]] +name = "types-python-dateutil" +version = "2.9.0.20241206" +description = "Typing stubs for python-dateutil" +optional = false +python-versions = ">=3.8" +files = [ + {file = "types_python_dateutil-2.9.0.20241206-py3-none-any.whl", hash = "sha256:e248a4bc70a486d3e3ec84d0dc30eec3a5f979d6e7ee4123ae043eedbb987f53"}, + {file = "types_python_dateutil-2.9.0.20241206.tar.gz", hash = "sha256:18f493414c26ffba692a72369fea7a154c502646301ebfe3d56a04b3767284cb"}, +] + +[[package]] +name = "typing-extensions" +version = "4.13.2" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"}, + {file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"}, +] + +[metadata] +lock-version = "2.0" +python-versions = "^3.8" +content-hash = "bcf31a142c86d9e556553c8c260a93b563ac64a043076dbd48b26111d422c26e" diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/pyproject.toml b/seed/python-sdk/inferred-auth-implicit-api-key/pyproject.toml new file mode 100644 index 000000000000..b8cf3234c7f2 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/pyproject.toml @@ -0,0 +1,91 @@ +[project] +name = "fern_inferred-auth-implicit-api-key" +dynamic = ["version"] + +[tool.poetry] +name = "fern_inferred-auth-implicit-api-key" +version = "0.0.1" +description = "" +readme = "README.md" +authors = [] +keywords = [ + "fern", + "test" +] + +classifiers = [ + "Intended Audience :: Developers", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Operating System :: OS Independent", + "Operating System :: POSIX", + "Operating System :: MacOS", + "Operating System :: POSIX :: Linux", + "Operating System :: Microsoft :: Windows", + "Topic :: Software Development :: Libraries :: Python Modules", + "Typing :: Typed" +] +packages = [ + { include = "seed", from = "src"} +] + +[tool.poetry.urls] +Documentation = 'https://buildwithfern.com/learn' +Homepage = 'https://buildwithfern.com/' +Repository = 'https://github.com/inferred-auth-implicit-api-key/fern' + +[tool.poetry.dependencies] +python = "^3.8" +httpx = ">=0.21.2" +pydantic = ">= 1.9.2" +pydantic-core = ">=2.18.2" +typing_extensions = ">= 4.0.0" + +[tool.poetry.group.dev.dependencies] +mypy = "==1.13.0" +pytest = "^7.4.0" +pytest-asyncio = "^0.23.5" +pytest-xdist = "^3.6.1" +python-dateutil = "^2.9.0" +types-python-dateutil = "^2.9.0.20240316" +ruff = "==0.11.5" + +[tool.pytest.ini_options] +testpaths = [ "tests" ] +asyncio_mode = "auto" + +[tool.mypy] +plugins = ["pydantic.mypy"] + +[tool.ruff] +line-length = 120 + +[tool.ruff.lint] +select = [ + "E", # pycodestyle errors + "F", # pyflakes + "I", # isort +] +ignore = [ + "E402", # Module level import not at top of file + "E501", # Line too long + "E711", # Comparison to `None` should be `cond is not None` + "E712", # Avoid equality comparisons to `True`; use `if ...:` checks + "E721", # Use `is` and `is not` for type comparisons, or `isinstance()` for insinstance checks + "E722", # Do not use bare `except` + "E731", # Do not assign a `lambda` expression, use a `def` + "F821", # Undefined name + "F841" # Local variable ... is assigned to but never used +] + +[tool.ruff.lint.isort] +section-order = ["future", "standard-library", "third-party", "first-party"] + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/reference.md b/seed/python-sdk/inferred-auth-implicit-api-key/reference.md new file mode 100644 index 000000000000..51553e8ac599 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/reference.md @@ -0,0 +1,199 @@ +# Reference +## Auth +
client.auth.get_token(...) -> AsyncHttpResponse[TokenResponse] +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from seed import SeedInferredAuthImplicitApiKey + +client = SeedInferredAuthImplicitApiKey( + base_url="https://yourhost.com/path/to/api", +) +client.auth.get_token( + api_key="api_key", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**api_key:** `str` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## NestedNoAuth Api +
client.nested_no_auth.api.get_something() -> AsyncHttpResponse[None] +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from seed import SeedInferredAuthImplicitApiKey + +client = SeedInferredAuthImplicitApiKey( + base_url="https://yourhost.com/path/to/api", +) +client.nested_no_auth.api.get_something() + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Nested Api +
client.nested.api.get_something() -> AsyncHttpResponse[None] +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from seed import SeedInferredAuthImplicitApiKey + +client = SeedInferredAuthImplicitApiKey( + base_url="https://yourhost.com/path/to/api", +) +client.nested.api.get_something() + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Simple +
client.simple.get_something() -> AsyncHttpResponse[None] +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from seed import SeedInferredAuthImplicitApiKey + +client = SeedInferredAuthImplicitApiKey( + base_url="https://yourhost.com/path/to/api", +) +client.simple.get_something() + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/requirements.txt b/seed/python-sdk/inferred-auth-implicit-api-key/requirements.txt new file mode 100644 index 000000000000..e80f640a2e74 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/requirements.txt @@ -0,0 +1,4 @@ +httpx>=0.21.2 +pydantic>= 1.9.2 +pydantic-core>=2.18.2 +typing_extensions>= 4.0.0 diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/snippet.json b/seed/python-sdk/inferred-auth-implicit-api-key/snippet.json new file mode 100644 index 000000000000..d2bf2863998e --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/snippet.json @@ -0,0 +1,57 @@ +{ + "types": {}, + "endpoints": [ + { + "example_identifier": "default", + "id": { + "path": "/token", + "method": "POST", + "identifier_override": "endpoint_auth.getToken" + }, + "snippet": { + "sync_client": "from seed import SeedInferredAuthImplicitApiKey\n\nclient = SeedInferredAuthImplicitApiKey(\n base_url=\"https://yourhost.com/path/to/api\",\n)\nclient.auth.get_token(\n api_key=\"api_key\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedInferredAuthImplicitApiKey\n\nclient = AsyncSeedInferredAuthImplicitApiKey(\n base_url=\"https://yourhost.com/path/to/api\",\n)\n\n\nasync def main() -> None:\n await client.auth.get_token(\n api_key=\"api_key\",\n )\n\n\nasyncio.run(main())\n", + "type": "python" + } + }, + { + "example_identifier": "default", + "id": { + "path": "/nested-no-auth/get-something", + "method": "GET", + "identifier_override": "endpoint_nested-no-auth/api.getSomething" + }, + "snippet": { + "sync_client": "from seed import SeedInferredAuthImplicitApiKey\n\nclient = SeedInferredAuthImplicitApiKey(\n base_url=\"https://yourhost.com/path/to/api\",\n)\nclient.nested_no_auth.api.get_something()\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedInferredAuthImplicitApiKey\n\nclient = AsyncSeedInferredAuthImplicitApiKey(\n base_url=\"https://yourhost.com/path/to/api\",\n)\n\n\nasync def main() -> None:\n await client.nested_no_auth.api.get_something()\n\n\nasyncio.run(main())\n", + "type": "python" + } + }, + { + "example_identifier": "default", + "id": { + "path": "/nested/get-something", + "method": "GET", + "identifier_override": "endpoint_nested/api.getSomething" + }, + "snippet": { + "sync_client": "from seed import SeedInferredAuthImplicitApiKey\n\nclient = SeedInferredAuthImplicitApiKey(\n base_url=\"https://yourhost.com/path/to/api\",\n)\nclient.nested.api.get_something()\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedInferredAuthImplicitApiKey\n\nclient = AsyncSeedInferredAuthImplicitApiKey(\n base_url=\"https://yourhost.com/path/to/api\",\n)\n\n\nasync def main() -> None:\n await client.nested.api.get_something()\n\n\nasyncio.run(main())\n", + "type": "python" + } + }, + { + "example_identifier": "default", + "id": { + "path": "/get-something", + "method": "GET", + "identifier_override": "endpoint_simple.getSomething" + }, + "snippet": { + "sync_client": "from seed import SeedInferredAuthImplicitApiKey\n\nclient = SeedInferredAuthImplicitApiKey(\n base_url=\"https://yourhost.com/path/to/api\",\n)\nclient.simple.get_something()\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedInferredAuthImplicitApiKey\n\nclient = AsyncSeedInferredAuthImplicitApiKey(\n base_url=\"https://yourhost.com/path/to/api\",\n)\n\n\nasync def main() -> None:\n await client.simple.get_something()\n\n\nasyncio.run(main())\n", + "type": "python" + } + } + ] +} \ No newline at end of file diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/__init__.py b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/__init__.py new file mode 100644 index 000000000000..20ad81a5566e --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/__init__.py @@ -0,0 +1,55 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from . import auth, nested, nested_no_auth, simple + from .auth import TokenResponse + from .client import AsyncSeedInferredAuthImplicitApiKey, SeedInferredAuthImplicitApiKey + from .version import __version__ +_dynamic_imports: typing.Dict[str, str] = { + "AsyncSeedInferredAuthImplicitApiKey": ".client", + "SeedInferredAuthImplicitApiKey": ".client", + "TokenResponse": ".auth", + "__version__": ".version", + "auth": ".auth", + "nested": ".nested", + "nested_no_auth": ".nested_no_auth", + "simple": ".simple", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "AsyncSeedInferredAuthImplicitApiKey", + "SeedInferredAuthImplicitApiKey", + "TokenResponse", + "__version__", + "auth", + "nested", + "nested_no_auth", + "simple", +] diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/auth/__init__.py b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/auth/__init__.py new file mode 100644 index 000000000000..687d87d84895 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/auth/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import TokenResponse +_dynamic_imports: typing.Dict[str, str] = {"TokenResponse": ".types"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["TokenResponse"] diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/auth/client.py b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/auth/client.py new file mode 100644 index 000000000000..8161509ca5f4 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/auth/client.py @@ -0,0 +1,104 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.request_options import RequestOptions +from .raw_client import AsyncRawAuthClient, RawAuthClient +from .types.token_response import TokenResponse + + +class AuthClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawAuthClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawAuthClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawAuthClient + """ + return self._raw_client + + def get_token(self, *, api_key: str, request_options: typing.Optional[RequestOptions] = None) -> TokenResponse: + """ + Parameters + ---------- + api_key : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + TokenResponse + + Examples + -------- + from seed import SeedInferredAuthImplicitApiKey + + client = SeedInferredAuthImplicitApiKey( + base_url="https://yourhost.com/path/to/api", + ) + client.auth.get_token( + api_key="api_key", + ) + """ + _response = self._raw_client.get_token(api_key=api_key, request_options=request_options) + return _response.data + + +class AsyncAuthClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawAuthClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawAuthClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawAuthClient + """ + return self._raw_client + + async def get_token( + self, *, api_key: str, request_options: typing.Optional[RequestOptions] = None + ) -> TokenResponse: + """ + Parameters + ---------- + api_key : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + TokenResponse + + Examples + -------- + import asyncio + + from seed import AsyncSeedInferredAuthImplicitApiKey + + client = AsyncSeedInferredAuthImplicitApiKey( + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.auth.get_token( + api_key="api_key", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_token(api_key=api_key, request_options=request_options) + return _response.data diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/auth/raw_client.py b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/auth/raw_client.py new file mode 100644 index 000000000000..5473b3663cae --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/auth/raw_client.py @@ -0,0 +1,97 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +from ..core.api_error import ApiError +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.http_response import AsyncHttpResponse, HttpResponse +from ..core.pydantic_utilities import parse_obj_as +from ..core.request_options import RequestOptions +from .types.token_response import TokenResponse + + +class RawAuthClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def get_token( + self, *, api_key: str, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[TokenResponse]: + """ + Parameters + ---------- + api_key : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[TokenResponse] + """ + _response = self._client_wrapper.httpx_client.request( + "token", + method="POST", + headers={ + "X-Api-Key": str(api_key) if api_key is not None else None, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + TokenResponse, + parse_obj_as( + type_=TokenResponse, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + +class AsyncRawAuthClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def get_token( + self, *, api_key: str, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[TokenResponse]: + """ + Parameters + ---------- + api_key : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[TokenResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + "token", + method="POST", + headers={ + "X-Api-Key": str(api_key) if api_key is not None else None, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + TokenResponse, + parse_obj_as( + type_=TokenResponse, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/auth/types/__init__.py b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/auth/types/__init__.py new file mode 100644 index 000000000000..846044624065 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/auth/types/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .token_response import TokenResponse +_dynamic_imports: typing.Dict[str, str] = {"TokenResponse": ".token_response"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["TokenResponse"] diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/auth/types/token_response.py b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/auth/types/token_response.py new file mode 100644 index 000000000000..abb24d39392a --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/auth/types/token_response.py @@ -0,0 +1,26 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class TokenResponse(UniversalBaseModel): + """ + An auth token response. + """ + + access_token: str + token_type: str + expires_in: int + scope: typing.Optional[str] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/client.py b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/client.py new file mode 100644 index 000000000000..86b034cbde4c --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/client.py @@ -0,0 +1,194 @@ +# This file was auto-generated by Fern from our API Definition. + +from __future__ import annotations + +import typing + +import httpx +from .core.client_wrapper import AsyncClientWrapper, SyncClientWrapper + +if typing.TYPE_CHECKING: + from .auth.client import AsyncAuthClient, AuthClient + from .nested.client import AsyncNestedClient, NestedClient + from .nested_no_auth.client import AsyncNestedNoAuthClient, NestedNoAuthClient + from .simple.client import AsyncSimpleClient, SimpleClient + + +class SeedInferredAuthImplicitApiKey: + """ + Use this class to access the different functions within the SDK. You can instantiate any number of clients with different configuration that will propagate to these functions. + + Parameters + ---------- + base_url : str + The base url to use for requests from the client. + + headers : typing.Optional[typing.Dict[str, str]] + Additional headers to send with every request. + + timeout : typing.Optional[float] + The timeout to be used, in seconds, for requests. By default the timeout is 60 seconds, unless a custom httpx client is used, in which case this default is not enforced. + + follow_redirects : typing.Optional[bool] + Whether the default httpx client follows redirects or not, this is irrelevant if a custom httpx client is passed in. + + httpx_client : typing.Optional[httpx.Client] + The httpx client to use for making requests, a preconfigured client is used by default, however this is useful should you want to pass in any custom httpx configuration. + + Examples + -------- + from seed import SeedInferredAuthImplicitApiKey + + client = SeedInferredAuthImplicitApiKey( + base_url="https://yourhost.com/path/to/api", + ) + """ + + def __init__( + self, + *, + base_url: str, + headers: typing.Optional[typing.Dict[str, str]] = None, + timeout: typing.Optional[float] = None, + follow_redirects: typing.Optional[bool] = True, + httpx_client: typing.Optional[httpx.Client] = None, + ): + _defaulted_timeout = ( + timeout if timeout is not None else 60 if httpx_client is None else httpx_client.timeout.read + ) + self._client_wrapper = SyncClientWrapper( + base_url=base_url, + headers=headers, + httpx_client=httpx_client + if httpx_client is not None + else httpx.Client(timeout=_defaulted_timeout, follow_redirects=follow_redirects) + if follow_redirects is not None + else httpx.Client(timeout=_defaulted_timeout), + timeout=_defaulted_timeout, + ) + self._auth: typing.Optional[AuthClient] = None + self._nested_no_auth: typing.Optional[NestedNoAuthClient] = None + self._nested: typing.Optional[NestedClient] = None + self._simple: typing.Optional[SimpleClient] = None + + @property + def auth(self): + if self._auth is None: + from .auth.client import AuthClient # noqa: E402 + + self._auth = AuthClient(client_wrapper=self._client_wrapper) + return self._auth + + @property + def nested_no_auth(self): + if self._nested_no_auth is None: + from .nested_no_auth.client import NestedNoAuthClient # noqa: E402 + + self._nested_no_auth = NestedNoAuthClient(client_wrapper=self._client_wrapper) + return self._nested_no_auth + + @property + def nested(self): + if self._nested is None: + from .nested.client import NestedClient # noqa: E402 + + self._nested = NestedClient(client_wrapper=self._client_wrapper) + return self._nested + + @property + def simple(self): + if self._simple is None: + from .simple.client import SimpleClient # noqa: E402 + + self._simple = SimpleClient(client_wrapper=self._client_wrapper) + return self._simple + + +class AsyncSeedInferredAuthImplicitApiKey: + """ + Use this class to access the different functions within the SDK. You can instantiate any number of clients with different configuration that will propagate to these functions. + + Parameters + ---------- + base_url : str + The base url to use for requests from the client. + + headers : typing.Optional[typing.Dict[str, str]] + Additional headers to send with every request. + + timeout : typing.Optional[float] + The timeout to be used, in seconds, for requests. By default the timeout is 60 seconds, unless a custom httpx client is used, in which case this default is not enforced. + + follow_redirects : typing.Optional[bool] + Whether the default httpx client follows redirects or not, this is irrelevant if a custom httpx client is passed in. + + httpx_client : typing.Optional[httpx.AsyncClient] + The httpx client to use for making requests, a preconfigured client is used by default, however this is useful should you want to pass in any custom httpx configuration. + + Examples + -------- + from seed import AsyncSeedInferredAuthImplicitApiKey + + client = AsyncSeedInferredAuthImplicitApiKey( + base_url="https://yourhost.com/path/to/api", + ) + """ + + def __init__( + self, + *, + base_url: str, + headers: typing.Optional[typing.Dict[str, str]] = None, + timeout: typing.Optional[float] = None, + follow_redirects: typing.Optional[bool] = True, + httpx_client: typing.Optional[httpx.AsyncClient] = None, + ): + _defaulted_timeout = ( + timeout if timeout is not None else 60 if httpx_client is None else httpx_client.timeout.read + ) + self._client_wrapper = AsyncClientWrapper( + base_url=base_url, + headers=headers, + httpx_client=httpx_client + if httpx_client is not None + else httpx.AsyncClient(timeout=_defaulted_timeout, follow_redirects=follow_redirects) + if follow_redirects is not None + else httpx.AsyncClient(timeout=_defaulted_timeout), + timeout=_defaulted_timeout, + ) + self._auth: typing.Optional[AsyncAuthClient] = None + self._nested_no_auth: typing.Optional[AsyncNestedNoAuthClient] = None + self._nested: typing.Optional[AsyncNestedClient] = None + self._simple: typing.Optional[AsyncSimpleClient] = None + + @property + def auth(self): + if self._auth is None: + from .auth.client import AsyncAuthClient # noqa: E402 + + self._auth = AsyncAuthClient(client_wrapper=self._client_wrapper) + return self._auth + + @property + def nested_no_auth(self): + if self._nested_no_auth is None: + from .nested_no_auth.client import AsyncNestedNoAuthClient # noqa: E402 + + self._nested_no_auth = AsyncNestedNoAuthClient(client_wrapper=self._client_wrapper) + return self._nested_no_auth + + @property + def nested(self): + if self._nested is None: + from .nested.client import AsyncNestedClient # noqa: E402 + + self._nested = AsyncNestedClient(client_wrapper=self._client_wrapper) + return self._nested + + @property + def simple(self): + if self._simple is None: + from .simple.client import AsyncSimpleClient # noqa: E402 + + self._simple = AsyncSimpleClient(client_wrapper=self._client_wrapper) + return self._simple diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/__init__.py b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/__init__.py new file mode 100644 index 000000000000..9a33e233875e --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/__init__.py @@ -0,0 +1,105 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .api_error import ApiError + from .client_wrapper import AsyncClientWrapper, BaseClientWrapper, SyncClientWrapper + from .datetime_utils import serialize_datetime + from .file import File, convert_file_dict_to_httpx_tuples, with_content_type + from .http_client import AsyncHttpClient, HttpClient + from .http_response import AsyncHttpResponse, HttpResponse + from .jsonable_encoder import jsonable_encoder + from .pydantic_utilities import ( + IS_PYDANTIC_V2, + UniversalBaseModel, + UniversalRootModel, + parse_obj_as, + universal_field_validator, + universal_root_validator, + update_forward_refs, + ) + from .query_encoder import encode_query + from .remove_none_from_dict import remove_none_from_dict + from .request_options import RequestOptions + from .serialization import FieldMetadata, convert_and_respect_annotation_metadata +_dynamic_imports: typing.Dict[str, str] = { + "ApiError": ".api_error", + "AsyncClientWrapper": ".client_wrapper", + "AsyncHttpClient": ".http_client", + "AsyncHttpResponse": ".http_response", + "BaseClientWrapper": ".client_wrapper", + "FieldMetadata": ".serialization", + "File": ".file", + "HttpClient": ".http_client", + "HttpResponse": ".http_response", + "IS_PYDANTIC_V2": ".pydantic_utilities", + "RequestOptions": ".request_options", + "SyncClientWrapper": ".client_wrapper", + "UniversalBaseModel": ".pydantic_utilities", + "UniversalRootModel": ".pydantic_utilities", + "convert_and_respect_annotation_metadata": ".serialization", + "convert_file_dict_to_httpx_tuples": ".file", + "encode_query": ".query_encoder", + "jsonable_encoder": ".jsonable_encoder", + "parse_obj_as": ".pydantic_utilities", + "remove_none_from_dict": ".remove_none_from_dict", + "serialize_datetime": ".datetime_utils", + "universal_field_validator": ".pydantic_utilities", + "universal_root_validator": ".pydantic_utilities", + "update_forward_refs": ".pydantic_utilities", + "with_content_type": ".file", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "ApiError", + "AsyncClientWrapper", + "AsyncHttpClient", + "AsyncHttpResponse", + "BaseClientWrapper", + "FieldMetadata", + "File", + "HttpClient", + "HttpResponse", + "IS_PYDANTIC_V2", + "RequestOptions", + "SyncClientWrapper", + "UniversalBaseModel", + "UniversalRootModel", + "convert_and_respect_annotation_metadata", + "convert_file_dict_to_httpx_tuples", + "encode_query", + "jsonable_encoder", + "parse_obj_as", + "remove_none_from_dict", + "serialize_datetime", + "universal_field_validator", + "universal_root_validator", + "update_forward_refs", + "with_content_type", +] diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/api_error.py b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/api_error.py new file mode 100644 index 000000000000..6f850a60cba3 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/api_error.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +from typing import Any, Dict, Optional + + +class ApiError(Exception): + headers: Optional[Dict[str, str]] + status_code: Optional[int] + body: Any + + def __init__( + self, + *, + headers: Optional[Dict[str, str]] = None, + status_code: Optional[int] = None, + body: Any = None, + ) -> None: + self.headers = headers + self.status_code = status_code + self.body = body + + def __str__(self) -> str: + return f"headers: {self.headers}, status_code: {self.status_code}, body: {self.body}" diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/client_wrapper.py b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/client_wrapper.py new file mode 100644 index 000000000000..c736d4965064 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/client_wrapper.py @@ -0,0 +1,84 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import httpx +from .http_client import AsyncHttpClient, HttpClient + + +class BaseClientWrapper: + def __init__( + self, + *, + headers: typing.Optional[typing.Dict[str, str]] = None, + base_url: str, + timeout: typing.Optional[float] = None, + ): + self._headers = headers + self._base_url = base_url + self._timeout = timeout + + def get_headers(self) -> typing.Dict[str, str]: + headers: typing.Dict[str, str] = { + "User-Agent": "fern_inferred-auth-implicit-api-key/0.0.1", + "X-Fern-Language": "Python", + "X-Fern-SDK-Name": "fern_inferred-auth-implicit-api-key", + "X-Fern-SDK-Version": "0.0.1", + **(self.get_custom_headers() or {}), + } + return headers + + def get_custom_headers(self) -> typing.Optional[typing.Dict[str, str]]: + return self._headers + + def get_base_url(self) -> str: + return self._base_url + + def get_timeout(self) -> typing.Optional[float]: + return self._timeout + + +class SyncClientWrapper(BaseClientWrapper): + def __init__( + self, + *, + headers: typing.Optional[typing.Dict[str, str]] = None, + base_url: str, + timeout: typing.Optional[float] = None, + httpx_client: httpx.Client, + ): + super().__init__(headers=headers, base_url=base_url, timeout=timeout) + self.httpx_client = HttpClient( + httpx_client=httpx_client, + base_headers=self.get_headers, + base_timeout=self.get_timeout, + base_url=self.get_base_url, + ) + + +class AsyncClientWrapper(BaseClientWrapper): + def __init__( + self, + *, + headers: typing.Optional[typing.Dict[str, str]] = None, + base_url: str, + timeout: typing.Optional[float] = None, + async_token: typing.Optional[typing.Callable[[], typing.Awaitable[str]]] = None, + httpx_client: httpx.AsyncClient, + ): + super().__init__(headers=headers, base_url=base_url, timeout=timeout) + self._async_token = async_token + self.httpx_client = AsyncHttpClient( + httpx_client=httpx_client, + base_headers=self.get_headers, + base_timeout=self.get_timeout, + base_url=self.get_base_url, + async_base_headers=self.async_get_headers, + ) + + async def async_get_headers(self) -> typing.Dict[str, str]: + headers = self.get_headers() + if self._async_token is not None: + token = await self._async_token() + headers["Authorization"] = f"Bearer {token}" + return headers diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/datetime_utils.py b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/datetime_utils.py new file mode 100644 index 000000000000..7c9864a944c2 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/datetime_utils.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt + + +def serialize_datetime(v: dt.datetime) -> str: + """ + Serialize a datetime including timezone info. + + Uses the timezone info provided if present, otherwise uses the current runtime's timezone info. + + UTC datetimes end in "Z" while all other timezones are represented as offset from UTC, e.g. +05:00. + """ + + def _serialize_zoned_datetime(v: dt.datetime) -> str: + if v.tzinfo is not None and v.tzinfo.tzname(None) == dt.timezone.utc.tzname(None): + # UTC is a special case where we use "Z" at the end instead of "+00:00" + return v.isoformat().replace("+00:00", "Z") + else: + # Delegate to the typical +/- offset format + return v.isoformat() + + if v.tzinfo is not None: + return _serialize_zoned_datetime(v) + else: + local_tz = dt.datetime.now().astimezone().tzinfo + localized_dt = v.replace(tzinfo=local_tz) + return _serialize_zoned_datetime(localized_dt) diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/file.py b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/file.py new file mode 100644 index 000000000000..44b0d27c0895 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/file.py @@ -0,0 +1,67 @@ +# This file was auto-generated by Fern from our API Definition. + +from typing import IO, Dict, List, Mapping, Optional, Tuple, Union, cast + +# File typing inspired by the flexibility of types within the httpx library +# https://github.com/encode/httpx/blob/master/httpx/_types.py +FileContent = Union[IO[bytes], bytes, str] +File = Union[ + # file (or bytes) + FileContent, + # (filename, file (or bytes)) + Tuple[Optional[str], FileContent], + # (filename, file (or bytes), content_type) + Tuple[Optional[str], FileContent, Optional[str]], + # (filename, file (or bytes), content_type, headers) + Tuple[ + Optional[str], + FileContent, + Optional[str], + Mapping[str, str], + ], +] + + +def convert_file_dict_to_httpx_tuples( + d: Dict[str, Union[File, List[File]]], +) -> List[Tuple[str, File]]: + """ + The format we use is a list of tuples, where the first element is the + name of the file and the second is the file object. Typically HTTPX wants + a dict, but to be able to send lists of files, you have to use the list + approach (which also works for non-lists) + https://github.com/encode/httpx/pull/1032 + """ + + httpx_tuples = [] + for key, file_like in d.items(): + if isinstance(file_like, list): + for file_like_item in file_like: + httpx_tuples.append((key, file_like_item)) + else: + httpx_tuples.append((key, file_like)) + return httpx_tuples + + +def with_content_type(*, file: File, default_content_type: str) -> File: + """ + This function resolves to the file's content type, if provided, and defaults + to the default_content_type value if not. + """ + if isinstance(file, tuple): + if len(file) == 2: + filename, content = cast(Tuple[Optional[str], FileContent], file) # type: ignore + return (filename, content, default_content_type) + elif len(file) == 3: + filename, content, file_content_type = cast(Tuple[Optional[str], FileContent, Optional[str]], file) # type: ignore + out_content_type = file_content_type or default_content_type + return (filename, content, out_content_type) + elif len(file) == 4: + filename, content, file_content_type, headers = cast( # type: ignore + Tuple[Optional[str], FileContent, Optional[str], Mapping[str, str]], file + ) + out_content_type = file_content_type or default_content_type + return (filename, content, out_content_type, headers) + else: + raise ValueError(f"Unexpected tuple length: {len(file)}") + return (None, file, default_content_type) diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/force_multipart.py b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/force_multipart.py new file mode 100644 index 000000000000..5440913fd4bc --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/force_multipart.py @@ -0,0 +1,18 @@ +# This file was auto-generated by Fern from our API Definition. + +from typing import Any, Dict + + +class ForceMultipartDict(Dict[str, Any]): + """ + A dictionary subclass that always evaluates to True in boolean contexts. + + This is used to force multipart/form-data encoding in HTTP requests even when + the dictionary is empty, which would normally evaluate to False. + """ + + def __bool__(self) -> bool: + return True + + +FORCE_MULTIPART = ForceMultipartDict() diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/http_client.py b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/http_client.py new file mode 100644 index 000000000000..bf19fcc243b6 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/http_client.py @@ -0,0 +1,629 @@ +# This file was auto-generated by Fern from our API Definition. + +import asyncio +import email.utils +import re +import time +import typing +import urllib.parse +from contextlib import asynccontextmanager, contextmanager +from random import random + +import httpx +from .file import File, convert_file_dict_to_httpx_tuples +from .force_multipart import FORCE_MULTIPART +from .jsonable_encoder import jsonable_encoder +from .query_encoder import encode_query +from .remove_none_from_dict import remove_none_from_dict as remove_none_from_dict +from .request_options import RequestOptions +from httpx._types import RequestFiles + +INITIAL_RETRY_DELAY_SECONDS = 1.0 +MAX_RETRY_DELAY_SECONDS = 60.0 +JITTER_FACTOR = 0.2 # 20% random jitter + + +def _parse_retry_after(response_headers: httpx.Headers) -> typing.Optional[float]: + """ + This function parses the `Retry-After` header in a HTTP response and returns the number of seconds to wait. + + Inspired by the urllib3 retry implementation. + """ + retry_after_ms = response_headers.get("retry-after-ms") + if retry_after_ms is not None: + try: + return int(retry_after_ms) / 1000 if retry_after_ms > 0 else 0 + except Exception: + pass + + retry_after = response_headers.get("retry-after") + if retry_after is None: + return None + + # Attempt to parse the header as an int. + if re.match(r"^\s*[0-9]+\s*$", retry_after): + seconds = float(retry_after) + # Fallback to parsing it as a date. + else: + retry_date_tuple = email.utils.parsedate_tz(retry_after) + if retry_date_tuple is None: + return None + if retry_date_tuple[9] is None: # Python 2 + # Assume UTC if no timezone was specified + # On Python2.7, parsedate_tz returns None for a timezone offset + # instead of 0 if no timezone is given, where mktime_tz treats + # a None timezone offset as local time. + retry_date_tuple = retry_date_tuple[:9] + (0,) + retry_date_tuple[10:] + + retry_date = email.utils.mktime_tz(retry_date_tuple) + seconds = retry_date - time.time() + + if seconds < 0: + seconds = 0 + + return seconds + + +def _add_positive_jitter(delay: float) -> float: + """Add positive jitter (0-20%) to prevent thundering herd.""" + jitter_multiplier = 1 + random() * JITTER_FACTOR + return delay * jitter_multiplier + + +def _add_symmetric_jitter(delay: float) -> float: + """Add symmetric jitter (±10%) for exponential backoff.""" + jitter_multiplier = 1 + (random() - 0.5) * JITTER_FACTOR + return delay * jitter_multiplier + + +def _parse_x_ratelimit_reset(response_headers: httpx.Headers) -> typing.Optional[float]: + """ + Parse the X-RateLimit-Reset header (Unix timestamp in seconds). + Returns seconds to wait, or None if header is missing/invalid. + """ + reset_time_str = response_headers.get("x-ratelimit-reset") + if reset_time_str is None: + return None + + try: + reset_time = int(reset_time_str) + delay = reset_time - time.time() + if delay > 0: + return delay + except (ValueError, TypeError): + pass + + return None + + +def _retry_timeout(response: httpx.Response, retries: int) -> float: + """ + Determine the amount of time to wait before retrying a request. + This function begins by trying to parse a retry-after header from the response, and then proceeds to use exponential backoff + with a jitter to determine the number of seconds to wait. + """ + + # 1. Check Retry-After header first + retry_after = _parse_retry_after(response.headers) + if retry_after is not None and retry_after > 0: + return min(retry_after, MAX_RETRY_DELAY_SECONDS) + + # 2. Check X-RateLimit-Reset header (with positive jitter) + ratelimit_reset = _parse_x_ratelimit_reset(response.headers) + if ratelimit_reset is not None: + return _add_positive_jitter(min(ratelimit_reset, MAX_RETRY_DELAY_SECONDS)) + + # 3. Fall back to exponential backoff (with symmetric jitter) + backoff = min(INITIAL_RETRY_DELAY_SECONDS * pow(2.0, retries), MAX_RETRY_DELAY_SECONDS) + return _add_symmetric_jitter(backoff) + + +def _should_retry(response: httpx.Response) -> bool: + retryable_400s = [429, 408, 409] + return response.status_code >= 500 or response.status_code in retryable_400s + + +def _maybe_filter_none_from_multipart_data( + data: typing.Optional[typing.Any], + request_files: typing.Optional[RequestFiles], + force_multipart: typing.Optional[bool], +) -> typing.Optional[typing.Any]: + """ + Filter None values from data body for multipart/form requests. + This prevents httpx from converting None to empty strings in multipart encoding. + Only applies when files are present or force_multipart is True. + """ + if data is not None and isinstance(data, typing.Mapping) and (request_files or force_multipart): + return remove_none_from_dict(data) + return data + + +def remove_omit_from_dict( + original: typing.Dict[str, typing.Optional[typing.Any]], + omit: typing.Optional[typing.Any], +) -> typing.Dict[str, typing.Any]: + if omit is None: + return original + new: typing.Dict[str, typing.Any] = {} + for key, value in original.items(): + if value is not omit: + new[key] = value + return new + + +def maybe_filter_request_body( + data: typing.Optional[typing.Any], + request_options: typing.Optional[RequestOptions], + omit: typing.Optional[typing.Any], +) -> typing.Optional[typing.Any]: + if data is None: + return ( + jsonable_encoder(request_options.get("additional_body_parameters", {})) or {} + if request_options is not None + else None + ) + elif not isinstance(data, typing.Mapping): + data_content = jsonable_encoder(data) + else: + data_content = { + **(jsonable_encoder(remove_omit_from_dict(data, omit))), # type: ignore + **( + jsonable_encoder(request_options.get("additional_body_parameters", {})) or {} + if request_options is not None + else {} + ), + } + return data_content + + +# Abstracted out for testing purposes +def get_request_body( + *, + json: typing.Optional[typing.Any], + data: typing.Optional[typing.Any], + request_options: typing.Optional[RequestOptions], + omit: typing.Optional[typing.Any], +) -> typing.Tuple[typing.Optional[typing.Any], typing.Optional[typing.Any]]: + json_body = None + data_body = None + if data is not None: + data_body = maybe_filter_request_body(data, request_options, omit) + else: + # If both data and json are None, we send json data in the event extra properties are specified + json_body = maybe_filter_request_body(json, request_options, omit) + + # If you have an empty JSON body, you should just send None + return (json_body if json_body != {} else None), data_body if data_body != {} else None + + +class HttpClient: + def __init__( + self, + *, + httpx_client: httpx.Client, + base_timeout: typing.Callable[[], typing.Optional[float]], + base_headers: typing.Callable[[], typing.Dict[str, str]], + base_url: typing.Optional[typing.Callable[[], str]] = None, + ): + self.base_url = base_url + self.base_timeout = base_timeout + self.base_headers = base_headers + self.httpx_client = httpx_client + + def get_base_url(self, maybe_base_url: typing.Optional[str]) -> str: + base_url = maybe_base_url + if self.base_url is not None and base_url is None: + base_url = self.base_url() + + if base_url is None: + raise ValueError("A base_url is required to make this request, please provide one and try again.") + return base_url + + def request( + self, + path: typing.Optional[str] = None, + *, + method: str, + base_url: typing.Optional[str] = None, + params: typing.Optional[typing.Dict[str, typing.Any]] = None, + json: typing.Optional[typing.Any] = None, + data: typing.Optional[typing.Any] = None, + content: typing.Optional[typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]] = None, + files: typing.Optional[ + typing.Union[ + typing.Dict[str, typing.Optional[typing.Union[File, typing.List[File]]]], + typing.List[typing.Tuple[str, File]], + ] + ] = None, + headers: typing.Optional[typing.Dict[str, typing.Any]] = None, + request_options: typing.Optional[RequestOptions] = None, + retries: int = 2, + omit: typing.Optional[typing.Any] = None, + force_multipart: typing.Optional[bool] = None, + ) -> httpx.Response: + base_url = self.get_base_url(base_url) + timeout = ( + request_options.get("timeout_in_seconds") + if request_options is not None and request_options.get("timeout_in_seconds") is not None + else self.base_timeout() + ) + + json_body, data_body = get_request_body(json=json, data=data, request_options=request_options, omit=omit) + + request_files: typing.Optional[RequestFiles] = ( + convert_file_dict_to_httpx_tuples(remove_omit_from_dict(remove_none_from_dict(files), omit)) + if (files is not None and files is not omit and isinstance(files, dict)) + else None + ) + + if (request_files is None or len(request_files) == 0) and force_multipart: + request_files = FORCE_MULTIPART + + data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + + response = self.httpx_client.request( + method=method, + url=urllib.parse.urljoin(f"{base_url}/", path), + headers=jsonable_encoder( + remove_none_from_dict( + { + **self.base_headers(), + **(headers if headers is not None else {}), + **(request_options.get("additional_headers", {}) or {} if request_options is not None else {}), + } + ) + ), + params=_encoded_params if _encoded_params else None, + json=json_body, + data=data_body, + content=content, + files=request_files, + timeout=timeout, + ) + + max_retries: int = request_options.get("max_retries", 0) if request_options is not None else 0 + if _should_retry(response=response): + if max_retries > retries: + time.sleep(_retry_timeout(response=response, retries=retries)) + return self.request( + path=path, + method=method, + base_url=base_url, + params=params, + json=json, + content=content, + files=files, + headers=headers, + request_options=request_options, + retries=retries + 1, + omit=omit, + ) + + return response + + @contextmanager + def stream( + self, + path: typing.Optional[str] = None, + *, + method: str, + base_url: typing.Optional[str] = None, + params: typing.Optional[typing.Dict[str, typing.Any]] = None, + json: typing.Optional[typing.Any] = None, + data: typing.Optional[typing.Any] = None, + content: typing.Optional[typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]] = None, + files: typing.Optional[ + typing.Union[ + typing.Dict[str, typing.Optional[typing.Union[File, typing.List[File]]]], + typing.List[typing.Tuple[str, File]], + ] + ] = None, + headers: typing.Optional[typing.Dict[str, typing.Any]] = None, + request_options: typing.Optional[RequestOptions] = None, + retries: int = 2, + omit: typing.Optional[typing.Any] = None, + force_multipart: typing.Optional[bool] = None, + ) -> typing.Iterator[httpx.Response]: + base_url = self.get_base_url(base_url) + timeout = ( + request_options.get("timeout_in_seconds") + if request_options is not None and request_options.get("timeout_in_seconds") is not None + else self.base_timeout() + ) + + request_files: typing.Optional[RequestFiles] = ( + convert_file_dict_to_httpx_tuples(remove_omit_from_dict(remove_none_from_dict(files), omit)) + if (files is not None and files is not omit and isinstance(files, dict)) + else None + ) + + if (request_files is None or len(request_files) == 0) and force_multipart: + request_files = FORCE_MULTIPART + + json_body, data_body = get_request_body(json=json, data=data, request_options=request_options, omit=omit) + + data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + + with self.httpx_client.stream( + method=method, + url=urllib.parse.urljoin(f"{base_url}/", path), + headers=jsonable_encoder( + remove_none_from_dict( + { + **self.base_headers(), + **(headers if headers is not None else {}), + **(request_options.get("additional_headers", {}) if request_options is not None else {}), + } + ) + ), + params=_encoded_params if _encoded_params else None, + json=json_body, + data=data_body, + content=content, + files=request_files, + timeout=timeout, + ) as stream: + yield stream + + +class AsyncHttpClient: + def __init__( + self, + *, + httpx_client: httpx.AsyncClient, + base_timeout: typing.Callable[[], typing.Optional[float]], + base_headers: typing.Callable[[], typing.Dict[str, str]], + base_url: typing.Optional[typing.Callable[[], str]] = None, + async_base_headers: typing.Optional[typing.Callable[[], typing.Awaitable[typing.Dict[str, str]]]] = None, + ): + self.base_url = base_url + self.base_timeout = base_timeout + self.base_headers = base_headers + self.async_base_headers = async_base_headers + self.httpx_client = httpx_client + + async def _get_headers(self) -> typing.Dict[str, str]: + if self.async_base_headers is not None: + return await self.async_base_headers() + return self.base_headers() + + def get_base_url(self, maybe_base_url: typing.Optional[str]) -> str: + base_url = maybe_base_url + if self.base_url is not None and base_url is None: + base_url = self.base_url() + + if base_url is None: + raise ValueError("A base_url is required to make this request, please provide one and try again.") + return base_url + + async def request( + self, + path: typing.Optional[str] = None, + *, + method: str, + base_url: typing.Optional[str] = None, + params: typing.Optional[typing.Dict[str, typing.Any]] = None, + json: typing.Optional[typing.Any] = None, + data: typing.Optional[typing.Any] = None, + content: typing.Optional[typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]] = None, + files: typing.Optional[ + typing.Union[ + typing.Dict[str, typing.Optional[typing.Union[File, typing.List[File]]]], + typing.List[typing.Tuple[str, File]], + ] + ] = None, + headers: typing.Optional[typing.Dict[str, typing.Any]] = None, + request_options: typing.Optional[RequestOptions] = None, + retries: int = 2, + omit: typing.Optional[typing.Any] = None, + force_multipart: typing.Optional[bool] = None, + ) -> httpx.Response: + base_url = self.get_base_url(base_url) + timeout = ( + request_options.get("timeout_in_seconds") + if request_options is not None and request_options.get("timeout_in_seconds") is not None + else self.base_timeout() + ) + + request_files: typing.Optional[RequestFiles] = ( + convert_file_dict_to_httpx_tuples(remove_omit_from_dict(remove_none_from_dict(files), omit)) + if (files is not None and files is not omit and isinstance(files, dict)) + else None + ) + + if (request_files is None or len(request_files) == 0) and force_multipart: + request_files = FORCE_MULTIPART + + json_body, data_body = get_request_body(json=json, data=data, request_options=request_options, omit=omit) + + data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + + # Get headers (supports async token providers) + _headers = await self._get_headers() + + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + + # Add the input to each of these and do None-safety checks + response = await self.httpx_client.request( + method=method, + url=urllib.parse.urljoin(f"{base_url}/", path), + headers=jsonable_encoder( + remove_none_from_dict( + { + **_headers, + **(headers if headers is not None else {}), + **(request_options.get("additional_headers", {}) or {} if request_options is not None else {}), + } + ) + ), + params=_encoded_params if _encoded_params else None, + json=json_body, + data=data_body, + content=content, + files=request_files, + timeout=timeout, + ) + + max_retries: int = request_options.get("max_retries", 0) if request_options is not None else 0 + if _should_retry(response=response): + if max_retries > retries: + await asyncio.sleep(_retry_timeout(response=response, retries=retries)) + return await self.request( + path=path, + method=method, + base_url=base_url, + params=params, + json=json, + content=content, + files=files, + headers=headers, + request_options=request_options, + retries=retries + 1, + omit=omit, + ) + return response + + @asynccontextmanager + async def stream( + self, + path: typing.Optional[str] = None, + *, + method: str, + base_url: typing.Optional[str] = None, + params: typing.Optional[typing.Dict[str, typing.Any]] = None, + json: typing.Optional[typing.Any] = None, + data: typing.Optional[typing.Any] = None, + content: typing.Optional[typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]] = None, + files: typing.Optional[ + typing.Union[ + typing.Dict[str, typing.Optional[typing.Union[File, typing.List[File]]]], + typing.List[typing.Tuple[str, File]], + ] + ] = None, + headers: typing.Optional[typing.Dict[str, typing.Any]] = None, + request_options: typing.Optional[RequestOptions] = None, + retries: int = 2, + omit: typing.Optional[typing.Any] = None, + force_multipart: typing.Optional[bool] = None, + ) -> typing.AsyncIterator[httpx.Response]: + base_url = self.get_base_url(base_url) + timeout = ( + request_options.get("timeout_in_seconds") + if request_options is not None and request_options.get("timeout_in_seconds") is not None + else self.base_timeout() + ) + + request_files: typing.Optional[RequestFiles] = ( + convert_file_dict_to_httpx_tuples(remove_omit_from_dict(remove_none_from_dict(files), omit)) + if (files is not None and files is not omit and isinstance(files, dict)) + else None + ) + + if (request_files is None or len(request_files) == 0) and force_multipart: + request_files = FORCE_MULTIPART + + json_body, data_body = get_request_body(json=json, data=data, request_options=request_options, omit=omit) + + data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + + # Get headers (supports async token providers) + _headers = await self._get_headers() + + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + + async with self.httpx_client.stream( + method=method, + url=urllib.parse.urljoin(f"{base_url}/", path), + headers=jsonable_encoder( + remove_none_from_dict( + { + **_headers, + **(headers if headers is not None else {}), + **(request_options.get("additional_headers", {}) if request_options is not None else {}), + } + ) + ), + params=_encoded_params if _encoded_params else None, + json=json_body, + data=data_body, + content=content, + files=request_files, + timeout=timeout, + ) as stream: + yield stream diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/http_response.py b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/http_response.py new file mode 100644 index 000000000000..2479747e8bb0 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/http_response.py @@ -0,0 +1,55 @@ +# This file was auto-generated by Fern from our API Definition. + +from typing import Dict, Generic, TypeVar + +import httpx + +# Generic to represent the underlying type of the data wrapped by the HTTP response. +T = TypeVar("T") + + +class BaseHttpResponse: + """Minimalist HTTP response wrapper that exposes response headers.""" + + _response: httpx.Response + + def __init__(self, response: httpx.Response): + self._response = response + + @property + def headers(self) -> Dict[str, str]: + return dict(self._response.headers) + + +class HttpResponse(Generic[T], BaseHttpResponse): + """HTTP response wrapper that exposes response headers and data.""" + + _data: T + + def __init__(self, response: httpx.Response, data: T): + super().__init__(response) + self._data = data + + @property + def data(self) -> T: + return self._data + + def close(self) -> None: + self._response.close() + + +class AsyncHttpResponse(Generic[T], BaseHttpResponse): + """HTTP response wrapper that exposes response headers and data.""" + + _data: T + + def __init__(self, response: httpx.Response, data: T): + super().__init__(response) + self._data = data + + @property + def data(self) -> T: + return self._data + + async def close(self) -> None: + await self._response.aclose() diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/http_sse/__init__.py b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/http_sse/__init__.py new file mode 100644 index 000000000000..730e5a3382eb --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/http_sse/__init__.py @@ -0,0 +1,42 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from ._api import EventSource, aconnect_sse, connect_sse + from ._exceptions import SSEError + from ._models import ServerSentEvent +_dynamic_imports: typing.Dict[str, str] = { + "EventSource": "._api", + "SSEError": "._exceptions", + "ServerSentEvent": "._models", + "aconnect_sse": "._api", + "connect_sse": "._api", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["EventSource", "SSEError", "ServerSentEvent", "aconnect_sse", "connect_sse"] diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/http_sse/_api.py b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/http_sse/_api.py new file mode 100644 index 000000000000..f900b3b686de --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/http_sse/_api.py @@ -0,0 +1,112 @@ +# This file was auto-generated by Fern from our API Definition. + +import re +from contextlib import asynccontextmanager, contextmanager +from typing import Any, AsyncGenerator, AsyncIterator, Iterator, cast + +import httpx +from ._decoders import SSEDecoder +from ._exceptions import SSEError +from ._models import ServerSentEvent + + +class EventSource: + def __init__(self, response: httpx.Response) -> None: + self._response = response + + def _check_content_type(self) -> None: + content_type = self._response.headers.get("content-type", "").partition(";")[0] + if "text/event-stream" not in content_type: + raise SSEError( + f"Expected response header Content-Type to contain 'text/event-stream', got {content_type!r}" + ) + + def _get_charset(self) -> str: + """Extract charset from Content-Type header, fallback to UTF-8.""" + content_type = self._response.headers.get("content-type", "") + + # Parse charset parameter using regex + charset_match = re.search(r"charset=([^;\s]+)", content_type, re.IGNORECASE) + if charset_match: + charset = charset_match.group(1).strip("\"'") + # Validate that it's a known encoding + try: + # Test if the charset is valid by trying to encode/decode + "test".encode(charset).decode(charset) + return charset + except (LookupError, UnicodeError): + # If charset is invalid, fall back to UTF-8 + pass + + # Default to UTF-8 if no charset specified or invalid charset + return "utf-8" + + @property + def response(self) -> httpx.Response: + return self._response + + def iter_sse(self) -> Iterator[ServerSentEvent]: + self._check_content_type() + decoder = SSEDecoder() + charset = self._get_charset() + + buffer = "" + for chunk in self._response.iter_bytes(): + # Decode chunk using detected charset + text_chunk = chunk.decode(charset, errors="replace") + buffer += text_chunk + + # Process complete lines + while "\n" in buffer: + line, buffer = buffer.split("\n", 1) + line = line.rstrip("\r") + sse = decoder.decode(line) + # when we reach a "\n\n" => line = '' + # => decoder will attempt to return an SSE Event + if sse is not None: + yield sse + + # Process any remaining data in buffer + if buffer.strip(): + line = buffer.rstrip("\r") + sse = decoder.decode(line) + if sse is not None: + yield sse + + async def aiter_sse(self) -> AsyncGenerator[ServerSentEvent, None]: + self._check_content_type() + decoder = SSEDecoder() + lines = cast(AsyncGenerator[str, None], self._response.aiter_lines()) + try: + async for line in lines: + line = line.rstrip("\n") + sse = decoder.decode(line) + if sse is not None: + yield sse + finally: + await lines.aclose() + + +@contextmanager +def connect_sse(client: httpx.Client, method: str, url: str, **kwargs: Any) -> Iterator[EventSource]: + headers = kwargs.pop("headers", {}) + headers["Accept"] = "text/event-stream" + headers["Cache-Control"] = "no-store" + + with client.stream(method, url, headers=headers, **kwargs) as response: + yield EventSource(response) + + +@asynccontextmanager +async def aconnect_sse( + client: httpx.AsyncClient, + method: str, + url: str, + **kwargs: Any, +) -> AsyncIterator[EventSource]: + headers = kwargs.pop("headers", {}) + headers["Accept"] = "text/event-stream" + headers["Cache-Control"] = "no-store" + + async with client.stream(method, url, headers=headers, **kwargs) as response: + yield EventSource(response) diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/http_sse/_decoders.py b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/http_sse/_decoders.py new file mode 100644 index 000000000000..339b08901381 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/http_sse/_decoders.py @@ -0,0 +1,61 @@ +# This file was auto-generated by Fern from our API Definition. + +from typing import List, Optional + +from ._models import ServerSentEvent + + +class SSEDecoder: + def __init__(self) -> None: + self._event = "" + self._data: List[str] = [] + self._last_event_id = "" + self._retry: Optional[int] = None + + def decode(self, line: str) -> Optional[ServerSentEvent]: + # See: https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation # noqa: E501 + + if not line: + if not self._event and not self._data and not self._last_event_id and self._retry is None: + return None + + sse = ServerSentEvent( + event=self._event, + data="\n".join(self._data), + id=self._last_event_id, + retry=self._retry, + ) + + # NOTE: as per the SSE spec, do not reset last_event_id. + self._event = "" + self._data = [] + self._retry = None + + return sse + + if line.startswith(":"): + return None + + fieldname, _, value = line.partition(":") + + if value.startswith(" "): + value = value[1:] + + if fieldname == "event": + self._event = value + elif fieldname == "data": + self._data.append(value) + elif fieldname == "id": + if "\0" in value: + pass + else: + self._last_event_id = value + elif fieldname == "retry": + try: + self._retry = int(value) + except (TypeError, ValueError): + pass + else: + pass # Field is ignored. + + return None diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/http_sse/_exceptions.py b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/http_sse/_exceptions.py new file mode 100644 index 000000000000..81605a8a65ed --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/http_sse/_exceptions.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import httpx + + +class SSEError(httpx.TransportError): + pass diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/http_sse/_models.py b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/http_sse/_models.py new file mode 100644 index 000000000000..1af57f8fd0d2 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/http_sse/_models.py @@ -0,0 +1,17 @@ +# This file was auto-generated by Fern from our API Definition. + +import json +from dataclasses import dataclass +from typing import Any, Optional + + +@dataclass(frozen=True) +class ServerSentEvent: + event: str = "message" + data: str = "" + id: str = "" + retry: Optional[int] = None + + def json(self) -> Any: + """Parse the data field as JSON.""" + return json.loads(self.data) diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/jsonable_encoder.py b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/jsonable_encoder.py new file mode 100644 index 000000000000..afee3662d836 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/jsonable_encoder.py @@ -0,0 +1,100 @@ +# This file was auto-generated by Fern from our API Definition. + +""" +jsonable_encoder converts a Python object to a JSON-friendly dict +(e.g. datetimes to strings, Pydantic models to dicts). + +Taken from FastAPI, and made a bit simpler +https://github.com/tiangolo/fastapi/blob/master/fastapi/encoders.py +""" + +import base64 +import dataclasses +import datetime as dt +from enum import Enum +from pathlib import PurePath +from types import GeneratorType +from typing import Any, Callable, Dict, List, Optional, Set, Union + +import pydantic +from .datetime_utils import serialize_datetime +from .pydantic_utilities import ( + IS_PYDANTIC_V2, + encode_by_type, + to_jsonable_with_fallback, +) + +SetIntStr = Set[Union[int, str]] +DictIntStrAny = Dict[Union[int, str], Any] + + +def jsonable_encoder(obj: Any, custom_encoder: Optional[Dict[Any, Callable[[Any], Any]]] = None) -> Any: + custom_encoder = custom_encoder or {} + if custom_encoder: + if type(obj) in custom_encoder: + return custom_encoder[type(obj)](obj) + else: + for encoder_type, encoder_instance in custom_encoder.items(): + if isinstance(obj, encoder_type): + return encoder_instance(obj) + if isinstance(obj, pydantic.BaseModel): + if IS_PYDANTIC_V2: + encoder = getattr(obj.model_config, "json_encoders", {}) # type: ignore # Pydantic v2 + else: + encoder = getattr(obj.__config__, "json_encoders", {}) # type: ignore # Pydantic v1 + if custom_encoder: + encoder.update(custom_encoder) + obj_dict = obj.dict(by_alias=True) + if "__root__" in obj_dict: + obj_dict = obj_dict["__root__"] + if "root" in obj_dict: + obj_dict = obj_dict["root"] + return jsonable_encoder(obj_dict, custom_encoder=encoder) + if dataclasses.is_dataclass(obj): + obj_dict = dataclasses.asdict(obj) # type: ignore + return jsonable_encoder(obj_dict, custom_encoder=custom_encoder) + if isinstance(obj, bytes): + return base64.b64encode(obj).decode("utf-8") + if isinstance(obj, Enum): + return obj.value + if isinstance(obj, PurePath): + return str(obj) + if isinstance(obj, (str, int, float, type(None))): + return obj + if isinstance(obj, dt.datetime): + return serialize_datetime(obj) + if isinstance(obj, dt.date): + return str(obj) + if isinstance(obj, dict): + encoded_dict = {} + allowed_keys = set(obj.keys()) + for key, value in obj.items(): + if key in allowed_keys: + encoded_key = jsonable_encoder(key, custom_encoder=custom_encoder) + encoded_value = jsonable_encoder(value, custom_encoder=custom_encoder) + encoded_dict[encoded_key] = encoded_value + return encoded_dict + if isinstance(obj, (list, set, frozenset, GeneratorType, tuple)): + encoded_list = [] + for item in obj: + encoded_list.append(jsonable_encoder(item, custom_encoder=custom_encoder)) + return encoded_list + + def fallback_serializer(o: Any) -> Any: + attempt_encode = encode_by_type(o) + if attempt_encode is not None: + return attempt_encode + + try: + data = dict(o) + except Exception as e: + errors: List[Exception] = [] + errors.append(e) + try: + data = vars(o) + except Exception as e: + errors.append(e) + raise ValueError(errors) from e + return jsonable_encoder(data, custom_encoder=custom_encoder) + + return to_jsonable_with_fallback(obj, fallback_serializer) diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/pydantic_utilities.py b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/pydantic_utilities.py new file mode 100644 index 000000000000..185e5c4f64be --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/pydantic_utilities.py @@ -0,0 +1,260 @@ +# This file was auto-generated by Fern from our API Definition. + +# nopycln: file +import datetime as dt +from collections import defaultdict +from typing import Any, Callable, ClassVar, Dict, List, Mapping, Optional, Set, Tuple, Type, TypeVar, Union, cast + +import pydantic + +IS_PYDANTIC_V2 = pydantic.VERSION.startswith("2.") + +if IS_PYDANTIC_V2: + from pydantic.v1.datetime_parse import parse_date as parse_date + from pydantic.v1.datetime_parse import parse_datetime as parse_datetime + from pydantic.v1.fields import ModelField as ModelField + from pydantic.v1.json import ENCODERS_BY_TYPE as encoders_by_type # type: ignore[attr-defined] + from pydantic.v1.typing import get_args as get_args + from pydantic.v1.typing import get_origin as get_origin + from pydantic.v1.typing import is_literal_type as is_literal_type + from pydantic.v1.typing import is_union as is_union +else: + from pydantic.datetime_parse import parse_date as parse_date # type: ignore[no-redef] + from pydantic.datetime_parse import parse_datetime as parse_datetime # type: ignore[no-redef] + from pydantic.fields import ModelField as ModelField # type: ignore[attr-defined, no-redef] + from pydantic.json import ENCODERS_BY_TYPE as encoders_by_type # type: ignore[no-redef] + from pydantic.typing import get_args as get_args # type: ignore[no-redef] + from pydantic.typing import get_origin as get_origin # type: ignore[no-redef] + from pydantic.typing import is_literal_type as is_literal_type # type: ignore[no-redef] + from pydantic.typing import is_union as is_union # type: ignore[no-redef] + +from .datetime_utils import serialize_datetime +from .serialization import convert_and_respect_annotation_metadata +from typing_extensions import TypeAlias + +T = TypeVar("T") +Model = TypeVar("Model", bound=pydantic.BaseModel) + + +def parse_obj_as(type_: Type[T], object_: Any) -> T: + dealiased_object = convert_and_respect_annotation_metadata(object_=object_, annotation=type_, direction="read") + if IS_PYDANTIC_V2: + adapter = pydantic.TypeAdapter(type_) # type: ignore[attr-defined] + return adapter.validate_python(dealiased_object) + return pydantic.parse_obj_as(type_, dealiased_object) + + +def to_jsonable_with_fallback(obj: Any, fallback_serializer: Callable[[Any], Any]) -> Any: + if IS_PYDANTIC_V2: + from pydantic_core import to_jsonable_python + + return to_jsonable_python(obj, fallback=fallback_serializer) + return fallback_serializer(obj) + + +class UniversalBaseModel(pydantic.BaseModel): + if IS_PYDANTIC_V2: + model_config: ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( # type: ignore[typeddict-unknown-key] + # Allow fields beginning with `model_` to be used in the model + protected_namespaces=(), + ) + + @pydantic.model_serializer(mode="plain", when_used="json") # type: ignore[attr-defined] + def serialize_model(self) -> Any: # type: ignore[name-defined] + serialized = self.dict() # type: ignore[attr-defined] + data = {k: serialize_datetime(v) if isinstance(v, dt.datetime) else v for k, v in serialized.items()} + return data + + else: + + class Config: + smart_union = True + json_encoders = {dt.datetime: serialize_datetime} + + @classmethod + def model_construct(cls: Type["Model"], _fields_set: Optional[Set[str]] = None, **values: Any) -> "Model": + dealiased_object = convert_and_respect_annotation_metadata(object_=values, annotation=cls, direction="read") + return cls.construct(_fields_set, **dealiased_object) + + @classmethod + def construct(cls: Type["Model"], _fields_set: Optional[Set[str]] = None, **values: Any) -> "Model": + dealiased_object = convert_and_respect_annotation_metadata(object_=values, annotation=cls, direction="read") + if IS_PYDANTIC_V2: + return super().model_construct(_fields_set, **dealiased_object) # type: ignore[misc] + return super().construct(_fields_set, **dealiased_object) + + def json(self, **kwargs: Any) -> str: + kwargs_with_defaults = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + if IS_PYDANTIC_V2: + return super().model_dump_json(**kwargs_with_defaults) # type: ignore[misc] + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: Any) -> Dict[str, Any]: + """ + Override the default dict method to `exclude_unset` by default. This function patches + `exclude_unset` to work include fields within non-None default values. + """ + # Note: the logic here is multiplexed given the levers exposed in Pydantic V1 vs V2 + # Pydantic V1's .dict can be extremely slow, so we do not want to call it twice. + # + # We'd ideally do the same for Pydantic V2, but it shells out to a library to serialize models + # that we have less control over, and this is less intrusive than custom serializers for now. + if IS_PYDANTIC_V2: + kwargs_with_defaults_exclude_unset = { + **kwargs, + "by_alias": True, + "exclude_unset": True, + "exclude_none": False, + } + kwargs_with_defaults_exclude_none = { + **kwargs, + "by_alias": True, + "exclude_none": True, + "exclude_unset": False, + } + dict_dump = deep_union_pydantic_dicts( + super().model_dump(**kwargs_with_defaults_exclude_unset), # type: ignore[misc] + super().model_dump(**kwargs_with_defaults_exclude_none), # type: ignore[misc] + ) + + else: + _fields_set = self.__fields_set__.copy() + + fields = _get_model_fields(self.__class__) + for name, field in fields.items(): + if name not in _fields_set: + default = _get_field_default(field) + + # If the default values are non-null act like they've been set + # This effectively allows exclude_unset to work like exclude_none where + # the latter passes through intentionally set none values. + if default is not None or ("exclude_unset" in kwargs and not kwargs["exclude_unset"]): + _fields_set.add(name) + + if default is not None: + self.__fields_set__.add(name) + + kwargs_with_defaults_exclude_unset_include_fields = { + "by_alias": True, + "exclude_unset": True, + "include": _fields_set, + **kwargs, + } + + dict_dump = super().dict(**kwargs_with_defaults_exclude_unset_include_fields) + + return cast( + Dict[str, Any], + convert_and_respect_annotation_metadata(object_=dict_dump, annotation=self.__class__, direction="write"), + ) + + +def _union_list_of_pydantic_dicts(source: List[Any], destination: List[Any]) -> List[Any]: + converted_list: List[Any] = [] + for i, item in enumerate(source): + destination_value = destination[i] + if isinstance(item, dict): + converted_list.append(deep_union_pydantic_dicts(item, destination_value)) + elif isinstance(item, list): + converted_list.append(_union_list_of_pydantic_dicts(item, destination_value)) + else: + converted_list.append(item) + return converted_list + + +def deep_union_pydantic_dicts(source: Dict[str, Any], destination: Dict[str, Any]) -> Dict[str, Any]: + for key, value in source.items(): + node = destination.setdefault(key, {}) + if isinstance(value, dict): + deep_union_pydantic_dicts(value, node) + # Note: we do not do this same processing for sets given we do not have sets of models + # and given the sets are unordered, the processing of the set and matching objects would + # be non-trivial. + elif isinstance(value, list): + destination[key] = _union_list_of_pydantic_dicts(value, node) + else: + destination[key] = value + + return destination + + +if IS_PYDANTIC_V2: + + class V2RootModel(UniversalBaseModel, pydantic.RootModel): # type: ignore[misc, name-defined, type-arg] + pass + + UniversalRootModel: TypeAlias = V2RootModel # type: ignore[misc] +else: + UniversalRootModel: TypeAlias = UniversalBaseModel # type: ignore[misc, no-redef] + + +def encode_by_type(o: Any) -> Any: + encoders_by_class_tuples: Dict[Callable[[Any], Any], Tuple[Any, ...]] = defaultdict(tuple) + for type_, encoder in encoders_by_type.items(): + encoders_by_class_tuples[encoder] += (type_,) + + if type(o) in encoders_by_type: + return encoders_by_type[type(o)](o) + for encoder, classes_tuple in encoders_by_class_tuples.items(): + if isinstance(o, classes_tuple): + return encoder(o) + + +def update_forward_refs(model: Type["Model"], **localns: Any) -> None: + if IS_PYDANTIC_V2: + model.model_rebuild(raise_errors=False) # type: ignore[attr-defined] + else: + model.update_forward_refs(**localns) + + +# Mirrors Pydantic's internal typing +AnyCallable = Callable[..., Any] + + +def universal_root_validator( + pre: bool = False, +) -> Callable[[AnyCallable], AnyCallable]: + def decorator(func: AnyCallable) -> AnyCallable: + if IS_PYDANTIC_V2: + # In Pydantic v2, for RootModel we always use "before" mode + # The custom validators transform the input value before the model is created + return cast(AnyCallable, pydantic.model_validator(mode="before")(func)) # type: ignore[attr-defined] + return cast(AnyCallable, pydantic.root_validator(pre=pre)(func)) # type: ignore[call-overload] + + return decorator + + +def universal_field_validator(field_name: str, pre: bool = False) -> Callable[[AnyCallable], AnyCallable]: + def decorator(func: AnyCallable) -> AnyCallable: + if IS_PYDANTIC_V2: + return cast(AnyCallable, pydantic.field_validator(field_name, mode="before" if pre else "after")(func)) # type: ignore[attr-defined] + return cast(AnyCallable, pydantic.validator(field_name, pre=pre)(func)) + + return decorator + + +PydanticField = Union[ModelField, pydantic.fields.FieldInfo] + + +def _get_model_fields(model: Type["Model"]) -> Mapping[str, PydanticField]: + if IS_PYDANTIC_V2: + return cast(Mapping[str, PydanticField], model.model_fields) # type: ignore[attr-defined] + return cast(Mapping[str, PydanticField], model.__fields__) + + +def _get_field_default(field: PydanticField) -> Any: + try: + value = field.get_default() # type: ignore[union-attr] + except: + value = field.default + if IS_PYDANTIC_V2: + from pydantic_core import PydanticUndefined + + if value == PydanticUndefined: + return None + return value + return value diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/query_encoder.py b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/query_encoder.py new file mode 100644 index 000000000000..3183001d4046 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/query_encoder.py @@ -0,0 +1,58 @@ +# This file was auto-generated by Fern from our API Definition. + +from typing import Any, Dict, List, Optional, Tuple + +import pydantic + + +# Flattens dicts to be of the form {"key[subkey][subkey2]": value} where value is not a dict +def traverse_query_dict(dict_flat: Dict[str, Any], key_prefix: Optional[str] = None) -> List[Tuple[str, Any]]: + result = [] + for k, v in dict_flat.items(): + key = f"{key_prefix}[{k}]" if key_prefix is not None else k + if isinstance(v, dict): + result.extend(traverse_query_dict(v, key)) + elif isinstance(v, list): + for arr_v in v: + if isinstance(arr_v, dict): + result.extend(traverse_query_dict(arr_v, key)) + else: + result.append((key, arr_v)) + else: + result.append((key, v)) + return result + + +def single_query_encoder(query_key: str, query_value: Any) -> List[Tuple[str, Any]]: + if isinstance(query_value, pydantic.BaseModel) or isinstance(query_value, dict): + if isinstance(query_value, pydantic.BaseModel): + obj_dict = query_value.dict(by_alias=True) + else: + obj_dict = query_value + return traverse_query_dict(obj_dict, query_key) + elif isinstance(query_value, list): + encoded_values: List[Tuple[str, Any]] = [] + for value in query_value: + if isinstance(value, pydantic.BaseModel) or isinstance(value, dict): + if isinstance(value, pydantic.BaseModel): + obj_dict = value.dict(by_alias=True) + elif isinstance(value, dict): + obj_dict = value + + encoded_values.extend(single_query_encoder(query_key, obj_dict)) + else: + encoded_values.append((query_key, value)) + + return encoded_values + + return [(query_key, query_value)] + + +def encode_query(query: Optional[Dict[str, Any]]) -> Optional[List[Tuple[str, Any]]]: + if query is None: + return None + + encoded_query = [] + for k, v in query.items(): + encoded_query.extend(single_query_encoder(k, v)) + return encoded_query diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/remove_none_from_dict.py b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/remove_none_from_dict.py new file mode 100644 index 000000000000..c2298143f14a --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/remove_none_from_dict.py @@ -0,0 +1,11 @@ +# This file was auto-generated by Fern from our API Definition. + +from typing import Any, Dict, Mapping, Optional + + +def remove_none_from_dict(original: Mapping[str, Optional[Any]]) -> Dict[str, Any]: + new: Dict[str, Any] = {} + for key, value in original.items(): + if value is not None: + new[key] = value + return new diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/request_options.py b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/request_options.py new file mode 100644 index 000000000000..1b38804432ba --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/request_options.py @@ -0,0 +1,35 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +try: + from typing import NotRequired # type: ignore +except ImportError: + from typing_extensions import NotRequired + + +class RequestOptions(typing.TypedDict, total=False): + """ + Additional options for request-specific configuration when calling APIs via the SDK. + This is used primarily as an optional final parameter for service functions. + + Attributes: + - timeout_in_seconds: int. The number of seconds to await an API call before timing out. + + - max_retries: int. The max number of retries to attempt if the API call fails. + + - additional_headers: typing.Dict[str, typing.Any]. A dictionary containing additional parameters to spread into the request's header dict + + - additional_query_parameters: typing.Dict[str, typing.Any]. A dictionary containing additional parameters to spread into the request's query parameters dict + + - additional_body_parameters: typing.Dict[str, typing.Any]. A dictionary containing additional parameters to spread into the request's body parameters dict + + - chunk_size: int. The size, in bytes, to process each chunk of data being streamed back within the response. This equates to leveraging `chunk_size` within `requests` or `httpx`, and is only leveraged for file downloads. + """ + + timeout_in_seconds: NotRequired[int] + max_retries: NotRequired[int] + additional_headers: NotRequired[typing.Dict[str, typing.Any]] + additional_query_parameters: NotRequired[typing.Dict[str, typing.Any]] + additional_body_parameters: NotRequired[typing.Dict[str, typing.Any]] + chunk_size: NotRequired[int] diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/serialization.py b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/serialization.py new file mode 100644 index 000000000000..c36e865cc729 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/core/serialization.py @@ -0,0 +1,276 @@ +# This file was auto-generated by Fern from our API Definition. + +import collections +import inspect +import typing + +import pydantic +import typing_extensions + + +class FieldMetadata: + """ + Metadata class used to annotate fields to provide additional information. + + Example: + class MyDict(TypedDict): + field: typing.Annotated[str, FieldMetadata(alias="field_name")] + + Will serialize: `{"field": "value"}` + To: `{"field_name": "value"}` + """ + + alias: str + + def __init__(self, *, alias: str) -> None: + self.alias = alias + + +def convert_and_respect_annotation_metadata( + *, + object_: typing.Any, + annotation: typing.Any, + inner_type: typing.Optional[typing.Any] = None, + direction: typing.Literal["read", "write"], +) -> typing.Any: + """ + Respect the metadata annotations on a field, such as aliasing. This function effectively + manipulates the dict-form of an object to respect the metadata annotations. This is primarily used for + TypedDicts, which cannot support aliasing out of the box, and can be extended for additional + utilities, such as defaults. + + Parameters + ---------- + object_ : typing.Any + + annotation : type + The type we're looking to apply typing annotations from + + inner_type : typing.Optional[type] + + Returns + ------- + typing.Any + """ + + if object_ is None: + return None + if inner_type is None: + inner_type = annotation + + clean_type = _remove_annotations(inner_type) + # Pydantic models + if ( + inspect.isclass(clean_type) + and issubclass(clean_type, pydantic.BaseModel) + and isinstance(object_, typing.Mapping) + ): + return _convert_mapping(object_, clean_type, direction) + # TypedDicts + if typing_extensions.is_typeddict(clean_type) and isinstance(object_, typing.Mapping): + return _convert_mapping(object_, clean_type, direction) + + if ( + typing_extensions.get_origin(clean_type) == typing.Dict + or typing_extensions.get_origin(clean_type) == dict + or clean_type == typing.Dict + ) and isinstance(object_, typing.Dict): + key_type = typing_extensions.get_args(clean_type)[0] + value_type = typing_extensions.get_args(clean_type)[1] + + return { + key: convert_and_respect_annotation_metadata( + object_=value, + annotation=annotation, + inner_type=value_type, + direction=direction, + ) + for key, value in object_.items() + } + + # If you're iterating on a string, do not bother to coerce it to a sequence. + if not isinstance(object_, str): + if ( + typing_extensions.get_origin(clean_type) == typing.Set + or typing_extensions.get_origin(clean_type) == set + or clean_type == typing.Set + ) and isinstance(object_, typing.Set): + inner_type = typing_extensions.get_args(clean_type)[0] + return { + convert_and_respect_annotation_metadata( + object_=item, + annotation=annotation, + inner_type=inner_type, + direction=direction, + ) + for item in object_ + } + elif ( + ( + typing_extensions.get_origin(clean_type) == typing.List + or typing_extensions.get_origin(clean_type) == list + or clean_type == typing.List + ) + and isinstance(object_, typing.List) + ) or ( + ( + typing_extensions.get_origin(clean_type) == typing.Sequence + or typing_extensions.get_origin(clean_type) == collections.abc.Sequence + or clean_type == typing.Sequence + ) + and isinstance(object_, typing.Sequence) + ): + inner_type = typing_extensions.get_args(clean_type)[0] + return [ + convert_and_respect_annotation_metadata( + object_=item, + annotation=annotation, + inner_type=inner_type, + direction=direction, + ) + for item in object_ + ] + + if typing_extensions.get_origin(clean_type) == typing.Union: + # We should be able to ~relatively~ safely try to convert keys against all + # member types in the union, the edge case here is if one member aliases a field + # of the same name to a different name from another member + # Or if another member aliases a field of the same name that another member does not. + for member in typing_extensions.get_args(clean_type): + object_ = convert_and_respect_annotation_metadata( + object_=object_, + annotation=annotation, + inner_type=member, + direction=direction, + ) + return object_ + + annotated_type = _get_annotation(annotation) + if annotated_type is None: + return object_ + + # If the object is not a TypedDict, a Union, or other container (list, set, sequence, etc.) + # Then we can safely call it on the recursive conversion. + return object_ + + +def _convert_mapping( + object_: typing.Mapping[str, object], + expected_type: typing.Any, + direction: typing.Literal["read", "write"], +) -> typing.Mapping[str, object]: + converted_object: typing.Dict[str, object] = {} + try: + annotations = typing_extensions.get_type_hints(expected_type, include_extras=True) + except NameError: + # The TypedDict contains a circular reference, so + # we use the __annotations__ attribute directly. + annotations = getattr(expected_type, "__annotations__", {}) + aliases_to_field_names = _get_alias_to_field_name(annotations) + for key, value in object_.items(): + if direction == "read" and key in aliases_to_field_names: + dealiased_key = aliases_to_field_names.get(key) + if dealiased_key is not None: + type_ = annotations.get(dealiased_key) + else: + type_ = annotations.get(key) + # Note you can't get the annotation by the field name if you're in read mode, so you must check the aliases map + # + # So this is effectively saying if we're in write mode, and we don't have a type, or if we're in read mode and we don't have an alias + # then we can just pass the value through as is + if type_ is None: + converted_object[key] = value + elif direction == "read" and key not in aliases_to_field_names: + converted_object[key] = convert_and_respect_annotation_metadata( + object_=value, annotation=type_, direction=direction + ) + else: + converted_object[_alias_key(key, type_, direction, aliases_to_field_names)] = ( + convert_and_respect_annotation_metadata(object_=value, annotation=type_, direction=direction) + ) + return converted_object + + +def _get_annotation(type_: typing.Any) -> typing.Optional[typing.Any]: + maybe_annotated_type = typing_extensions.get_origin(type_) + if maybe_annotated_type is None: + return None + + if maybe_annotated_type == typing_extensions.NotRequired: + type_ = typing_extensions.get_args(type_)[0] + maybe_annotated_type = typing_extensions.get_origin(type_) + + if maybe_annotated_type == typing_extensions.Annotated: + return type_ + + return None + + +def _remove_annotations(type_: typing.Any) -> typing.Any: + maybe_annotated_type = typing_extensions.get_origin(type_) + if maybe_annotated_type is None: + return type_ + + if maybe_annotated_type == typing_extensions.NotRequired: + return _remove_annotations(typing_extensions.get_args(type_)[0]) + + if maybe_annotated_type == typing_extensions.Annotated: + return _remove_annotations(typing_extensions.get_args(type_)[0]) + + return type_ + + +def get_alias_to_field_mapping(type_: typing.Any) -> typing.Dict[str, str]: + annotations = typing_extensions.get_type_hints(type_, include_extras=True) + return _get_alias_to_field_name(annotations) + + +def get_field_to_alias_mapping(type_: typing.Any) -> typing.Dict[str, str]: + annotations = typing_extensions.get_type_hints(type_, include_extras=True) + return _get_field_to_alias_name(annotations) + + +def _get_alias_to_field_name( + field_to_hint: typing.Dict[str, typing.Any], +) -> typing.Dict[str, str]: + aliases = {} + for field, hint in field_to_hint.items(): + maybe_alias = _get_alias_from_type(hint) + if maybe_alias is not None: + aliases[maybe_alias] = field + return aliases + + +def _get_field_to_alias_name( + field_to_hint: typing.Dict[str, typing.Any], +) -> typing.Dict[str, str]: + aliases = {} + for field, hint in field_to_hint.items(): + maybe_alias = _get_alias_from_type(hint) + if maybe_alias is not None: + aliases[field] = maybe_alias + return aliases + + +def _get_alias_from_type(type_: typing.Any) -> typing.Optional[str]: + maybe_annotated_type = _get_annotation(type_) + + if maybe_annotated_type is not None: + # The actual annotations are 1 onward, the first is the annotated type + annotations = typing_extensions.get_args(maybe_annotated_type)[1:] + + for annotation in annotations: + if isinstance(annotation, FieldMetadata) and annotation.alias is not None: + return annotation.alias + return None + + +def _alias_key( + key: str, + type_: typing.Any, + direction: typing.Literal["read", "write"], + aliases_to_field_names: typing.Dict[str, str], +) -> str: + if direction == "read": + return aliases_to_field_names.get(key, key) + return _get_alias_from_type(type_=type_) or key diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/nested/__init__.py b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/nested/__init__.py new file mode 100644 index 000000000000..87547c8ef9a8 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/nested/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from . import api +_dynamic_imports: typing.Dict[str, str] = {"api": ".api"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["api"] diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/nested/api/__init__.py b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/nested/api/__init__.py new file mode 100644 index 000000000000..5cde0202dcf3 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/nested/api/__init__.py @@ -0,0 +1,4 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/nested/api/client.py b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/nested/api/client.py new file mode 100644 index 000000000000..2c7b13cf699b --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/nested/api/client.py @@ -0,0 +1,93 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from .raw_client import AsyncRawApiClient, RawApiClient + + +class ApiClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawApiClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawApiClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawApiClient + """ + return self._raw_client + + def get_something(self, *, request_options: typing.Optional[RequestOptions] = None) -> None: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + from seed import SeedInferredAuthImplicitApiKey + + client = SeedInferredAuthImplicitApiKey( + base_url="https://yourhost.com/path/to/api", + ) + client.nested.api.get_something() + """ + _response = self._raw_client.get_something(request_options=request_options) + return _response.data + + +class AsyncApiClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawApiClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawApiClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawApiClient + """ + return self._raw_client + + async def get_something(self, *, request_options: typing.Optional[RequestOptions] = None) -> None: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + import asyncio + + from seed import AsyncSeedInferredAuthImplicitApiKey + + client = AsyncSeedInferredAuthImplicitApiKey( + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.nested.api.get_something() + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_something(request_options=request_options) + return _response.data diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/nested/api/raw_client.py b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/nested/api/raw_client.py new file mode 100644 index 000000000000..d7f7becabd87 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/nested/api/raw_client.py @@ -0,0 +1,69 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +from ...core.api_error import ApiError +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.http_response import AsyncHttpResponse, HttpResponse +from ...core.request_options import RequestOptions + + +class RawApiClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def get_something(self, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[None]: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[None] + """ + _response = self._client_wrapper.httpx_client.request( + "nested/get-something", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return HttpResponse(response=_response, data=None) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + +class AsyncRawApiClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def get_something( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[None]: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[None] + """ + _response = await self._client_wrapper.httpx_client.request( + "nested/get-something", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return AsyncHttpResponse(response=_response, data=None) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/nested/client.py b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/nested/client.py new file mode 100644 index 000000000000..b6b69853720e --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/nested/client.py @@ -0,0 +1,63 @@ +# This file was auto-generated by Fern from our API Definition. + +from __future__ import annotations + +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from .raw_client import AsyncRawNestedClient, RawNestedClient + +if typing.TYPE_CHECKING: + from .api.client import ApiClient, AsyncApiClient + + +class NestedClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawNestedClient(client_wrapper=client_wrapper) + self._client_wrapper = client_wrapper + self._api: typing.Optional[ApiClient] = None + + @property + def with_raw_response(self) -> RawNestedClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawNestedClient + """ + return self._raw_client + + @property + def api(self): + if self._api is None: + from .api.client import ApiClient # noqa: E402 + + self._api = ApiClient(client_wrapper=self._client_wrapper) + return self._api + + +class AsyncNestedClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawNestedClient(client_wrapper=client_wrapper) + self._client_wrapper = client_wrapper + self._api: typing.Optional[AsyncApiClient] = None + + @property + def with_raw_response(self) -> AsyncRawNestedClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawNestedClient + """ + return self._raw_client + + @property + def api(self): + if self._api is None: + from .api.client import AsyncApiClient # noqa: E402 + + self._api = AsyncApiClient(client_wrapper=self._client_wrapper) + return self._api diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/nested/raw_client.py b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/nested/raw_client.py new file mode 100644 index 000000000000..27d31b97385d --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/nested/raw_client.py @@ -0,0 +1,13 @@ +# This file was auto-generated by Fern from our API Definition. + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper + + +class RawNestedClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + +class AsyncRawNestedClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/nested_no_auth/__init__.py b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/nested_no_auth/__init__.py new file mode 100644 index 000000000000..87547c8ef9a8 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/nested_no_auth/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from . import api +_dynamic_imports: typing.Dict[str, str] = {"api": ".api"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["api"] diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/nested_no_auth/api/__init__.py b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/nested_no_auth/api/__init__.py new file mode 100644 index 000000000000..5cde0202dcf3 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/nested_no_auth/api/__init__.py @@ -0,0 +1,4 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/nested_no_auth/api/client.py b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/nested_no_auth/api/client.py new file mode 100644 index 000000000000..49329410e43a --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/nested_no_auth/api/client.py @@ -0,0 +1,93 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from .raw_client import AsyncRawApiClient, RawApiClient + + +class ApiClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawApiClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawApiClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawApiClient + """ + return self._raw_client + + def get_something(self, *, request_options: typing.Optional[RequestOptions] = None) -> None: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + from seed import SeedInferredAuthImplicitApiKey + + client = SeedInferredAuthImplicitApiKey( + base_url="https://yourhost.com/path/to/api", + ) + client.nested_no_auth.api.get_something() + """ + _response = self._raw_client.get_something(request_options=request_options) + return _response.data + + +class AsyncApiClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawApiClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawApiClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawApiClient + """ + return self._raw_client + + async def get_something(self, *, request_options: typing.Optional[RequestOptions] = None) -> None: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + import asyncio + + from seed import AsyncSeedInferredAuthImplicitApiKey + + client = AsyncSeedInferredAuthImplicitApiKey( + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.nested_no_auth.api.get_something() + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_something(request_options=request_options) + return _response.data diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/nested_no_auth/api/raw_client.py b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/nested_no_auth/api/raw_client.py new file mode 100644 index 000000000000..4fbe653ac421 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/nested_no_auth/api/raw_client.py @@ -0,0 +1,69 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +from ...core.api_error import ApiError +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.http_response import AsyncHttpResponse, HttpResponse +from ...core.request_options import RequestOptions + + +class RawApiClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def get_something(self, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[None]: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[None] + """ + _response = self._client_wrapper.httpx_client.request( + "nested-no-auth/get-something", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return HttpResponse(response=_response, data=None) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + +class AsyncRawApiClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def get_something( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[None]: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[None] + """ + _response = await self._client_wrapper.httpx_client.request( + "nested-no-auth/get-something", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return AsyncHttpResponse(response=_response, data=None) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/nested_no_auth/client.py b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/nested_no_auth/client.py new file mode 100644 index 000000000000..d72921720357 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/nested_no_auth/client.py @@ -0,0 +1,63 @@ +# This file was auto-generated by Fern from our API Definition. + +from __future__ import annotations + +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from .raw_client import AsyncRawNestedNoAuthClient, RawNestedNoAuthClient + +if typing.TYPE_CHECKING: + from .api.client import ApiClient, AsyncApiClient + + +class NestedNoAuthClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawNestedNoAuthClient(client_wrapper=client_wrapper) + self._client_wrapper = client_wrapper + self._api: typing.Optional[ApiClient] = None + + @property + def with_raw_response(self) -> RawNestedNoAuthClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawNestedNoAuthClient + """ + return self._raw_client + + @property + def api(self): + if self._api is None: + from .api.client import ApiClient # noqa: E402 + + self._api = ApiClient(client_wrapper=self._client_wrapper) + return self._api + + +class AsyncNestedNoAuthClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawNestedNoAuthClient(client_wrapper=client_wrapper) + self._client_wrapper = client_wrapper + self._api: typing.Optional[AsyncApiClient] = None + + @property + def with_raw_response(self) -> AsyncRawNestedNoAuthClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawNestedNoAuthClient + """ + return self._raw_client + + @property + def api(self): + if self._api is None: + from .api.client import AsyncApiClient # noqa: E402 + + self._api = AsyncApiClient(client_wrapper=self._client_wrapper) + return self._api diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/nested_no_auth/raw_client.py b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/nested_no_auth/raw_client.py new file mode 100644 index 000000000000..0c7a80a10e7a --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/nested_no_auth/raw_client.py @@ -0,0 +1,13 @@ +# This file was auto-generated by Fern from our API Definition. + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper + + +class RawNestedNoAuthClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + +class AsyncRawNestedNoAuthClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/py.typed b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/py.typed new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/simple/__init__.py b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/simple/__init__.py new file mode 100644 index 000000000000..5cde0202dcf3 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/simple/__init__.py @@ -0,0 +1,4 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/simple/client.py b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/simple/client.py new file mode 100644 index 000000000000..ef2efdca2bd2 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/simple/client.py @@ -0,0 +1,93 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.request_options import RequestOptions +from .raw_client import AsyncRawSimpleClient, RawSimpleClient + + +class SimpleClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawSimpleClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawSimpleClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawSimpleClient + """ + return self._raw_client + + def get_something(self, *, request_options: typing.Optional[RequestOptions] = None) -> None: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + from seed import SeedInferredAuthImplicitApiKey + + client = SeedInferredAuthImplicitApiKey( + base_url="https://yourhost.com/path/to/api", + ) + client.simple.get_something() + """ + _response = self._raw_client.get_something(request_options=request_options) + return _response.data + + +class AsyncSimpleClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawSimpleClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawSimpleClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawSimpleClient + """ + return self._raw_client + + async def get_something(self, *, request_options: typing.Optional[RequestOptions] = None) -> None: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + import asyncio + + from seed import AsyncSeedInferredAuthImplicitApiKey + + client = AsyncSeedInferredAuthImplicitApiKey( + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.simple.get_something() + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_something(request_options=request_options) + return _response.data diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/simple/raw_client.py b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/simple/raw_client.py new file mode 100644 index 000000000000..aa82d3dd5984 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/simple/raw_client.py @@ -0,0 +1,69 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +from ..core.api_error import ApiError +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.http_response import AsyncHttpResponse, HttpResponse +from ..core.request_options import RequestOptions + + +class RawSimpleClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def get_something(self, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[None]: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[None] + """ + _response = self._client_wrapper.httpx_client.request( + "get-something", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return HttpResponse(response=_response, data=None) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + +class AsyncRawSimpleClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def get_something( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[None]: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[None] + """ + _response = await self._client_wrapper.httpx_client.request( + "get-something", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return AsyncHttpResponse(response=_response, data=None) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/version.py b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/version.py new file mode 100644 index 000000000000..ffc49a9e7d8d --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/src/seed/version.py @@ -0,0 +1,3 @@ +from importlib import metadata + +__version__ = metadata.version("fern_inferred-auth-implicit-api-key") diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/tests/custom/test_client.py b/seed/python-sdk/inferred-auth-implicit-api-key/tests/custom/test_client.py new file mode 100644 index 000000000000..ab04ce6393ef --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/tests/custom/test_client.py @@ -0,0 +1,7 @@ +import pytest + + +# Get started with writing tests with pytest at https://docs.pytest.org +@pytest.mark.skip(reason="Unimplemented") +def test_client() -> None: + assert True diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/tests/utils/__init__.py b/seed/python-sdk/inferred-auth-implicit-api-key/tests/utils/__init__.py new file mode 100644 index 000000000000..f3ea2659bb1c --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/tests/utils/__init__.py @@ -0,0 +1,2 @@ +# This file was auto-generated by Fern from our API Definition. + diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/tests/utils/assets/models/__init__.py b/seed/python-sdk/inferred-auth-implicit-api-key/tests/utils/assets/models/__init__.py new file mode 100644 index 000000000000..2cf01263529d --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/tests/utils/assets/models/__init__.py @@ -0,0 +1,21 @@ +# This file was auto-generated by Fern from our API Definition. + +# This file was auto-generated by Fern from our API Definition. + +from .circle import CircleParams +from .object_with_defaults import ObjectWithDefaultsParams +from .object_with_optional_field import ObjectWithOptionalFieldParams +from .shape import Shape_CircleParams, Shape_SquareParams, ShapeParams +from .square import SquareParams +from .undiscriminated_shape import UndiscriminatedShapeParams + +__all__ = [ + "CircleParams", + "ObjectWithDefaultsParams", + "ObjectWithOptionalFieldParams", + "ShapeParams", + "Shape_CircleParams", + "Shape_SquareParams", + "SquareParams", + "UndiscriminatedShapeParams", +] diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/tests/utils/assets/models/circle.py b/seed/python-sdk/inferred-auth-implicit-api-key/tests/utils/assets/models/circle.py new file mode 100644 index 000000000000..74ecf38c308b --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/tests/utils/assets/models/circle.py @@ -0,0 +1,11 @@ +# This file was auto-generated by Fern from our API Definition. + +# This file was auto-generated by Fern from our API Definition. + +import typing_extensions + +from seed.core.serialization import FieldMetadata + + +class CircleParams(typing_extensions.TypedDict): + radius_measurement: typing_extensions.Annotated[float, FieldMetadata(alias="radiusMeasurement")] diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/tests/utils/assets/models/color.py b/seed/python-sdk/inferred-auth-implicit-api-key/tests/utils/assets/models/color.py new file mode 100644 index 000000000000..2aa2c4c52f0c --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/tests/utils/assets/models/color.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +# This file was auto-generated by Fern from our API Definition. + +import typing + +Color = typing.Union[typing.Literal["red", "blue"], typing.Any] diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/tests/utils/assets/models/object_with_defaults.py b/seed/python-sdk/inferred-auth-implicit-api-key/tests/utils/assets/models/object_with_defaults.py new file mode 100644 index 000000000000..a977b1d2aa1c --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/tests/utils/assets/models/object_with_defaults.py @@ -0,0 +1,15 @@ +# This file was auto-generated by Fern from our API Definition. + +# This file was auto-generated by Fern from our API Definition. + +import typing_extensions + + +class ObjectWithDefaultsParams(typing_extensions.TypedDict): + """ + Defines properties with default values and validation rules. + """ + + decimal: typing_extensions.NotRequired[float] + string: typing_extensions.NotRequired[str] + required_string: str diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/tests/utils/assets/models/object_with_optional_field.py b/seed/python-sdk/inferred-auth-implicit-api-key/tests/utils/assets/models/object_with_optional_field.py new file mode 100644 index 000000000000..6b5608bc05b6 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/tests/utils/assets/models/object_with_optional_field.py @@ -0,0 +1,35 @@ +# This file was auto-generated by Fern from our API Definition. + +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing +import uuid + +import typing_extensions +from .color import Color +from .shape import ShapeParams +from .undiscriminated_shape import UndiscriminatedShapeParams + +from seed.core.serialization import FieldMetadata + + +class ObjectWithOptionalFieldParams(typing_extensions.TypedDict): + literal: typing.Literal["lit_one"] + string: typing_extensions.NotRequired[str] + integer: typing_extensions.NotRequired[int] + long_: typing_extensions.NotRequired[typing_extensions.Annotated[int, FieldMetadata(alias="long")]] + double: typing_extensions.NotRequired[float] + bool_: typing_extensions.NotRequired[typing_extensions.Annotated[bool, FieldMetadata(alias="bool")]] + datetime: typing_extensions.NotRequired[dt.datetime] + date: typing_extensions.NotRequired[dt.date] + uuid_: typing_extensions.NotRequired[typing_extensions.Annotated[uuid.UUID, FieldMetadata(alias="uuid")]] + base_64: typing_extensions.NotRequired[typing_extensions.Annotated[str, FieldMetadata(alias="base64")]] + list_: typing_extensions.NotRequired[typing_extensions.Annotated[typing.Sequence[str], FieldMetadata(alias="list")]] + set_: typing_extensions.NotRequired[typing_extensions.Annotated[typing.Set[str], FieldMetadata(alias="set")]] + map_: typing_extensions.NotRequired[typing_extensions.Annotated[typing.Dict[int, str], FieldMetadata(alias="map")]] + enum: typing_extensions.NotRequired[Color] + union: typing_extensions.NotRequired[ShapeParams] + second_union: typing_extensions.NotRequired[ShapeParams] + undiscriminated_union: typing_extensions.NotRequired[UndiscriminatedShapeParams] + any: typing.Optional[typing.Any] diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/tests/utils/assets/models/shape.py b/seed/python-sdk/inferred-auth-implicit-api-key/tests/utils/assets/models/shape.py new file mode 100644 index 000000000000..7e70010a251f --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/tests/utils/assets/models/shape.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +# This file was auto-generated by Fern from our API Definition. + +from __future__ import annotations + +import typing + +import typing_extensions + +from seed.core.serialization import FieldMetadata + + +class Base(typing_extensions.TypedDict): + id: str + + +class Shape_CircleParams(Base): + shape_type: typing_extensions.Annotated[typing.Literal["circle"], FieldMetadata(alias="shapeType")] + radius_measurement: typing_extensions.Annotated[float, FieldMetadata(alias="radiusMeasurement")] + + +class Shape_SquareParams(Base): + shape_type: typing_extensions.Annotated[typing.Literal["square"], FieldMetadata(alias="shapeType")] + length_measurement: typing_extensions.Annotated[float, FieldMetadata(alias="lengthMeasurement")] + + +ShapeParams = typing.Union[Shape_CircleParams, Shape_SquareParams] diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/tests/utils/assets/models/square.py b/seed/python-sdk/inferred-auth-implicit-api-key/tests/utils/assets/models/square.py new file mode 100644 index 000000000000..71c7d25fd4ad --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/tests/utils/assets/models/square.py @@ -0,0 +1,11 @@ +# This file was auto-generated by Fern from our API Definition. + +# This file was auto-generated by Fern from our API Definition. + +import typing_extensions + +from seed.core.serialization import FieldMetadata + + +class SquareParams(typing_extensions.TypedDict): + length_measurement: typing_extensions.Annotated[float, FieldMetadata(alias="lengthMeasurement")] diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/tests/utils/assets/models/undiscriminated_shape.py b/seed/python-sdk/inferred-auth-implicit-api-key/tests/utils/assets/models/undiscriminated_shape.py new file mode 100644 index 000000000000..99f12b300d1d --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/tests/utils/assets/models/undiscriminated_shape.py @@ -0,0 +1,10 @@ +# This file was auto-generated by Fern from our API Definition. + +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .circle import CircleParams +from .square import SquareParams + +UndiscriminatedShapeParams = typing.Union[CircleParams, SquareParams] diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/tests/utils/test_http_client.py b/seed/python-sdk/inferred-auth-implicit-api-key/tests/utils/test_http_client.py new file mode 100644 index 000000000000..cf967de21db4 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/tests/utils/test_http_client.py @@ -0,0 +1,253 @@ +# This file was auto-generated by Fern from our API Definition. + +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict +from seed.core.request_options import RequestOptions + + +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + +def get_request_options() -> RequestOptions: + return {"additional_body_parameters": {"see you": "later"}} + + +def get_request_options_with_none() -> RequestOptions: + return {"additional_body_parameters": {"see you": "later", "optional": None}} + + +def test_get_json_request_body() -> None: + json_body, data_body = get_request_body(json={"hello": "world"}, data=None, request_options=None, omit=None) + assert json_body == {"hello": "world"} + assert data_body is None + + json_body_extras, data_body_extras = get_request_body( + json={"goodbye": "world"}, data=None, request_options=get_request_options(), omit=None + ) + + assert json_body_extras == {"goodbye": "world", "see you": "later"} + assert data_body_extras is None + + +def test_get_files_request_body() -> None: + json_body, data_body = get_request_body(json=None, data={"hello": "world"}, request_options=None, omit=None) + assert data_body == {"hello": "world"} + assert json_body is None + + json_body_extras, data_body_extras = get_request_body( + json=None, data={"goodbye": "world"}, request_options=get_request_options(), omit=None + ) + + assert data_body_extras == {"goodbye": "world", "see you": "later"} + assert json_body_extras is None + + +def test_get_none_request_body() -> None: + json_body, data_body = get_request_body(json=None, data=None, request_options=None, omit=None) + assert data_body is None + assert json_body is None + + json_body_extras, data_body_extras = get_request_body( + json=None, data=None, request_options=get_request_options(), omit=None + ) + + assert json_body_extras == {"see you": "later"} + assert data_body_extras is None + + +def test_get_empty_json_request_body() -> None: + unrelated_request_options: RequestOptions = {"max_retries": 3} + json_body, data_body = get_request_body(json=None, data=None, request_options=unrelated_request_options, omit=None) + assert json_body is None + assert data_body is None + + json_body_extras, data_body_extras = get_request_body( + json={}, data=None, request_options=unrelated_request_options, omit=None + ) + + assert json_body_extras is None + assert data_body_extras is None + + +def test_json_body_preserves_none_values() -> None: + """Test that JSON bodies preserve None values (they become JSON null).""" + json_body, data_body = get_request_body( + json={"hello": "world", "optional": None}, data=None, request_options=None, omit=None + ) + # JSON bodies should preserve None values + assert json_body == {"hello": "world", "optional": None} + assert data_body is None + + +def test_data_body_preserves_none_values_without_multipart() -> None: + """Test that data bodies preserve None values when not using multipart. + + The filtering of None values happens in HttpClient.request/stream methods, + not in get_request_body. This test verifies get_request_body doesn't filter None. + """ + json_body, data_body = get_request_body( + json=None, data={"hello": "world", "optional": None}, request_options=None, omit=None + ) + # get_request_body should preserve None values in data body + # The filtering happens later in HttpClient.request when multipart is detected + assert data_body == {"hello": "world", "optional": None} + assert json_body is None + + +def test_remove_none_from_dict_filters_none_values() -> None: + """Test that remove_none_from_dict correctly filters out None values.""" + original = {"hello": "world", "optional": None, "another": "value", "also_none": None} + filtered = remove_none_from_dict(original) + assert filtered == {"hello": "world", "another": "value"} + # Original should not be modified + assert original == {"hello": "world", "optional": None, "another": "value", "also_none": None} + + +def test_remove_none_from_dict_empty_dict() -> None: + """Test that remove_none_from_dict handles empty dict.""" + assert remove_none_from_dict({}) == {} + + +def test_remove_none_from_dict_all_none() -> None: + """Test that remove_none_from_dict handles dict with all None values.""" + assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/tests/utils/test_query_encoding.py b/seed/python-sdk/inferred-auth-implicit-api-key/tests/utils/test_query_encoding.py new file mode 100644 index 000000000000..ef5fd7094f9b --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/tests/utils/test_query_encoding.py @@ -0,0 +1,36 @@ +# This file was auto-generated by Fern from our API Definition. + +from seed.core.query_encoder import encode_query + + +def test_query_encoding_deep_objects() -> None: + assert encode_query({"hello world": "hello world"}) == [("hello world", "hello world")] + assert encode_query({"hello_world": {"hello": "world"}}) == [("hello_world[hello]", "world")] + assert encode_query({"hello_world": {"hello": {"world": "today"}, "test": "this"}, "hi": "there"}) == [ + ("hello_world[hello][world]", "today"), + ("hello_world[test]", "this"), + ("hi", "there"), + ] + + +def test_query_encoding_deep_object_arrays() -> None: + assert encode_query({"objects": [{"key": "hello", "value": "world"}, {"key": "foo", "value": "bar"}]}) == [ + ("objects[key]", "hello"), + ("objects[value]", "world"), + ("objects[key]", "foo"), + ("objects[value]", "bar"), + ] + assert encode_query( + {"users": [{"name": "string", "tags": ["string"]}, {"name": "string2", "tags": ["string2", "string3"]}]} + ) == [ + ("users[name]", "string"), + ("users[tags]", "string"), + ("users[name]", "string2"), + ("users[tags]", "string2"), + ("users[tags]", "string3"), + ] + + +def test_encode_query_with_none() -> None: + encoded = encode_query(None) + assert encoded is None diff --git a/seed/python-sdk/inferred-auth-implicit-api-key/tests/utils/test_serialization.py b/seed/python-sdk/inferred-auth-implicit-api-key/tests/utils/test_serialization.py new file mode 100644 index 000000000000..b298db89c4bd --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-api-key/tests/utils/test_serialization.py @@ -0,0 +1,72 @@ +# This file was auto-generated by Fern from our API Definition. + +from typing import Any, List + +from .assets.models import ObjectWithOptionalFieldParams, ShapeParams + +from seed.core.serialization import convert_and_respect_annotation_metadata + +UNION_TEST: ShapeParams = {"radius_measurement": 1.0, "shape_type": "circle", "id": "1"} +UNION_TEST_CONVERTED = {"shapeType": "circle", "radiusMeasurement": 1.0, "id": "1"} + + +def test_convert_and_respect_annotation_metadata() -> None: + data: ObjectWithOptionalFieldParams = { + "string": "string", + "long_": 12345, + "bool_": True, + "literal": "lit_one", + "any": "any", + } + converted = convert_and_respect_annotation_metadata( + object_=data, annotation=ObjectWithOptionalFieldParams, direction="write" + ) + assert converted == {"string": "string", "long": 12345, "bool": True, "literal": "lit_one", "any": "any"} + + +def test_convert_and_respect_annotation_metadata_in_list() -> None: + data: List[ObjectWithOptionalFieldParams] = [ + {"string": "string", "long_": 12345, "bool_": True, "literal": "lit_one", "any": "any"}, + {"string": "another string", "long_": 67890, "list_": [], "literal": "lit_one", "any": "any"}, + ] + converted = convert_and_respect_annotation_metadata( + object_=data, annotation=List[ObjectWithOptionalFieldParams], direction="write" + ) + + assert converted == [ + {"string": "string", "long": 12345, "bool": True, "literal": "lit_one", "any": "any"}, + {"string": "another string", "long": 67890, "list": [], "literal": "lit_one", "any": "any"}, + ] + + +def test_convert_and_respect_annotation_metadata_in_nested_object() -> None: + data: ObjectWithOptionalFieldParams = { + "string": "string", + "long_": 12345, + "union": UNION_TEST, + "literal": "lit_one", + "any": "any", + } + converted = convert_and_respect_annotation_metadata( + object_=data, annotation=ObjectWithOptionalFieldParams, direction="write" + ) + + assert converted == { + "string": "string", + "long": 12345, + "union": UNION_TEST_CONVERTED, + "literal": "lit_one", + "any": "any", + } + + +def test_convert_and_respect_annotation_metadata_in_union() -> None: + converted = convert_and_respect_annotation_metadata(object_=UNION_TEST, annotation=ShapeParams, direction="write") + + assert converted == UNION_TEST_CONVERTED + + +def test_convert_and_respect_annotation_metadata_with_empty_object() -> None: + data: Any = {} + converted = convert_and_respect_annotation_metadata(object_=data, annotation=ShapeParams, direction="write") + assert converted == data diff --git a/seed/python-sdk/inferred-auth-implicit-no-expiry/src/seed/core/http_client.py b/seed/python-sdk/inferred-auth-implicit-no-expiry/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/inferred-auth-implicit-no-expiry/src/seed/core/http_client.py +++ b/seed/python-sdk/inferred-auth-implicit-no-expiry/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/inferred-auth-implicit-no-expiry/tests/utils/test_http_client.py b/seed/python-sdk/inferred-auth-implicit-no-expiry/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/inferred-auth-implicit-no-expiry/tests/utils/test_http_client.py +++ b/seed/python-sdk/inferred-auth-implicit-no-expiry/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/inferred-auth-implicit-reference/.fern/metadata.json b/seed/python-sdk/inferred-auth-implicit-reference/.fern/metadata.json new file mode 100644 index 000000000000..623048546df1 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/.fern/metadata.json @@ -0,0 +1,5 @@ +{ + "cliVersion": "DUMMY", + "generatorName": "fernapi/fern-python-sdk", + "generatorVersion": "latest" +} \ No newline at end of file diff --git a/seed/python-sdk/inferred-auth-implicit-reference/.github/workflows/ci.yml b/seed/python-sdk/inferred-auth-implicit-reference/.github/workflows/ci.yml new file mode 100644 index 000000000000..ffd2d8acab24 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/.github/workflows/ci.yml @@ -0,0 +1,60 @@ +name: ci +on: [push] +jobs: + compile: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + - name: Set up python + uses: actions/setup-python@v4 + with: + python-version: 3.9 + - name: Bootstrap poetry + run: | + curl -sSL https://install.python-poetry.org | python - -y --version 1.5.1 + - name: Install dependencies + run: poetry install + - name: Compile + run: poetry run mypy . + test: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + - name: Set up python + uses: actions/setup-python@v4 + with: + python-version: 3.9 + - name: Bootstrap poetry + run: | + curl -sSL https://install.python-poetry.org | python - -y --version 1.5.1 + - name: Install dependencies + run: poetry install + + - name: Test + run: poetry run pytest -rP -n auto . + + publish: + needs: [compile, test] + if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + - name: Set up python + uses: actions/setup-python@v4 + with: + python-version: 3.9 + - name: Bootstrap poetry + run: | + curl -sSL https://install.python-poetry.org | python - -y --version 1.5.1 + - name: Install dependencies + run: poetry install + - name: Publish to pypi + run: | + poetry config repositories.remote + poetry --no-interaction -v publish --build --repository remote --username "$PYPI_USERNAME" --password "$PYPI_PASSWORD" + env: + PYPI_USERNAME: ${{ secrets.PYPI_USERNAME }} + PYPI_PASSWORD: ${{ secrets.PYPI_PASSWORD }} diff --git a/seed/python-sdk/inferred-auth-implicit-reference/.gitignore b/seed/python-sdk/inferred-auth-implicit-reference/.gitignore new file mode 100644 index 000000000000..d2e4ca808d21 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/.gitignore @@ -0,0 +1,5 @@ +.mypy_cache/ +.ruff_cache/ +__pycache__/ +dist/ +poetry.toml diff --git a/seed/python-sdk/inferred-auth-implicit-reference/README.md b/seed/python-sdk/inferred-auth-implicit-reference/README.md new file mode 100644 index 000000000000..4dfaf4736917 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/README.md @@ -0,0 +1,173 @@ +# Seed Python Library + +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FPython) +[![pypi](https://img.shields.io/pypi/v/fern_inferred-auth-implicit-reference)](https://pypi.python.org/pypi/fern_inferred-auth-implicit-reference) + +The Seed Python library provides convenient access to the Seed APIs from Python. + +## Table of Contents + +- [Installation](#installation) +- [Reference](#reference) +- [Usage](#usage) +- [Async Client](#async-client) +- [Exception Handling](#exception-handling) +- [Advanced](#advanced) + - [Access Raw Response Data](#access-raw-response-data) + - [Retries](#retries) + - [Timeouts](#timeouts) + - [Custom Client](#custom-client) +- [Contributing](#contributing) + +## Installation + +```sh +pip install fern_inferred-auth-implicit-reference +``` + +## Reference + +A full reference for this library is available [here](./reference.md). + +## Usage + +Instantiate and use the client with the following: + +```python +from seed import SeedInferredAuthImplicit + +client = SeedInferredAuthImplicit( + base_url="https://yourhost.com/path/to/api", +) +client.auth.get_token_with_client_credentials( + client_id="client_id", + client_secret="client_secret", + scope="scope", +) +``` + +## Async Client + +The SDK also exports an `async` client so that you can make non-blocking calls to our API. Note that if you are constructing an Async httpx client class to pass into this client, use `httpx.AsyncClient()` instead of `httpx.Client()` (e.g. for the `httpx_client` parameter of this client). + +```python +import asyncio + +from seed import AsyncSeedInferredAuthImplicit + +client = AsyncSeedInferredAuthImplicit( + base_url="https://yourhost.com/path/to/api", +) + + +async def main() -> None: + await client.auth.get_token_with_client_credentials( + client_id="client_id", + client_secret="client_secret", + scope="scope", + ) + + +asyncio.run(main()) +``` + +## Exception Handling + +When the API returns a non-success status code (4xx or 5xx response), a subclass of the following error +will be thrown. + +```python +from seed.core.api_error import ApiError + +try: + client.auth.get_token_with_client_credentials(...) +except ApiError as e: + print(e.status_code) + print(e.body) +``` + +## Advanced + +### Access Raw Response Data + +The SDK provides access to raw response data, including headers, through the `.with_raw_response` property. +The `.with_raw_response` property returns a "raw" client that can be used to access the `.headers` and `.data` attributes. + +```python +from seed import SeedInferredAuthImplicit + +client = SeedInferredAuthImplicit( + ..., +) +response = client.auth.with_raw_response.get_token_with_client_credentials(...) +print(response.headers) # access the response headers +print(response.data) # access the underlying object +``` + +### Retries + +The SDK is instrumented with automatic retries with exponential backoff. A request will be retried as long +as the request is deemed retryable and the number of retry attempts has not grown larger than the configured +retry limit (default: 2). + +A request is deemed retryable when any of the following HTTP status codes is returned: + +- [408](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/408) (Timeout) +- [429](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429) (Too Many Requests) +- [5XX](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500) (Internal Server Errors) + +Use the `max_retries` request option to configure this behavior. + +```python +client.auth.get_token_with_client_credentials(..., request_options={ + "max_retries": 1 +}) +``` + +### Timeouts + +The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. + +```python + +from seed import SeedInferredAuthImplicit + +client = SeedInferredAuthImplicit( + ..., + timeout=20.0, +) + + +# Override timeout for a specific method +client.auth.get_token_with_client_credentials(..., request_options={ + "timeout_in_seconds": 1 +}) +``` + +### Custom Client + +You can override the `httpx` client to customize it for your use-case. Some common use-cases include support for proxies +and transports. + +```python +import httpx +from seed import SeedInferredAuthImplicit + +client = SeedInferredAuthImplicit( + ..., + httpx_client=httpx.Client( + proxy="http://my.test.proxy.example.com", + transport=httpx.HTTPTransport(local_address="0.0.0.0"), + ), +) +``` + +## Contributing + +While we value open-source contributions to this SDK, this library is generated programmatically. +Additions made directly to this library would have to be moved over to our generation code, +otherwise they would be overwritten upon the next generated release. Feel free to open a PR as +a proof of concept, but know that we will not be able to merge it as-is. We suggest opening +an issue first to discuss with us! + +On the other hand, contributions to the README are always very welcome! diff --git a/seed/python-sdk/inferred-auth-implicit-reference/poetry.lock b/seed/python-sdk/inferred-auth-implicit-reference/poetry.lock new file mode 100644 index 000000000000..9decbf87e177 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/poetry.lock @@ -0,0 +1,594 @@ +# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. + +[[package]] +name = "annotated-types" +version = "0.7.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +files = [ + {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, + {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.9\""} + +[[package]] +name = "anyio" +version = "4.5.2" +description = "High level compatibility layer for multiple asynchronous event loop implementations" +optional = false +python-versions = ">=3.8" +files = [ + {file = "anyio-4.5.2-py3-none-any.whl", hash = "sha256:c011ee36bc1e8ba40e5a81cb9df91925c218fe9b778554e0b56a21e1b5d4716f"}, + {file = "anyio-4.5.2.tar.gz", hash = "sha256:23009af4ed04ce05991845451e11ef02fc7c5ed29179ac9a420e5ad0ac7ddc5b"}, +] + +[package.dependencies] +exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} +idna = ">=2.8" +sniffio = ">=1.1" +typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""} + +[package.extras] +doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21.0b1)"] +trio = ["trio (>=0.26.1)"] + +[[package]] +name = "certifi" +version = "2025.11.12" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.7" +files = [ + {file = "certifi-2025.11.12-py3-none-any.whl", hash = "sha256:97de8790030bbd5c2d96b7ec782fc2f7820ef8dba6db909ccf95449f2d062d4b"}, + {file = "certifi-2025.11.12.tar.gz", hash = "sha256:d8ab5478f2ecd78af242878415affce761ca6bc54a22a27e026d7c25357c3316"}, +] + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "exceptiongroup" +version = "1.3.1" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598"}, + {file = "exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""} + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "execnet" +version = "2.1.2" +description = "execnet: rapid multi-Python deployment" +optional = false +python-versions = ">=3.8" +files = [ + {file = "execnet-2.1.2-py3-none-any.whl", hash = "sha256:67fba928dd5a544b783f6056f449e5e3931a5c378b128bc18501f7ea79e296ec"}, + {file = "execnet-2.1.2.tar.gz", hash = "sha256:63d83bfdd9a23e35b9c6a3261412324f964c2ec8dcd8d3c6916ee9373e0befcd"}, +] + +[package.extras] +testing = ["hatch", "pre-commit", "pytest", "tox"] + +[[package]] +name = "h11" +version = "0.16.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +optional = false +python-versions = ">=3.8" +files = [ + {file = "h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86"}, + {file = "h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1"}, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +description = "A minimal low-level HTTP client." +optional = false +python-versions = ">=3.8" +files = [ + {file = "httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55"}, + {file = "httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8"}, +] + +[package.dependencies] +certifi = "*" +h11 = ">=0.16" + +[package.extras] +asyncio = ["anyio (>=4.0,<5.0)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] +trio = ["trio (>=0.22.0,<1.0)"] + +[[package]] +name = "httpx" +version = "0.28.1" +description = "The next generation HTTP client." +optional = false +python-versions = ">=3.8" +files = [ + {file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"}, + {file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"}, +] + +[package.dependencies] +anyio = "*" +certifi = "*" +httpcore = "==1.*" +idna = "*" + +[package.extras] +brotli = ["brotli", "brotlicffi"] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "idna" +version = "3.11" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea"}, + {file = "idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902"}, +] + +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + +[[package]] +name = "iniconfig" +version = "2.1.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.8" +files = [ + {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, + {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, +] + +[[package]] +name = "mypy" +version = "1.13.0" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mypy-1.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6607e0f1dd1fb7f0aca14d936d13fd19eba5e17e1cd2a14f808fa5f8f6d8f60a"}, + {file = "mypy-1.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8a21be69bd26fa81b1f80a61ee7ab05b076c674d9b18fb56239d72e21d9f4c80"}, + {file = "mypy-1.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b2353a44d2179846a096e25691d54d59904559f4232519d420d64da6828a3a7"}, + {file = "mypy-1.13.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0730d1c6a2739d4511dc4253f8274cdd140c55c32dfb0a4cf8b7a43f40abfa6f"}, + {file = "mypy-1.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:c5fc54dbb712ff5e5a0fca797e6e0aa25726c7e72c6a5850cfd2adbc1eb0a372"}, + {file = "mypy-1.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:581665e6f3a8a9078f28d5502f4c334c0c8d802ef55ea0e7276a6e409bc0d82d"}, + {file = "mypy-1.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3ddb5b9bf82e05cc9a627e84707b528e5c7caaa1c55c69e175abb15a761cec2d"}, + {file = "mypy-1.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20c7ee0bc0d5a9595c46f38beb04201f2620065a93755704e141fcac9f59db2b"}, + {file = "mypy-1.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3790ded76f0b34bc9c8ba4def8f919dd6a46db0f5a6610fb994fe8efdd447f73"}, + {file = "mypy-1.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:51f869f4b6b538229c1d1bcc1dd7d119817206e2bc54e8e374b3dfa202defcca"}, + {file = "mypy-1.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5c7051a3461ae84dfb5dd15eff5094640c61c5f22257c8b766794e6dd85e72d5"}, + {file = "mypy-1.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:39bb21c69a5d6342f4ce526e4584bc5c197fd20a60d14a8624d8743fffb9472e"}, + {file = "mypy-1.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:164f28cb9d6367439031f4c81e84d3ccaa1e19232d9d05d37cb0bd880d3f93c2"}, + {file = "mypy-1.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a4c1bfcdbce96ff5d96fc9b08e3831acb30dc44ab02671eca5953eadad07d6d0"}, + {file = "mypy-1.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:a0affb3a79a256b4183ba09811e3577c5163ed06685e4d4b46429a271ba174d2"}, + {file = "mypy-1.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a7b44178c9760ce1a43f544e595d35ed61ac2c3de306599fa59b38a6048e1aa7"}, + {file = "mypy-1.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5d5092efb8516d08440e36626f0153b5006d4088c1d663d88bf79625af3d1d62"}, + {file = "mypy-1.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2904956dac40ced10931ac967ae63c5089bd498542194b436eb097a9f77bc8"}, + {file = "mypy-1.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:7bfd8836970d33c2105562650656b6846149374dc8ed77d98424b40b09340ba7"}, + {file = "mypy-1.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:9f73dba9ec77acb86457a8fc04b5239822df0c14a082564737833d2963677dbc"}, + {file = "mypy-1.13.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:100fac22ce82925f676a734af0db922ecfea991e1d7ec0ceb1e115ebe501301a"}, + {file = "mypy-1.13.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7bcb0bb7f42a978bb323a7c88f1081d1b5dee77ca86f4100735a6f541299d8fb"}, + {file = "mypy-1.13.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bde31fc887c213e223bbfc34328070996061b0833b0a4cfec53745ed61f3519b"}, + {file = "mypy-1.13.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:07de989f89786f62b937851295ed62e51774722e5444a27cecca993fc3f9cd74"}, + {file = "mypy-1.13.0-cp38-cp38-win_amd64.whl", hash = "sha256:4bde84334fbe19bad704b3f5b78c4abd35ff1026f8ba72b29de70dda0916beb6"}, + {file = "mypy-1.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0246bcb1b5de7f08f2826451abd947bf656945209b140d16ed317f65a17dc7dc"}, + {file = "mypy-1.13.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7f5b7deae912cf8b77e990b9280f170381fdfbddf61b4ef80927edd813163732"}, + {file = "mypy-1.13.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7029881ec6ffb8bc233a4fa364736789582c738217b133f1b55967115288a2bc"}, + {file = "mypy-1.13.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3e38b980e5681f28f033f3be86b099a247b13c491f14bb8b1e1e134d23bb599d"}, + {file = "mypy-1.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:a6789be98a2017c912ae6ccb77ea553bbaf13d27605d2ca20a76dfbced631b24"}, + {file = "mypy-1.13.0-py3-none-any.whl", hash = "sha256:9c250883f9fd81d212e0952c92dbfcc96fc237f4b7c92f56ac81fd48460b3e5a"}, + {file = "mypy-1.13.0.tar.gz", hash = "sha256:0291a61b6fbf3e6673e3405cfcc0e7650bebc7939659fdca2702958038bd835e"}, +] + +[package.dependencies] +mypy-extensions = ">=1.0.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = ">=4.6.0" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +faster-cache = ["orjson"] +install-types = ["pip"] +mypyc = ["setuptools (>=50)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.8" +files = [ + {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, + {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, +] + +[[package]] +name = "packaging" +version = "25.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, + {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, +] + +[[package]] +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pydantic" +version = "2.10.6" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584"}, + {file = "pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236"}, +] + +[package.dependencies] +annotated-types = ">=0.6.0" +pydantic-core = "2.27.2" +typing-extensions = ">=4.12.2" + +[package.extras] +email = ["email-validator (>=2.0.0)"] +timezone = ["tzdata"] + +[[package]] +name = "pydantic-core" +version = "2.27.2" +description = "Core functionality for Pydantic validation and serialization" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa"}, + {file = "pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7969e133a6f183be60e9f6f56bfae753585680f3b7307a8e555a948d443cc05a"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3de9961f2a346257caf0aa508a4da705467f53778e9ef6fe744c038119737ef5"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2bb4d3e5873c37bb3dd58714d4cd0b0e6238cebc4177ac8fe878f8b3aa8e74c"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:280d219beebb0752699480fe8f1dc61ab6615c2046d76b7ab7ee38858de0a4e7"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47956ae78b6422cbd46f772f1746799cbb862de838fd8d1fbd34a82e05b0983a"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:14d4a5c49d2f009d62a2a7140d3064f686d17a5d1a268bc641954ba181880236"}, + {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:337b443af21d488716f8d0b6164de833e788aa6bd7e3a39c005febc1284f4962"}, + {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:03d0f86ea3184a12f41a2d23f7ccb79cdb5a18e06993f8a45baa8dfec746f0e9"}, + {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7041c36f5680c6e0f08d922aed302e98b3745d97fe1589db0a3eebf6624523af"}, + {file = "pydantic_core-2.27.2-cp310-cp310-win32.whl", hash = "sha256:50a68f3e3819077be2c98110c1f9dcb3817e93f267ba80a2c05bb4f8799e2ff4"}, + {file = "pydantic_core-2.27.2-cp310-cp310-win_amd64.whl", hash = "sha256:e0fd26b16394ead34a424eecf8a31a1f5137094cabe84a1bcb10fa6ba39d3d31"}, + {file = "pydantic_core-2.27.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8e10c99ef58cfdf2a66fc15d66b16c4a04f62bca39db589ae8cba08bc55331bc"}, + {file = "pydantic_core-2.27.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:26f32e0adf166a84d0cb63be85c562ca8a6fa8de28e5f0d92250c6b7e9e2aff7"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c19d1ea0673cd13cc2f872f6c9ab42acc4e4f492a7ca9d3795ce2b112dd7e15"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e68c4446fe0810e959cdff46ab0a41ce2f2c86d227d96dc3847af0ba7def306"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9640b0059ff4f14d1f37321b94061c6db164fbe49b334b31643e0528d100d99"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40d02e7d45c9f8af700f3452f329ead92da4c5f4317ca9b896de7ce7199ea459"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d81d2068e1c1228a565af076598f9e7451712700b673de8f502f0334f281387d"}, + {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1a4207639fb02ec2dbb76227d7c751a20b1a6b4bc52850568e52260cae64ca3b"}, + {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:3de3ce3c9ddc8bbd88f6e0e304dea0e66d843ec9de1b0042b0911c1663ffd474"}, + {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:30c5f68ded0c36466acede341551106821043e9afaad516adfb6e8fa80a4e6a6"}, + {file = "pydantic_core-2.27.2-cp311-cp311-win32.whl", hash = "sha256:c70c26d2c99f78b125a3459f8afe1aed4d9687c24fd677c6a4436bc042e50d6c"}, + {file = "pydantic_core-2.27.2-cp311-cp311-win_amd64.whl", hash = "sha256:08e125dbdc505fa69ca7d9c499639ab6407cfa909214d500897d02afb816e7cc"}, + {file = "pydantic_core-2.27.2-cp311-cp311-win_arm64.whl", hash = "sha256:26f0d68d4b235a2bae0c3fc585c585b4ecc51382db0e3ba402a22cbc440915e4"}, + {file = "pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0"}, + {file = "pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4"}, + {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3"}, + {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4"}, + {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57"}, + {file = "pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc"}, + {file = "pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9"}, + {file = "pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b"}, + {file = "pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b"}, + {file = "pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4"}, + {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27"}, + {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee"}, + {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1"}, + {file = "pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130"}, + {file = "pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee"}, + {file = "pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b"}, + {file = "pydantic_core-2.27.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d3e8d504bdd3f10835468f29008d72fc8359d95c9c415ce6e767203db6127506"}, + {file = "pydantic_core-2.27.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:521eb9b7f036c9b6187f0b47318ab0d7ca14bd87f776240b90b21c1f4f149320"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85210c4d99a0114f5a9481b44560d7d1e35e32cc5634c656bc48e590b669b145"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d716e2e30c6f140d7560ef1538953a5cd1a87264c737643d481f2779fc247fe1"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f66d89ba397d92f840f8654756196d93804278457b5fbede59598a1f9f90b228"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:669e193c1c576a58f132e3158f9dfa9662969edb1a250c54d8fa52590045f046"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdbe7629b996647b99c01b37f11170a57ae675375b14b8c13b8518b8320ced5"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d262606bf386a5ba0b0af3b97f37c83d7011439e3dc1a9298f21efb292e42f1a"}, + {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cabb9bcb7e0d97f74df8646f34fc76fbf793b7f6dc2438517d7a9e50eee4f14d"}, + {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:d2d63f1215638d28221f664596b1ccb3944f6e25dd18cd3b86b0a4c408d5ebb9"}, + {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bca101c00bff0adb45a833f8451b9105d9df18accb8743b08107d7ada14bd7da"}, + {file = "pydantic_core-2.27.2-cp38-cp38-win32.whl", hash = "sha256:f6f8e111843bbb0dee4cb6594cdc73e79b3329b526037ec242a3e49012495b3b"}, + {file = "pydantic_core-2.27.2-cp38-cp38-win_amd64.whl", hash = "sha256:fd1aea04935a508f62e0d0ef1f5ae968774a32afc306fb8545e06f5ff5cdf3ad"}, + {file = "pydantic_core-2.27.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c10eb4f1659290b523af58fa7cffb452a61ad6ae5613404519aee4bfbf1df993"}, + {file = "pydantic_core-2.27.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ef592d4bad47296fb11f96cd7dc898b92e795032b4894dfb4076cfccd43a9308"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61709a844acc6bf0b7dce7daae75195a10aac96a596ea1b776996414791ede4"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c5f762659e47fdb7b16956c71598292f60a03aa92f8b6351504359dbdba6cf"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c9775e339e42e79ec99c441d9730fccf07414af63eac2f0e48e08fd38a64d76"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57762139821c31847cfb2df63c12f725788bd9f04bc2fb392790959b8f70f118"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d1e85068e818c73e048fe28cfc769040bb1f475524f4745a5dc621f75ac7630"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:097830ed52fd9e427942ff3b9bc17fab52913b2f50f2880dc4a5611446606a54"}, + {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:044a50963a614ecfae59bb1eaf7ea7efc4bc62f49ed594e18fa1e5d953c40e9f"}, + {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:4e0b4220ba5b40d727c7f879eac379b822eee5d8fff418e9d3381ee45b3b0362"}, + {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e4f4bb20d75e9325cc9696c6802657b58bc1dbbe3022f32cc2b2b632c3fbb96"}, + {file = "pydantic_core-2.27.2-cp39-cp39-win32.whl", hash = "sha256:cca63613e90d001b9f2f9a9ceb276c308bfa2a43fafb75c8031c4f66039e8c6e"}, + {file = "pydantic_core-2.27.2-cp39-cp39-win_amd64.whl", hash = "sha256:77d1bca19b0f7021b3a982e6f903dcd5b2b06076def36a652e3907f596e29f67"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2bf14caea37e91198329b828eae1618c068dfb8ef17bb33287a7ad4b61ac314e"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0cb791f5b45307caae8810c2023a184c74605ec3bcbb67d13846c28ff731ff8"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:688d3fd9fcb71f41c4c015c023d12a79d1c4c0732ec9eb35d96e3388a120dcf3"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d591580c34f4d731592f0e9fe40f9cc1b430d297eecc70b962e93c5c668f15f"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:82f986faf4e644ffc189a7f1aafc86e46ef70372bb153e7001e8afccc6e54133"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:bec317a27290e2537f922639cafd54990551725fc844249e64c523301d0822fc"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:0296abcb83a797db256b773f45773da397da75a08f5fcaef41f2044adec05f50"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0d75070718e369e452075a6017fbf187f788e17ed67a3abd47fa934d001863d9"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7e17b560be3c98a8e3aa66ce828bdebb9e9ac6ad5466fba92eb74c4c95cb1151"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c33939a82924da9ed65dab5a65d427205a73181d8098e79b6b426bdf8ad4e656"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:00bad2484fa6bda1e216e7345a798bd37c68fb2d97558edd584942aa41b7d278"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c817e2b40aba42bac6f457498dacabc568c3b7a986fc9ba7c8d9d260b71485fb"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:251136cdad0cb722e93732cb45ca5299fb56e1344a833640bf93b2803f8d1bfd"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2088237af596f0a524d3afc39ab3b036e8adb054ee57cbb1dcf8e09da5b29cc"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d4041c0b966a84b4ae7a09832eb691a35aec90910cd2dbe7a208de59be77965b"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:8083d4e875ebe0b864ffef72a4304827015cff328a1be6e22cc850753bfb122b"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f141ee28a0ad2123b6611b6ceff018039df17f32ada8b534e6aa039545a3efb2"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7d0c8399fcc1848491f00e0314bd59fb34a9c008761bcb422a057670c3f65e35"}, + {file = "pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" + +[[package]] +name = "pytest" +version = "7.4.4" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, + {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-asyncio" +version = "0.23.8" +description = "Pytest support for asyncio" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest_asyncio-0.23.8-py3-none-any.whl", hash = "sha256:50265d892689a5faefb84df80819d1ecef566eb3549cf915dfb33569359d1ce2"}, + {file = "pytest_asyncio-0.23.8.tar.gz", hash = "sha256:759b10b33a6dc61cce40a8bd5205e302978bbbcc00e279a8b61d9a6a3c82e4d3"}, +] + +[package.dependencies] +pytest = ">=7.0.0,<9" + +[package.extras] +docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] +testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] + +[[package]] +name = "pytest-xdist" +version = "3.6.1" +description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7"}, + {file = "pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d"}, +] + +[package.dependencies] +execnet = ">=2.1" +pytest = ">=7.0.0" + +[package.extras] +psutil = ["psutil (>=3.0)"] +setproctitle = ["setproctitle"] +testing = ["filelock"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "ruff" +version = "0.11.5" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.11.5-py3-none-linux_armv6l.whl", hash = "sha256:2561294e108eb648e50f210671cc56aee590fb6167b594144401532138c66c7b"}, + {file = "ruff-0.11.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ac12884b9e005c12d0bd121f56ccf8033e1614f736f766c118ad60780882a077"}, + {file = "ruff-0.11.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:4bfd80a6ec559a5eeb96c33f832418bf0fb96752de0539905cf7b0cc1d31d779"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0947c0a1afa75dcb5db4b34b070ec2bccee869d40e6cc8ab25aca11a7d527794"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ad871ff74b5ec9caa66cb725b85d4ef89b53f8170f47c3406e32ef040400b038"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e6cf918390cfe46d240732d4d72fa6e18e528ca1f60e318a10835cf2fa3dc19f"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:56145ee1478582f61c08f21076dc59153310d606ad663acc00ea3ab5b2125f82"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e5f66f8f1e8c9fc594cbd66fbc5f246a8d91f916cb9667e80208663ec3728304"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80b4df4d335a80315ab9afc81ed1cff62be112bd165e162b5eed8ac55bfc8470"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3068befab73620b8a0cc2431bd46b3cd619bc17d6f7695a3e1bb166b652c382a"}, + {file = "ruff-0.11.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f5da2e710a9641828e09aa98b92c9ebbc60518fdf3921241326ca3e8f8e55b8b"}, + {file = "ruff-0.11.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ef39f19cb8ec98cbc762344921e216f3857a06c47412030374fffd413fb8fd3a"}, + {file = "ruff-0.11.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:b2a7cedf47244f431fd11aa5a7e2806dda2e0c365873bda7834e8f7d785ae159"}, + {file = "ruff-0.11.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:81be52e7519f3d1a0beadcf8e974715b2dfc808ae8ec729ecfc79bddf8dbb783"}, + {file = "ruff-0.11.5-py3-none-win32.whl", hash = "sha256:e268da7b40f56e3eca571508a7e567e794f9bfcc0f412c4b607931d3af9c4afe"}, + {file = "ruff-0.11.5-py3-none-win_amd64.whl", hash = "sha256:6c6dc38af3cfe2863213ea25b6dc616d679205732dc0fb673356c2d69608f800"}, + {file = "ruff-0.11.5-py3-none-win_arm64.whl", hash = "sha256:67e241b4314f4eacf14a601d586026a962f4002a475aa702c69980a38087aa4e"}, + {file = "ruff-0.11.5.tar.gz", hash = "sha256:cae2e2439cb88853e421901ec040a758960b576126dab520fa08e9de431d1bef"}, +] + +[[package]] +name = "six" +version = "1.17.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +description = "Sniff out which async library your code is running under" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, + {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, +] + +[[package]] +name = "tomli" +version = "2.3.0" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45"}, + {file = "tomli-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:883b1c0d6398a6a9d29b508c331fa56adbcdff647f6ace4dfca0f50e90dfd0ba"}, + {file = "tomli-2.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1381caf13ab9f300e30dd8feadb3de072aeb86f1d34a8569453ff32a7dea4bf"}, + {file = "tomli-2.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0e285d2649b78c0d9027570d4da3425bdb49830a6156121360b3f8511ea3441"}, + {file = "tomli-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a154a9ae14bfcf5d8917a59b51ffd5a3ac1fd149b71b47a3a104ca4edcfa845"}, + {file = "tomli-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:74bf8464ff93e413514fefd2be591c3b0b23231a77f901db1eb30d6f712fc42c"}, + {file = "tomli-2.3.0-cp311-cp311-win32.whl", hash = "sha256:00b5f5d95bbfc7d12f91ad8c593a1659b6387b43f054104cda404be6bda62456"}, + {file = "tomli-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:4dc4ce8483a5d429ab602f111a93a6ab1ed425eae3122032db7e9acf449451be"}, + {file = "tomli-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d7d86942e56ded512a594786a5ba0a5e521d02529b3826e7761a05138341a2ac"}, + {file = "tomli-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:73ee0b47d4dad1c5e996e3cd33b8a76a50167ae5f96a2607cbe8cc773506ab22"}, + {file = "tomli-2.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:792262b94d5d0a466afb5bc63c7daa9d75520110971ee269152083270998316f"}, + {file = "tomli-2.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f195fe57ecceac95a66a75ac24d9d5fbc98ef0962e09b2eddec5d39375aae52"}, + {file = "tomli-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e31d432427dcbf4d86958c184b9bfd1e96b5b71f8eb17e6d02531f434fd335b8"}, + {file = "tomli-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b0882799624980785240ab732537fcfc372601015c00f7fc367c55308c186f6"}, + {file = "tomli-2.3.0-cp312-cp312-win32.whl", hash = "sha256:ff72b71b5d10d22ecb084d345fc26f42b5143c5533db5e2eaba7d2d335358876"}, + {file = "tomli-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:1cb4ed918939151a03f33d4242ccd0aa5f11b3547d0cf30f7c74a408a5b99878"}, + {file = "tomli-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5192f562738228945d7b13d4930baffda67b69425a7f0da96d360b0a3888136b"}, + {file = "tomli-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:be71c93a63d738597996be9528f4abe628d1adf5e6eb11607bc8fe1a510b5dae"}, + {file = "tomli-2.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4665508bcbac83a31ff8ab08f424b665200c0e1e645d2bd9ab3d3e557b6185b"}, + {file = "tomli-2.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4021923f97266babc6ccab9f5068642a0095faa0a51a246a6a02fccbb3514eaf"}, + {file = "tomli-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4ea38c40145a357d513bffad0ed869f13c1773716cf71ccaa83b0fa0cc4e42f"}, + {file = "tomli-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad805ea85eda330dbad64c7ea7a4556259665bdf9d2672f5dccc740eb9d3ca05"}, + {file = "tomli-2.3.0-cp313-cp313-win32.whl", hash = "sha256:97d5eec30149fd3294270e889b4234023f2c69747e555a27bd708828353ab606"}, + {file = "tomli-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0c95ca56fbe89e065c6ead5b593ee64b84a26fca063b5d71a1122bf26e533999"}, + {file = "tomli-2.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cebc6fe843e0733ee827a282aca4999b596241195f43b4cc371d64fc6639da9e"}, + {file = "tomli-2.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4c2ef0244c75aba9355561272009d934953817c49f47d768070c3c94355c2aa3"}, + {file = "tomli-2.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c22a8bf253bacc0cf11f35ad9808b6cb75ada2631c2d97c971122583b129afbc"}, + {file = "tomli-2.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0eea8cc5c5e9f89c9b90c4896a8deefc74f518db5927d0e0e8d4a80953d774d0"}, + {file = "tomli-2.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b74a0e59ec5d15127acdabd75ea17726ac4c5178ae51b85bfe39c4f8a278e879"}, + {file = "tomli-2.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b5870b50c9db823c595983571d1296a6ff3e1b88f734a4c8f6fc6188397de005"}, + {file = "tomli-2.3.0-cp314-cp314-win32.whl", hash = "sha256:feb0dacc61170ed7ab602d3d972a58f14ee3ee60494292d384649a3dc38ef463"}, + {file = "tomli-2.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:b273fcbd7fc64dc3600c098e39136522650c49bca95df2d11cf3b626422392c8"}, + {file = "tomli-2.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:940d56ee0410fa17ee1f12b817b37a4d4e4dc4d27340863cc67236c74f582e77"}, + {file = "tomli-2.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f85209946d1fe94416debbb88d00eb92ce9cd5266775424ff81bc959e001acaf"}, + {file = "tomli-2.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a56212bdcce682e56b0aaf79e869ba5d15a6163f88d5451cbde388d48b13f530"}, + {file = "tomli-2.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5f3ffd1e098dfc032d4d3af5c0ac64f6d286d98bc148698356847b80fa4de1b"}, + {file = "tomli-2.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e01decd096b1530d97d5d85cb4dff4af2d8347bd35686654a004f8dea20fc67"}, + {file = "tomli-2.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8a35dd0e643bb2610f156cca8db95d213a90015c11fee76c946aa62b7ae7e02f"}, + {file = "tomli-2.3.0-cp314-cp314t-win32.whl", hash = "sha256:a1f7f282fe248311650081faafa5f4732bdbfef5d45fe3f2e702fbc6f2d496e0"}, + {file = "tomli-2.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:70a251f8d4ba2d9ac2542eecf008b3c8a9fc5c3f9f02c56a9d7952612be2fdba"}, + {file = "tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b"}, + {file = "tomli-2.3.0.tar.gz", hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549"}, +] + +[[package]] +name = "types-python-dateutil" +version = "2.9.0.20241206" +description = "Typing stubs for python-dateutil" +optional = false +python-versions = ">=3.8" +files = [ + {file = "types_python_dateutil-2.9.0.20241206-py3-none-any.whl", hash = "sha256:e248a4bc70a486d3e3ec84d0dc30eec3a5f979d6e7ee4123ae043eedbb987f53"}, + {file = "types_python_dateutil-2.9.0.20241206.tar.gz", hash = "sha256:18f493414c26ffba692a72369fea7a154c502646301ebfe3d56a04b3767284cb"}, +] + +[[package]] +name = "typing-extensions" +version = "4.13.2" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"}, + {file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"}, +] + +[metadata] +lock-version = "2.0" +python-versions = "^3.8" +content-hash = "bcf31a142c86d9e556553c8c260a93b563ac64a043076dbd48b26111d422c26e" diff --git a/seed/python-sdk/inferred-auth-implicit-reference/pyproject.toml b/seed/python-sdk/inferred-auth-implicit-reference/pyproject.toml new file mode 100644 index 000000000000..ca8408666403 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/pyproject.toml @@ -0,0 +1,91 @@ +[project] +name = "fern_inferred-auth-implicit-reference" +dynamic = ["version"] + +[tool.poetry] +name = "fern_inferred-auth-implicit-reference" +version = "0.0.1" +description = "" +readme = "README.md" +authors = [] +keywords = [ + "fern", + "test" +] + +classifiers = [ + "Intended Audience :: Developers", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Operating System :: OS Independent", + "Operating System :: POSIX", + "Operating System :: MacOS", + "Operating System :: POSIX :: Linux", + "Operating System :: Microsoft :: Windows", + "Topic :: Software Development :: Libraries :: Python Modules", + "Typing :: Typed" +] +packages = [ + { include = "seed", from = "src"} +] + +[tool.poetry.urls] +Documentation = 'https://buildwithfern.com/learn' +Homepage = 'https://buildwithfern.com/' +Repository = 'https://github.com/inferred-auth-implicit-reference/fern' + +[tool.poetry.dependencies] +python = "^3.8" +httpx = ">=0.21.2" +pydantic = ">= 1.9.2" +pydantic-core = ">=2.18.2" +typing_extensions = ">= 4.0.0" + +[tool.poetry.group.dev.dependencies] +mypy = "==1.13.0" +pytest = "^7.4.0" +pytest-asyncio = "^0.23.5" +pytest-xdist = "^3.6.1" +python-dateutil = "^2.9.0" +types-python-dateutil = "^2.9.0.20240316" +ruff = "==0.11.5" + +[tool.pytest.ini_options] +testpaths = [ "tests" ] +asyncio_mode = "auto" + +[tool.mypy] +plugins = ["pydantic.mypy"] + +[tool.ruff] +line-length = 120 + +[tool.ruff.lint] +select = [ + "E", # pycodestyle errors + "F", # pyflakes + "I", # isort +] +ignore = [ + "E402", # Module level import not at top of file + "E501", # Line too long + "E711", # Comparison to `None` should be `cond is not None` + "E712", # Avoid equality comparisons to `True`; use `if ...:` checks + "E721", # Use `is` and `is not` for type comparisons, or `isinstance()` for insinstance checks + "E722", # Do not use bare `except` + "E731", # Do not assign a `lambda` expression, use a `def` + "F821", # Undefined name + "F841" # Local variable ... is assigned to but never used +] + +[tool.ruff.lint.isort] +section-order = ["future", "standard-library", "third-party", "first-party"] + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/seed/python-sdk/inferred-auth-implicit-reference/reference.md b/seed/python-sdk/inferred-auth-implicit-reference/reference.md new file mode 100644 index 000000000000..4c1d56e9f0c5 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/reference.md @@ -0,0 +1,300 @@ +# Reference +## Auth +
client.auth.get_token_with_client_credentials(...) -> AsyncHttpResponse[TokenResponse] +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from seed import SeedInferredAuthImplicit + +client = SeedInferredAuthImplicit( + base_url="https://yourhost.com/path/to/api", +) +client.auth.get_token_with_client_credentials( + client_id="client_id", + client_secret="client_secret", + scope="scope", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**client_id:** `str` + +
+
+ +
+
+ +**client_secret:** `str` + +
+
+ +
+
+ +**scope:** `typing.Optional[str]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.auth.refresh_token(...) -> AsyncHttpResponse[TokenResponse] +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from seed import SeedInferredAuthImplicit + +client = SeedInferredAuthImplicit( + base_url="https://yourhost.com/path/to/api", +) +client.auth.refresh_token( + client_id="client_id", + client_secret="client_secret", + refresh_token="refresh_token", + scope="scope", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**client_id:** `str` + +
+
+ +
+
+ +**client_secret:** `str` + +
+
+ +
+
+ +**refresh_token:** `str` + +
+
+ +
+
+ +**scope:** `typing.Optional[str]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## NestedNoAuth Api +
client.nested_no_auth.api.get_something() -> AsyncHttpResponse[None] +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from seed import SeedInferredAuthImplicit + +client = SeedInferredAuthImplicit( + base_url="https://yourhost.com/path/to/api", +) +client.nested_no_auth.api.get_something() + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Nested Api +
client.nested.api.get_something() -> AsyncHttpResponse[None] +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from seed import SeedInferredAuthImplicit + +client = SeedInferredAuthImplicit( + base_url="https://yourhost.com/path/to/api", +) +client.nested.api.get_something() + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Simple +
client.simple.get_something() -> AsyncHttpResponse[None] +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from seed import SeedInferredAuthImplicit + +client = SeedInferredAuthImplicit( + base_url="https://yourhost.com/path/to/api", +) +client.simple.get_something() + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ diff --git a/seed/python-sdk/inferred-auth-implicit-reference/requirements.txt b/seed/python-sdk/inferred-auth-implicit-reference/requirements.txt new file mode 100644 index 000000000000..e80f640a2e74 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/requirements.txt @@ -0,0 +1,4 @@ +httpx>=0.21.2 +pydantic>= 1.9.2 +pydantic-core>=2.18.2 +typing_extensions>= 4.0.0 diff --git a/seed/python-sdk/inferred-auth-implicit-reference/snippet.json b/seed/python-sdk/inferred-auth-implicit-reference/snippet.json new file mode 100644 index 000000000000..97c25fee8feb --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/snippet.json @@ -0,0 +1,70 @@ +{ + "types": {}, + "endpoints": [ + { + "example_identifier": "default", + "id": { + "path": "/token", + "method": "POST", + "identifier_override": "endpoint_auth.getTokenWithClientCredentials" + }, + "snippet": { + "sync_client": "from seed import SeedInferredAuthImplicit\n\nclient = SeedInferredAuthImplicit(\n base_url=\"https://yourhost.com/path/to/api\",\n)\nclient.auth.get_token_with_client_credentials(\n client_id=\"client_id\",\n client_secret=\"client_secret\",\n scope=\"scope\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedInferredAuthImplicit\n\nclient = AsyncSeedInferredAuthImplicit(\n base_url=\"https://yourhost.com/path/to/api\",\n)\n\n\nasync def main() -> None:\n await client.auth.get_token_with_client_credentials(\n client_id=\"client_id\",\n client_secret=\"client_secret\",\n scope=\"scope\",\n )\n\n\nasyncio.run(main())\n", + "type": "python" + } + }, + { + "example_identifier": "default", + "id": { + "path": "/token/refresh", + "method": "POST", + "identifier_override": "endpoint_auth.refreshToken" + }, + "snippet": { + "sync_client": "from seed import SeedInferredAuthImplicit\n\nclient = SeedInferredAuthImplicit(\n base_url=\"https://yourhost.com/path/to/api\",\n)\nclient.auth.refresh_token(\n client_id=\"client_id\",\n client_secret=\"client_secret\",\n refresh_token=\"refresh_token\",\n scope=\"scope\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedInferredAuthImplicit\n\nclient = AsyncSeedInferredAuthImplicit(\n base_url=\"https://yourhost.com/path/to/api\",\n)\n\n\nasync def main() -> None:\n await client.auth.refresh_token(\n client_id=\"client_id\",\n client_secret=\"client_secret\",\n refresh_token=\"refresh_token\",\n scope=\"scope\",\n )\n\n\nasyncio.run(main())\n", + "type": "python" + } + }, + { + "example_identifier": "default", + "id": { + "path": "/nested-no-auth/get-something", + "method": "GET", + "identifier_override": "endpoint_nested-no-auth/api.getSomething" + }, + "snippet": { + "sync_client": "from seed import SeedInferredAuthImplicit\n\nclient = SeedInferredAuthImplicit(\n base_url=\"https://yourhost.com/path/to/api\",\n)\nclient.nested_no_auth.api.get_something()\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedInferredAuthImplicit\n\nclient = AsyncSeedInferredAuthImplicit(\n base_url=\"https://yourhost.com/path/to/api\",\n)\n\n\nasync def main() -> None:\n await client.nested_no_auth.api.get_something()\n\n\nasyncio.run(main())\n", + "type": "python" + } + }, + { + "example_identifier": "default", + "id": { + "path": "/nested/get-something", + "method": "GET", + "identifier_override": "endpoint_nested/api.getSomething" + }, + "snippet": { + "sync_client": "from seed import SeedInferredAuthImplicit\n\nclient = SeedInferredAuthImplicit(\n base_url=\"https://yourhost.com/path/to/api\",\n)\nclient.nested.api.get_something()\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedInferredAuthImplicit\n\nclient = AsyncSeedInferredAuthImplicit(\n base_url=\"https://yourhost.com/path/to/api\",\n)\n\n\nasync def main() -> None:\n await client.nested.api.get_something()\n\n\nasyncio.run(main())\n", + "type": "python" + } + }, + { + "example_identifier": "default", + "id": { + "path": "/get-something", + "method": "GET", + "identifier_override": "endpoint_simple.getSomething" + }, + "snippet": { + "sync_client": "from seed import SeedInferredAuthImplicit\n\nclient = SeedInferredAuthImplicit(\n base_url=\"https://yourhost.com/path/to/api\",\n)\nclient.simple.get_something()\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedInferredAuthImplicit\n\nclient = AsyncSeedInferredAuthImplicit(\n base_url=\"https://yourhost.com/path/to/api\",\n)\n\n\nasync def main() -> None:\n await client.simple.get_something()\n\n\nasyncio.run(main())\n", + "type": "python" + } + } + ] +} \ No newline at end of file diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/__init__.py b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/__init__.py new file mode 100644 index 000000000000..a1e4e1d9d080 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/__init__.py @@ -0,0 +1,59 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from . import auth, nested, nested_no_auth, simple + from .auth import GetTokenRequest, RefreshTokenRequest, TokenResponse + from .client import AsyncSeedInferredAuthImplicit, SeedInferredAuthImplicit + from .version import __version__ +_dynamic_imports: typing.Dict[str, str] = { + "AsyncSeedInferredAuthImplicit": ".client", + "GetTokenRequest": ".auth", + "RefreshTokenRequest": ".auth", + "SeedInferredAuthImplicit": ".client", + "TokenResponse": ".auth", + "__version__": ".version", + "auth": ".auth", + "nested": ".nested", + "nested_no_auth": ".nested_no_auth", + "simple": ".simple", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "AsyncSeedInferredAuthImplicit", + "GetTokenRequest", + "RefreshTokenRequest", + "SeedInferredAuthImplicit", + "TokenResponse", + "__version__", + "auth", + "nested", + "nested_no_auth", + "simple", +] diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/auth/__init__.py b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/auth/__init__.py new file mode 100644 index 000000000000..5d026aa0773a --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/auth/__init__.py @@ -0,0 +1,38 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import GetTokenRequest, RefreshTokenRequest, TokenResponse +_dynamic_imports: typing.Dict[str, str] = { + "GetTokenRequest": ".types", + "RefreshTokenRequest": ".types", + "TokenResponse": ".types", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["GetTokenRequest", "RefreshTokenRequest", "TokenResponse"] diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/auth/client.py b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/auth/client.py new file mode 100644 index 000000000000..c90311764a65 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/auth/client.py @@ -0,0 +1,243 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.request_options import RequestOptions +from .raw_client import AsyncRawAuthClient, RawAuthClient +from .types.token_response import TokenResponse + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class AuthClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawAuthClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawAuthClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawAuthClient + """ + return self._raw_client + + def get_token_with_client_credentials( + self, + *, + client_id: str, + client_secret: str, + scope: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> TokenResponse: + """ + Parameters + ---------- + client_id : str + + client_secret : str + + scope : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + TokenResponse + + Examples + -------- + from seed import SeedInferredAuthImplicit + + client = SeedInferredAuthImplicit( + base_url="https://yourhost.com/path/to/api", + ) + client.auth.get_token_with_client_credentials( + client_id="client_id", + client_secret="client_secret", + scope="scope", + ) + """ + _response = self._raw_client.get_token_with_client_credentials( + client_id=client_id, client_secret=client_secret, scope=scope, request_options=request_options + ) + return _response.data + + def refresh_token( + self, + *, + client_id: str, + client_secret: str, + refresh_token: str, + scope: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> TokenResponse: + """ + Parameters + ---------- + client_id : str + + client_secret : str + + refresh_token : str + + scope : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + TokenResponse + + Examples + -------- + from seed import SeedInferredAuthImplicit + + client = SeedInferredAuthImplicit( + base_url="https://yourhost.com/path/to/api", + ) + client.auth.refresh_token( + client_id="client_id", + client_secret="client_secret", + refresh_token="refresh_token", + scope="scope", + ) + """ + _response = self._raw_client.refresh_token( + client_id=client_id, + client_secret=client_secret, + refresh_token=refresh_token, + scope=scope, + request_options=request_options, + ) + return _response.data + + +class AsyncAuthClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawAuthClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawAuthClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawAuthClient + """ + return self._raw_client + + async def get_token_with_client_credentials( + self, + *, + client_id: str, + client_secret: str, + scope: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> TokenResponse: + """ + Parameters + ---------- + client_id : str + + client_secret : str + + scope : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + TokenResponse + + Examples + -------- + import asyncio + + from seed import AsyncSeedInferredAuthImplicit + + client = AsyncSeedInferredAuthImplicit( + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.auth.get_token_with_client_credentials( + client_id="client_id", + client_secret="client_secret", + scope="scope", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_token_with_client_credentials( + client_id=client_id, client_secret=client_secret, scope=scope, request_options=request_options + ) + return _response.data + + async def refresh_token( + self, + *, + client_id: str, + client_secret: str, + refresh_token: str, + scope: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> TokenResponse: + """ + Parameters + ---------- + client_id : str + + client_secret : str + + refresh_token : str + + scope : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + TokenResponse + + Examples + -------- + import asyncio + + from seed import AsyncSeedInferredAuthImplicit + + client = AsyncSeedInferredAuthImplicit( + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.auth.refresh_token( + client_id="client_id", + client_secret="client_secret", + refresh_token="refresh_token", + scope="scope", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.refresh_token( + client_id=client_id, + client_secret=client_secret, + refresh_token=refresh_token, + scope=scope, + request_options=request_options, + ) + return _response.data diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/auth/raw_client.py b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/auth/raw_client.py new file mode 100644 index 000000000000..8e905e1c219e --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/auth/raw_client.py @@ -0,0 +1,240 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +from ..core.api_error import ApiError +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.http_response import AsyncHttpResponse, HttpResponse +from ..core.pydantic_utilities import parse_obj_as +from ..core.request_options import RequestOptions +from .types.token_response import TokenResponse + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawAuthClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def get_token_with_client_credentials( + self, + *, + client_id: str, + client_secret: str, + scope: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[TokenResponse]: + """ + Parameters + ---------- + client_id : str + + client_secret : str + + scope : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[TokenResponse] + """ + _response = self._client_wrapper.httpx_client.request( + "token", + method="POST", + json={ + "client_id": client_id, + "client_secret": client_secret, + "scope": scope, + "audience": "https://api.example.com", + "grant_type": "client_credentials", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + TokenResponse, + parse_obj_as( + type_=TokenResponse, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + def refresh_token( + self, + *, + client_id: str, + client_secret: str, + refresh_token: str, + scope: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[TokenResponse]: + """ + Parameters + ---------- + client_id : str + + client_secret : str + + refresh_token : str + + scope : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[TokenResponse] + """ + _response = self._client_wrapper.httpx_client.request( + "token/refresh", + method="POST", + json={ + "client_id": client_id, + "client_secret": client_secret, + "refresh_token": refresh_token, + "scope": scope, + "audience": "https://api.example.com", + "grant_type": "refresh_token", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + TokenResponse, + parse_obj_as( + type_=TokenResponse, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + +class AsyncRawAuthClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def get_token_with_client_credentials( + self, + *, + client_id: str, + client_secret: str, + scope: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[TokenResponse]: + """ + Parameters + ---------- + client_id : str + + client_secret : str + + scope : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[TokenResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + "token", + method="POST", + json={ + "client_id": client_id, + "client_secret": client_secret, + "scope": scope, + "audience": "https://api.example.com", + "grant_type": "client_credentials", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + TokenResponse, + parse_obj_as( + type_=TokenResponse, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def refresh_token( + self, + *, + client_id: str, + client_secret: str, + refresh_token: str, + scope: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[TokenResponse]: + """ + Parameters + ---------- + client_id : str + + client_secret : str + + refresh_token : str + + scope : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[TokenResponse] + """ + _response = await self._client_wrapper.httpx_client.request( + "token/refresh", + method="POST", + json={ + "client_id": client_id, + "client_secret": client_secret, + "refresh_token": refresh_token, + "scope": scope, + "audience": "https://api.example.com", + "grant_type": "refresh_token", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + TokenResponse, + parse_obj_as( + type_=TokenResponse, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/auth/types/__init__.py b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/auth/types/__init__.py new file mode 100644 index 000000000000..093b4072347e --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/auth/types/__init__.py @@ -0,0 +1,40 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .get_token_request import GetTokenRequest + from .refresh_token_request import RefreshTokenRequest + from .token_response import TokenResponse +_dynamic_imports: typing.Dict[str, str] = { + "GetTokenRequest": ".get_token_request", + "RefreshTokenRequest": ".refresh_token_request", + "TokenResponse": ".token_response", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["GetTokenRequest", "RefreshTokenRequest", "TokenResponse"] diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/auth/types/get_token_request.py b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/auth/types/get_token_request.py new file mode 100644 index 000000000000..0fa84e794483 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/auth/types/get_token_request.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class GetTokenRequest(UniversalBaseModel): + """ + A request to obtain an OAuth token. + """ + + client_id: str + client_secret: str + audience: typing.Literal["https://api.example.com"] = "https://api.example.com" + grant_type: typing.Literal["client_credentials"] = "client_credentials" + scope: typing.Optional[str] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/auth/types/refresh_token_request.py b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/auth/types/refresh_token_request.py new file mode 100644 index 000000000000..434d5466534b --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/auth/types/refresh_token_request.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class RefreshTokenRequest(UniversalBaseModel): + """ + A request to refresh an OAuth token. + """ + + client_id: str + client_secret: str + refresh_token: str + audience: typing.Literal["https://api.example.com"] = "https://api.example.com" + grant_type: typing.Literal["refresh_token"] = "refresh_token" + scope: typing.Optional[str] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/auth/types/token_response.py b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/auth/types/token_response.py new file mode 100644 index 000000000000..27eeca677e12 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/auth/types/token_response.py @@ -0,0 +1,25 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class TokenResponse(UniversalBaseModel): + """ + An OAuth token response. + """ + + access_token: str + expires_in: int + refresh_token: typing.Optional[str] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/client.py b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/client.py new file mode 100644 index 000000000000..34d701d88a7a --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/client.py @@ -0,0 +1,194 @@ +# This file was auto-generated by Fern from our API Definition. + +from __future__ import annotations + +import typing + +import httpx +from .core.client_wrapper import AsyncClientWrapper, SyncClientWrapper + +if typing.TYPE_CHECKING: + from .auth.client import AsyncAuthClient, AuthClient + from .nested.client import AsyncNestedClient, NestedClient + from .nested_no_auth.client import AsyncNestedNoAuthClient, NestedNoAuthClient + from .simple.client import AsyncSimpleClient, SimpleClient + + +class SeedInferredAuthImplicit: + """ + Use this class to access the different functions within the SDK. You can instantiate any number of clients with different configuration that will propagate to these functions. + + Parameters + ---------- + base_url : str + The base url to use for requests from the client. + + headers : typing.Optional[typing.Dict[str, str]] + Additional headers to send with every request. + + timeout : typing.Optional[float] + The timeout to be used, in seconds, for requests. By default the timeout is 60 seconds, unless a custom httpx client is used, in which case this default is not enforced. + + follow_redirects : typing.Optional[bool] + Whether the default httpx client follows redirects or not, this is irrelevant if a custom httpx client is passed in. + + httpx_client : typing.Optional[httpx.Client] + The httpx client to use for making requests, a preconfigured client is used by default, however this is useful should you want to pass in any custom httpx configuration. + + Examples + -------- + from seed import SeedInferredAuthImplicit + + client = SeedInferredAuthImplicit( + base_url="https://yourhost.com/path/to/api", + ) + """ + + def __init__( + self, + *, + base_url: str, + headers: typing.Optional[typing.Dict[str, str]] = None, + timeout: typing.Optional[float] = None, + follow_redirects: typing.Optional[bool] = True, + httpx_client: typing.Optional[httpx.Client] = None, + ): + _defaulted_timeout = ( + timeout if timeout is not None else 60 if httpx_client is None else httpx_client.timeout.read + ) + self._client_wrapper = SyncClientWrapper( + base_url=base_url, + headers=headers, + httpx_client=httpx_client + if httpx_client is not None + else httpx.Client(timeout=_defaulted_timeout, follow_redirects=follow_redirects) + if follow_redirects is not None + else httpx.Client(timeout=_defaulted_timeout), + timeout=_defaulted_timeout, + ) + self._auth: typing.Optional[AuthClient] = None + self._nested_no_auth: typing.Optional[NestedNoAuthClient] = None + self._nested: typing.Optional[NestedClient] = None + self._simple: typing.Optional[SimpleClient] = None + + @property + def auth(self): + if self._auth is None: + from .auth.client import AuthClient # noqa: E402 + + self._auth = AuthClient(client_wrapper=self._client_wrapper) + return self._auth + + @property + def nested_no_auth(self): + if self._nested_no_auth is None: + from .nested_no_auth.client import NestedNoAuthClient # noqa: E402 + + self._nested_no_auth = NestedNoAuthClient(client_wrapper=self._client_wrapper) + return self._nested_no_auth + + @property + def nested(self): + if self._nested is None: + from .nested.client import NestedClient # noqa: E402 + + self._nested = NestedClient(client_wrapper=self._client_wrapper) + return self._nested + + @property + def simple(self): + if self._simple is None: + from .simple.client import SimpleClient # noqa: E402 + + self._simple = SimpleClient(client_wrapper=self._client_wrapper) + return self._simple + + +class AsyncSeedInferredAuthImplicit: + """ + Use this class to access the different functions within the SDK. You can instantiate any number of clients with different configuration that will propagate to these functions. + + Parameters + ---------- + base_url : str + The base url to use for requests from the client. + + headers : typing.Optional[typing.Dict[str, str]] + Additional headers to send with every request. + + timeout : typing.Optional[float] + The timeout to be used, in seconds, for requests. By default the timeout is 60 seconds, unless a custom httpx client is used, in which case this default is not enforced. + + follow_redirects : typing.Optional[bool] + Whether the default httpx client follows redirects or not, this is irrelevant if a custom httpx client is passed in. + + httpx_client : typing.Optional[httpx.AsyncClient] + The httpx client to use for making requests, a preconfigured client is used by default, however this is useful should you want to pass in any custom httpx configuration. + + Examples + -------- + from seed import AsyncSeedInferredAuthImplicit + + client = AsyncSeedInferredAuthImplicit( + base_url="https://yourhost.com/path/to/api", + ) + """ + + def __init__( + self, + *, + base_url: str, + headers: typing.Optional[typing.Dict[str, str]] = None, + timeout: typing.Optional[float] = None, + follow_redirects: typing.Optional[bool] = True, + httpx_client: typing.Optional[httpx.AsyncClient] = None, + ): + _defaulted_timeout = ( + timeout if timeout is not None else 60 if httpx_client is None else httpx_client.timeout.read + ) + self._client_wrapper = AsyncClientWrapper( + base_url=base_url, + headers=headers, + httpx_client=httpx_client + if httpx_client is not None + else httpx.AsyncClient(timeout=_defaulted_timeout, follow_redirects=follow_redirects) + if follow_redirects is not None + else httpx.AsyncClient(timeout=_defaulted_timeout), + timeout=_defaulted_timeout, + ) + self._auth: typing.Optional[AsyncAuthClient] = None + self._nested_no_auth: typing.Optional[AsyncNestedNoAuthClient] = None + self._nested: typing.Optional[AsyncNestedClient] = None + self._simple: typing.Optional[AsyncSimpleClient] = None + + @property + def auth(self): + if self._auth is None: + from .auth.client import AsyncAuthClient # noqa: E402 + + self._auth = AsyncAuthClient(client_wrapper=self._client_wrapper) + return self._auth + + @property + def nested_no_auth(self): + if self._nested_no_auth is None: + from .nested_no_auth.client import AsyncNestedNoAuthClient # noqa: E402 + + self._nested_no_auth = AsyncNestedNoAuthClient(client_wrapper=self._client_wrapper) + return self._nested_no_auth + + @property + def nested(self): + if self._nested is None: + from .nested.client import AsyncNestedClient # noqa: E402 + + self._nested = AsyncNestedClient(client_wrapper=self._client_wrapper) + return self._nested + + @property + def simple(self): + if self._simple is None: + from .simple.client import AsyncSimpleClient # noqa: E402 + + self._simple = AsyncSimpleClient(client_wrapper=self._client_wrapper) + return self._simple diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/__init__.py b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/__init__.py new file mode 100644 index 000000000000..9a33e233875e --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/__init__.py @@ -0,0 +1,105 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .api_error import ApiError + from .client_wrapper import AsyncClientWrapper, BaseClientWrapper, SyncClientWrapper + from .datetime_utils import serialize_datetime + from .file import File, convert_file_dict_to_httpx_tuples, with_content_type + from .http_client import AsyncHttpClient, HttpClient + from .http_response import AsyncHttpResponse, HttpResponse + from .jsonable_encoder import jsonable_encoder + from .pydantic_utilities import ( + IS_PYDANTIC_V2, + UniversalBaseModel, + UniversalRootModel, + parse_obj_as, + universal_field_validator, + universal_root_validator, + update_forward_refs, + ) + from .query_encoder import encode_query + from .remove_none_from_dict import remove_none_from_dict + from .request_options import RequestOptions + from .serialization import FieldMetadata, convert_and_respect_annotation_metadata +_dynamic_imports: typing.Dict[str, str] = { + "ApiError": ".api_error", + "AsyncClientWrapper": ".client_wrapper", + "AsyncHttpClient": ".http_client", + "AsyncHttpResponse": ".http_response", + "BaseClientWrapper": ".client_wrapper", + "FieldMetadata": ".serialization", + "File": ".file", + "HttpClient": ".http_client", + "HttpResponse": ".http_response", + "IS_PYDANTIC_V2": ".pydantic_utilities", + "RequestOptions": ".request_options", + "SyncClientWrapper": ".client_wrapper", + "UniversalBaseModel": ".pydantic_utilities", + "UniversalRootModel": ".pydantic_utilities", + "convert_and_respect_annotation_metadata": ".serialization", + "convert_file_dict_to_httpx_tuples": ".file", + "encode_query": ".query_encoder", + "jsonable_encoder": ".jsonable_encoder", + "parse_obj_as": ".pydantic_utilities", + "remove_none_from_dict": ".remove_none_from_dict", + "serialize_datetime": ".datetime_utils", + "universal_field_validator": ".pydantic_utilities", + "universal_root_validator": ".pydantic_utilities", + "update_forward_refs": ".pydantic_utilities", + "with_content_type": ".file", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "ApiError", + "AsyncClientWrapper", + "AsyncHttpClient", + "AsyncHttpResponse", + "BaseClientWrapper", + "FieldMetadata", + "File", + "HttpClient", + "HttpResponse", + "IS_PYDANTIC_V2", + "RequestOptions", + "SyncClientWrapper", + "UniversalBaseModel", + "UniversalRootModel", + "convert_and_respect_annotation_metadata", + "convert_file_dict_to_httpx_tuples", + "encode_query", + "jsonable_encoder", + "parse_obj_as", + "remove_none_from_dict", + "serialize_datetime", + "universal_field_validator", + "universal_root_validator", + "update_forward_refs", + "with_content_type", +] diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/api_error.py b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/api_error.py new file mode 100644 index 000000000000..6f850a60cba3 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/api_error.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +from typing import Any, Dict, Optional + + +class ApiError(Exception): + headers: Optional[Dict[str, str]] + status_code: Optional[int] + body: Any + + def __init__( + self, + *, + headers: Optional[Dict[str, str]] = None, + status_code: Optional[int] = None, + body: Any = None, + ) -> None: + self.headers = headers + self.status_code = status_code + self.body = body + + def __str__(self) -> str: + return f"headers: {self.headers}, status_code: {self.status_code}, body: {self.body}" diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/client_wrapper.py b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/client_wrapper.py new file mode 100644 index 000000000000..1c611d25b97a --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/client_wrapper.py @@ -0,0 +1,84 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import httpx +from .http_client import AsyncHttpClient, HttpClient + + +class BaseClientWrapper: + def __init__( + self, + *, + headers: typing.Optional[typing.Dict[str, str]] = None, + base_url: str, + timeout: typing.Optional[float] = None, + ): + self._headers = headers + self._base_url = base_url + self._timeout = timeout + + def get_headers(self) -> typing.Dict[str, str]: + headers: typing.Dict[str, str] = { + "User-Agent": "fern_inferred-auth-implicit-reference/0.0.1", + "X-Fern-Language": "Python", + "X-Fern-SDK-Name": "fern_inferred-auth-implicit-reference", + "X-Fern-SDK-Version": "0.0.1", + **(self.get_custom_headers() or {}), + } + return headers + + def get_custom_headers(self) -> typing.Optional[typing.Dict[str, str]]: + return self._headers + + def get_base_url(self) -> str: + return self._base_url + + def get_timeout(self) -> typing.Optional[float]: + return self._timeout + + +class SyncClientWrapper(BaseClientWrapper): + def __init__( + self, + *, + headers: typing.Optional[typing.Dict[str, str]] = None, + base_url: str, + timeout: typing.Optional[float] = None, + httpx_client: httpx.Client, + ): + super().__init__(headers=headers, base_url=base_url, timeout=timeout) + self.httpx_client = HttpClient( + httpx_client=httpx_client, + base_headers=self.get_headers, + base_timeout=self.get_timeout, + base_url=self.get_base_url, + ) + + +class AsyncClientWrapper(BaseClientWrapper): + def __init__( + self, + *, + headers: typing.Optional[typing.Dict[str, str]] = None, + base_url: str, + timeout: typing.Optional[float] = None, + async_token: typing.Optional[typing.Callable[[], typing.Awaitable[str]]] = None, + httpx_client: httpx.AsyncClient, + ): + super().__init__(headers=headers, base_url=base_url, timeout=timeout) + self._async_token = async_token + self.httpx_client = AsyncHttpClient( + httpx_client=httpx_client, + base_headers=self.get_headers, + base_timeout=self.get_timeout, + base_url=self.get_base_url, + async_base_headers=self.async_get_headers, + ) + + async def async_get_headers(self) -> typing.Dict[str, str]: + headers = self.get_headers() + if self._async_token is not None: + token = await self._async_token() + headers["Authorization"] = f"Bearer {token}" + return headers diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/datetime_utils.py b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/datetime_utils.py new file mode 100644 index 000000000000..7c9864a944c2 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/datetime_utils.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt + + +def serialize_datetime(v: dt.datetime) -> str: + """ + Serialize a datetime including timezone info. + + Uses the timezone info provided if present, otherwise uses the current runtime's timezone info. + + UTC datetimes end in "Z" while all other timezones are represented as offset from UTC, e.g. +05:00. + """ + + def _serialize_zoned_datetime(v: dt.datetime) -> str: + if v.tzinfo is not None and v.tzinfo.tzname(None) == dt.timezone.utc.tzname(None): + # UTC is a special case where we use "Z" at the end instead of "+00:00" + return v.isoformat().replace("+00:00", "Z") + else: + # Delegate to the typical +/- offset format + return v.isoformat() + + if v.tzinfo is not None: + return _serialize_zoned_datetime(v) + else: + local_tz = dt.datetime.now().astimezone().tzinfo + localized_dt = v.replace(tzinfo=local_tz) + return _serialize_zoned_datetime(localized_dt) diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/file.py b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/file.py new file mode 100644 index 000000000000..44b0d27c0895 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/file.py @@ -0,0 +1,67 @@ +# This file was auto-generated by Fern from our API Definition. + +from typing import IO, Dict, List, Mapping, Optional, Tuple, Union, cast + +# File typing inspired by the flexibility of types within the httpx library +# https://github.com/encode/httpx/blob/master/httpx/_types.py +FileContent = Union[IO[bytes], bytes, str] +File = Union[ + # file (or bytes) + FileContent, + # (filename, file (or bytes)) + Tuple[Optional[str], FileContent], + # (filename, file (or bytes), content_type) + Tuple[Optional[str], FileContent, Optional[str]], + # (filename, file (or bytes), content_type, headers) + Tuple[ + Optional[str], + FileContent, + Optional[str], + Mapping[str, str], + ], +] + + +def convert_file_dict_to_httpx_tuples( + d: Dict[str, Union[File, List[File]]], +) -> List[Tuple[str, File]]: + """ + The format we use is a list of tuples, where the first element is the + name of the file and the second is the file object. Typically HTTPX wants + a dict, but to be able to send lists of files, you have to use the list + approach (which also works for non-lists) + https://github.com/encode/httpx/pull/1032 + """ + + httpx_tuples = [] + for key, file_like in d.items(): + if isinstance(file_like, list): + for file_like_item in file_like: + httpx_tuples.append((key, file_like_item)) + else: + httpx_tuples.append((key, file_like)) + return httpx_tuples + + +def with_content_type(*, file: File, default_content_type: str) -> File: + """ + This function resolves to the file's content type, if provided, and defaults + to the default_content_type value if not. + """ + if isinstance(file, tuple): + if len(file) == 2: + filename, content = cast(Tuple[Optional[str], FileContent], file) # type: ignore + return (filename, content, default_content_type) + elif len(file) == 3: + filename, content, file_content_type = cast(Tuple[Optional[str], FileContent, Optional[str]], file) # type: ignore + out_content_type = file_content_type or default_content_type + return (filename, content, out_content_type) + elif len(file) == 4: + filename, content, file_content_type, headers = cast( # type: ignore + Tuple[Optional[str], FileContent, Optional[str], Mapping[str, str]], file + ) + out_content_type = file_content_type or default_content_type + return (filename, content, out_content_type, headers) + else: + raise ValueError(f"Unexpected tuple length: {len(file)}") + return (None, file, default_content_type) diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/force_multipart.py b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/force_multipart.py new file mode 100644 index 000000000000..5440913fd4bc --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/force_multipart.py @@ -0,0 +1,18 @@ +# This file was auto-generated by Fern from our API Definition. + +from typing import Any, Dict + + +class ForceMultipartDict(Dict[str, Any]): + """ + A dictionary subclass that always evaluates to True in boolean contexts. + + This is used to force multipart/form-data encoding in HTTP requests even when + the dictionary is empty, which would normally evaluate to False. + """ + + def __bool__(self) -> bool: + return True + + +FORCE_MULTIPART = ForceMultipartDict() diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/http_client.py b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/http_client.py new file mode 100644 index 000000000000..bf19fcc243b6 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/http_client.py @@ -0,0 +1,629 @@ +# This file was auto-generated by Fern from our API Definition. + +import asyncio +import email.utils +import re +import time +import typing +import urllib.parse +from contextlib import asynccontextmanager, contextmanager +from random import random + +import httpx +from .file import File, convert_file_dict_to_httpx_tuples +from .force_multipart import FORCE_MULTIPART +from .jsonable_encoder import jsonable_encoder +from .query_encoder import encode_query +from .remove_none_from_dict import remove_none_from_dict as remove_none_from_dict +from .request_options import RequestOptions +from httpx._types import RequestFiles + +INITIAL_RETRY_DELAY_SECONDS = 1.0 +MAX_RETRY_DELAY_SECONDS = 60.0 +JITTER_FACTOR = 0.2 # 20% random jitter + + +def _parse_retry_after(response_headers: httpx.Headers) -> typing.Optional[float]: + """ + This function parses the `Retry-After` header in a HTTP response and returns the number of seconds to wait. + + Inspired by the urllib3 retry implementation. + """ + retry_after_ms = response_headers.get("retry-after-ms") + if retry_after_ms is not None: + try: + return int(retry_after_ms) / 1000 if retry_after_ms > 0 else 0 + except Exception: + pass + + retry_after = response_headers.get("retry-after") + if retry_after is None: + return None + + # Attempt to parse the header as an int. + if re.match(r"^\s*[0-9]+\s*$", retry_after): + seconds = float(retry_after) + # Fallback to parsing it as a date. + else: + retry_date_tuple = email.utils.parsedate_tz(retry_after) + if retry_date_tuple is None: + return None + if retry_date_tuple[9] is None: # Python 2 + # Assume UTC if no timezone was specified + # On Python2.7, parsedate_tz returns None for a timezone offset + # instead of 0 if no timezone is given, where mktime_tz treats + # a None timezone offset as local time. + retry_date_tuple = retry_date_tuple[:9] + (0,) + retry_date_tuple[10:] + + retry_date = email.utils.mktime_tz(retry_date_tuple) + seconds = retry_date - time.time() + + if seconds < 0: + seconds = 0 + + return seconds + + +def _add_positive_jitter(delay: float) -> float: + """Add positive jitter (0-20%) to prevent thundering herd.""" + jitter_multiplier = 1 + random() * JITTER_FACTOR + return delay * jitter_multiplier + + +def _add_symmetric_jitter(delay: float) -> float: + """Add symmetric jitter (±10%) for exponential backoff.""" + jitter_multiplier = 1 + (random() - 0.5) * JITTER_FACTOR + return delay * jitter_multiplier + + +def _parse_x_ratelimit_reset(response_headers: httpx.Headers) -> typing.Optional[float]: + """ + Parse the X-RateLimit-Reset header (Unix timestamp in seconds). + Returns seconds to wait, or None if header is missing/invalid. + """ + reset_time_str = response_headers.get("x-ratelimit-reset") + if reset_time_str is None: + return None + + try: + reset_time = int(reset_time_str) + delay = reset_time - time.time() + if delay > 0: + return delay + except (ValueError, TypeError): + pass + + return None + + +def _retry_timeout(response: httpx.Response, retries: int) -> float: + """ + Determine the amount of time to wait before retrying a request. + This function begins by trying to parse a retry-after header from the response, and then proceeds to use exponential backoff + with a jitter to determine the number of seconds to wait. + """ + + # 1. Check Retry-After header first + retry_after = _parse_retry_after(response.headers) + if retry_after is not None and retry_after > 0: + return min(retry_after, MAX_RETRY_DELAY_SECONDS) + + # 2. Check X-RateLimit-Reset header (with positive jitter) + ratelimit_reset = _parse_x_ratelimit_reset(response.headers) + if ratelimit_reset is not None: + return _add_positive_jitter(min(ratelimit_reset, MAX_RETRY_DELAY_SECONDS)) + + # 3. Fall back to exponential backoff (with symmetric jitter) + backoff = min(INITIAL_RETRY_DELAY_SECONDS * pow(2.0, retries), MAX_RETRY_DELAY_SECONDS) + return _add_symmetric_jitter(backoff) + + +def _should_retry(response: httpx.Response) -> bool: + retryable_400s = [429, 408, 409] + return response.status_code >= 500 or response.status_code in retryable_400s + + +def _maybe_filter_none_from_multipart_data( + data: typing.Optional[typing.Any], + request_files: typing.Optional[RequestFiles], + force_multipart: typing.Optional[bool], +) -> typing.Optional[typing.Any]: + """ + Filter None values from data body for multipart/form requests. + This prevents httpx from converting None to empty strings in multipart encoding. + Only applies when files are present or force_multipart is True. + """ + if data is not None and isinstance(data, typing.Mapping) and (request_files or force_multipart): + return remove_none_from_dict(data) + return data + + +def remove_omit_from_dict( + original: typing.Dict[str, typing.Optional[typing.Any]], + omit: typing.Optional[typing.Any], +) -> typing.Dict[str, typing.Any]: + if omit is None: + return original + new: typing.Dict[str, typing.Any] = {} + for key, value in original.items(): + if value is not omit: + new[key] = value + return new + + +def maybe_filter_request_body( + data: typing.Optional[typing.Any], + request_options: typing.Optional[RequestOptions], + omit: typing.Optional[typing.Any], +) -> typing.Optional[typing.Any]: + if data is None: + return ( + jsonable_encoder(request_options.get("additional_body_parameters", {})) or {} + if request_options is not None + else None + ) + elif not isinstance(data, typing.Mapping): + data_content = jsonable_encoder(data) + else: + data_content = { + **(jsonable_encoder(remove_omit_from_dict(data, omit))), # type: ignore + **( + jsonable_encoder(request_options.get("additional_body_parameters", {})) or {} + if request_options is not None + else {} + ), + } + return data_content + + +# Abstracted out for testing purposes +def get_request_body( + *, + json: typing.Optional[typing.Any], + data: typing.Optional[typing.Any], + request_options: typing.Optional[RequestOptions], + omit: typing.Optional[typing.Any], +) -> typing.Tuple[typing.Optional[typing.Any], typing.Optional[typing.Any]]: + json_body = None + data_body = None + if data is not None: + data_body = maybe_filter_request_body(data, request_options, omit) + else: + # If both data and json are None, we send json data in the event extra properties are specified + json_body = maybe_filter_request_body(json, request_options, omit) + + # If you have an empty JSON body, you should just send None + return (json_body if json_body != {} else None), data_body if data_body != {} else None + + +class HttpClient: + def __init__( + self, + *, + httpx_client: httpx.Client, + base_timeout: typing.Callable[[], typing.Optional[float]], + base_headers: typing.Callable[[], typing.Dict[str, str]], + base_url: typing.Optional[typing.Callable[[], str]] = None, + ): + self.base_url = base_url + self.base_timeout = base_timeout + self.base_headers = base_headers + self.httpx_client = httpx_client + + def get_base_url(self, maybe_base_url: typing.Optional[str]) -> str: + base_url = maybe_base_url + if self.base_url is not None and base_url is None: + base_url = self.base_url() + + if base_url is None: + raise ValueError("A base_url is required to make this request, please provide one and try again.") + return base_url + + def request( + self, + path: typing.Optional[str] = None, + *, + method: str, + base_url: typing.Optional[str] = None, + params: typing.Optional[typing.Dict[str, typing.Any]] = None, + json: typing.Optional[typing.Any] = None, + data: typing.Optional[typing.Any] = None, + content: typing.Optional[typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]] = None, + files: typing.Optional[ + typing.Union[ + typing.Dict[str, typing.Optional[typing.Union[File, typing.List[File]]]], + typing.List[typing.Tuple[str, File]], + ] + ] = None, + headers: typing.Optional[typing.Dict[str, typing.Any]] = None, + request_options: typing.Optional[RequestOptions] = None, + retries: int = 2, + omit: typing.Optional[typing.Any] = None, + force_multipart: typing.Optional[bool] = None, + ) -> httpx.Response: + base_url = self.get_base_url(base_url) + timeout = ( + request_options.get("timeout_in_seconds") + if request_options is not None and request_options.get("timeout_in_seconds") is not None + else self.base_timeout() + ) + + json_body, data_body = get_request_body(json=json, data=data, request_options=request_options, omit=omit) + + request_files: typing.Optional[RequestFiles] = ( + convert_file_dict_to_httpx_tuples(remove_omit_from_dict(remove_none_from_dict(files), omit)) + if (files is not None and files is not omit and isinstance(files, dict)) + else None + ) + + if (request_files is None or len(request_files) == 0) and force_multipart: + request_files = FORCE_MULTIPART + + data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + + response = self.httpx_client.request( + method=method, + url=urllib.parse.urljoin(f"{base_url}/", path), + headers=jsonable_encoder( + remove_none_from_dict( + { + **self.base_headers(), + **(headers if headers is not None else {}), + **(request_options.get("additional_headers", {}) or {} if request_options is not None else {}), + } + ) + ), + params=_encoded_params if _encoded_params else None, + json=json_body, + data=data_body, + content=content, + files=request_files, + timeout=timeout, + ) + + max_retries: int = request_options.get("max_retries", 0) if request_options is not None else 0 + if _should_retry(response=response): + if max_retries > retries: + time.sleep(_retry_timeout(response=response, retries=retries)) + return self.request( + path=path, + method=method, + base_url=base_url, + params=params, + json=json, + content=content, + files=files, + headers=headers, + request_options=request_options, + retries=retries + 1, + omit=omit, + ) + + return response + + @contextmanager + def stream( + self, + path: typing.Optional[str] = None, + *, + method: str, + base_url: typing.Optional[str] = None, + params: typing.Optional[typing.Dict[str, typing.Any]] = None, + json: typing.Optional[typing.Any] = None, + data: typing.Optional[typing.Any] = None, + content: typing.Optional[typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]] = None, + files: typing.Optional[ + typing.Union[ + typing.Dict[str, typing.Optional[typing.Union[File, typing.List[File]]]], + typing.List[typing.Tuple[str, File]], + ] + ] = None, + headers: typing.Optional[typing.Dict[str, typing.Any]] = None, + request_options: typing.Optional[RequestOptions] = None, + retries: int = 2, + omit: typing.Optional[typing.Any] = None, + force_multipart: typing.Optional[bool] = None, + ) -> typing.Iterator[httpx.Response]: + base_url = self.get_base_url(base_url) + timeout = ( + request_options.get("timeout_in_seconds") + if request_options is not None and request_options.get("timeout_in_seconds") is not None + else self.base_timeout() + ) + + request_files: typing.Optional[RequestFiles] = ( + convert_file_dict_to_httpx_tuples(remove_omit_from_dict(remove_none_from_dict(files), omit)) + if (files is not None and files is not omit and isinstance(files, dict)) + else None + ) + + if (request_files is None or len(request_files) == 0) and force_multipart: + request_files = FORCE_MULTIPART + + json_body, data_body = get_request_body(json=json, data=data, request_options=request_options, omit=omit) + + data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + + with self.httpx_client.stream( + method=method, + url=urllib.parse.urljoin(f"{base_url}/", path), + headers=jsonable_encoder( + remove_none_from_dict( + { + **self.base_headers(), + **(headers if headers is not None else {}), + **(request_options.get("additional_headers", {}) if request_options is not None else {}), + } + ) + ), + params=_encoded_params if _encoded_params else None, + json=json_body, + data=data_body, + content=content, + files=request_files, + timeout=timeout, + ) as stream: + yield stream + + +class AsyncHttpClient: + def __init__( + self, + *, + httpx_client: httpx.AsyncClient, + base_timeout: typing.Callable[[], typing.Optional[float]], + base_headers: typing.Callable[[], typing.Dict[str, str]], + base_url: typing.Optional[typing.Callable[[], str]] = None, + async_base_headers: typing.Optional[typing.Callable[[], typing.Awaitable[typing.Dict[str, str]]]] = None, + ): + self.base_url = base_url + self.base_timeout = base_timeout + self.base_headers = base_headers + self.async_base_headers = async_base_headers + self.httpx_client = httpx_client + + async def _get_headers(self) -> typing.Dict[str, str]: + if self.async_base_headers is not None: + return await self.async_base_headers() + return self.base_headers() + + def get_base_url(self, maybe_base_url: typing.Optional[str]) -> str: + base_url = maybe_base_url + if self.base_url is not None and base_url is None: + base_url = self.base_url() + + if base_url is None: + raise ValueError("A base_url is required to make this request, please provide one and try again.") + return base_url + + async def request( + self, + path: typing.Optional[str] = None, + *, + method: str, + base_url: typing.Optional[str] = None, + params: typing.Optional[typing.Dict[str, typing.Any]] = None, + json: typing.Optional[typing.Any] = None, + data: typing.Optional[typing.Any] = None, + content: typing.Optional[typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]] = None, + files: typing.Optional[ + typing.Union[ + typing.Dict[str, typing.Optional[typing.Union[File, typing.List[File]]]], + typing.List[typing.Tuple[str, File]], + ] + ] = None, + headers: typing.Optional[typing.Dict[str, typing.Any]] = None, + request_options: typing.Optional[RequestOptions] = None, + retries: int = 2, + omit: typing.Optional[typing.Any] = None, + force_multipart: typing.Optional[bool] = None, + ) -> httpx.Response: + base_url = self.get_base_url(base_url) + timeout = ( + request_options.get("timeout_in_seconds") + if request_options is not None and request_options.get("timeout_in_seconds") is not None + else self.base_timeout() + ) + + request_files: typing.Optional[RequestFiles] = ( + convert_file_dict_to_httpx_tuples(remove_omit_from_dict(remove_none_from_dict(files), omit)) + if (files is not None and files is not omit and isinstance(files, dict)) + else None + ) + + if (request_files is None or len(request_files) == 0) and force_multipart: + request_files = FORCE_MULTIPART + + json_body, data_body = get_request_body(json=json, data=data, request_options=request_options, omit=omit) + + data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + + # Get headers (supports async token providers) + _headers = await self._get_headers() + + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + + # Add the input to each of these and do None-safety checks + response = await self.httpx_client.request( + method=method, + url=urllib.parse.urljoin(f"{base_url}/", path), + headers=jsonable_encoder( + remove_none_from_dict( + { + **_headers, + **(headers if headers is not None else {}), + **(request_options.get("additional_headers", {}) or {} if request_options is not None else {}), + } + ) + ), + params=_encoded_params if _encoded_params else None, + json=json_body, + data=data_body, + content=content, + files=request_files, + timeout=timeout, + ) + + max_retries: int = request_options.get("max_retries", 0) if request_options is not None else 0 + if _should_retry(response=response): + if max_retries > retries: + await asyncio.sleep(_retry_timeout(response=response, retries=retries)) + return await self.request( + path=path, + method=method, + base_url=base_url, + params=params, + json=json, + content=content, + files=files, + headers=headers, + request_options=request_options, + retries=retries + 1, + omit=omit, + ) + return response + + @asynccontextmanager + async def stream( + self, + path: typing.Optional[str] = None, + *, + method: str, + base_url: typing.Optional[str] = None, + params: typing.Optional[typing.Dict[str, typing.Any]] = None, + json: typing.Optional[typing.Any] = None, + data: typing.Optional[typing.Any] = None, + content: typing.Optional[typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]] = None, + files: typing.Optional[ + typing.Union[ + typing.Dict[str, typing.Optional[typing.Union[File, typing.List[File]]]], + typing.List[typing.Tuple[str, File]], + ] + ] = None, + headers: typing.Optional[typing.Dict[str, typing.Any]] = None, + request_options: typing.Optional[RequestOptions] = None, + retries: int = 2, + omit: typing.Optional[typing.Any] = None, + force_multipart: typing.Optional[bool] = None, + ) -> typing.AsyncIterator[httpx.Response]: + base_url = self.get_base_url(base_url) + timeout = ( + request_options.get("timeout_in_seconds") + if request_options is not None and request_options.get("timeout_in_seconds") is not None + else self.base_timeout() + ) + + request_files: typing.Optional[RequestFiles] = ( + convert_file_dict_to_httpx_tuples(remove_omit_from_dict(remove_none_from_dict(files), omit)) + if (files is not None and files is not omit and isinstance(files, dict)) + else None + ) + + if (request_files is None or len(request_files) == 0) and force_multipart: + request_files = FORCE_MULTIPART + + json_body, data_body = get_request_body(json=json, data=data, request_options=request_options, omit=omit) + + data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + + # Get headers (supports async token providers) + _headers = await self._get_headers() + + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + + async with self.httpx_client.stream( + method=method, + url=urllib.parse.urljoin(f"{base_url}/", path), + headers=jsonable_encoder( + remove_none_from_dict( + { + **_headers, + **(headers if headers is not None else {}), + **(request_options.get("additional_headers", {}) if request_options is not None else {}), + } + ) + ), + params=_encoded_params if _encoded_params else None, + json=json_body, + data=data_body, + content=content, + files=request_files, + timeout=timeout, + ) as stream: + yield stream diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/http_response.py b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/http_response.py new file mode 100644 index 000000000000..2479747e8bb0 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/http_response.py @@ -0,0 +1,55 @@ +# This file was auto-generated by Fern from our API Definition. + +from typing import Dict, Generic, TypeVar + +import httpx + +# Generic to represent the underlying type of the data wrapped by the HTTP response. +T = TypeVar("T") + + +class BaseHttpResponse: + """Minimalist HTTP response wrapper that exposes response headers.""" + + _response: httpx.Response + + def __init__(self, response: httpx.Response): + self._response = response + + @property + def headers(self) -> Dict[str, str]: + return dict(self._response.headers) + + +class HttpResponse(Generic[T], BaseHttpResponse): + """HTTP response wrapper that exposes response headers and data.""" + + _data: T + + def __init__(self, response: httpx.Response, data: T): + super().__init__(response) + self._data = data + + @property + def data(self) -> T: + return self._data + + def close(self) -> None: + self._response.close() + + +class AsyncHttpResponse(Generic[T], BaseHttpResponse): + """HTTP response wrapper that exposes response headers and data.""" + + _data: T + + def __init__(self, response: httpx.Response, data: T): + super().__init__(response) + self._data = data + + @property + def data(self) -> T: + return self._data + + async def close(self) -> None: + await self._response.aclose() diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/http_sse/__init__.py b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/http_sse/__init__.py new file mode 100644 index 000000000000..730e5a3382eb --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/http_sse/__init__.py @@ -0,0 +1,42 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from ._api import EventSource, aconnect_sse, connect_sse + from ._exceptions import SSEError + from ._models import ServerSentEvent +_dynamic_imports: typing.Dict[str, str] = { + "EventSource": "._api", + "SSEError": "._exceptions", + "ServerSentEvent": "._models", + "aconnect_sse": "._api", + "connect_sse": "._api", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["EventSource", "SSEError", "ServerSentEvent", "aconnect_sse", "connect_sse"] diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/http_sse/_api.py b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/http_sse/_api.py new file mode 100644 index 000000000000..f900b3b686de --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/http_sse/_api.py @@ -0,0 +1,112 @@ +# This file was auto-generated by Fern from our API Definition. + +import re +from contextlib import asynccontextmanager, contextmanager +from typing import Any, AsyncGenerator, AsyncIterator, Iterator, cast + +import httpx +from ._decoders import SSEDecoder +from ._exceptions import SSEError +from ._models import ServerSentEvent + + +class EventSource: + def __init__(self, response: httpx.Response) -> None: + self._response = response + + def _check_content_type(self) -> None: + content_type = self._response.headers.get("content-type", "").partition(";")[0] + if "text/event-stream" not in content_type: + raise SSEError( + f"Expected response header Content-Type to contain 'text/event-stream', got {content_type!r}" + ) + + def _get_charset(self) -> str: + """Extract charset from Content-Type header, fallback to UTF-8.""" + content_type = self._response.headers.get("content-type", "") + + # Parse charset parameter using regex + charset_match = re.search(r"charset=([^;\s]+)", content_type, re.IGNORECASE) + if charset_match: + charset = charset_match.group(1).strip("\"'") + # Validate that it's a known encoding + try: + # Test if the charset is valid by trying to encode/decode + "test".encode(charset).decode(charset) + return charset + except (LookupError, UnicodeError): + # If charset is invalid, fall back to UTF-8 + pass + + # Default to UTF-8 if no charset specified or invalid charset + return "utf-8" + + @property + def response(self) -> httpx.Response: + return self._response + + def iter_sse(self) -> Iterator[ServerSentEvent]: + self._check_content_type() + decoder = SSEDecoder() + charset = self._get_charset() + + buffer = "" + for chunk in self._response.iter_bytes(): + # Decode chunk using detected charset + text_chunk = chunk.decode(charset, errors="replace") + buffer += text_chunk + + # Process complete lines + while "\n" in buffer: + line, buffer = buffer.split("\n", 1) + line = line.rstrip("\r") + sse = decoder.decode(line) + # when we reach a "\n\n" => line = '' + # => decoder will attempt to return an SSE Event + if sse is not None: + yield sse + + # Process any remaining data in buffer + if buffer.strip(): + line = buffer.rstrip("\r") + sse = decoder.decode(line) + if sse is not None: + yield sse + + async def aiter_sse(self) -> AsyncGenerator[ServerSentEvent, None]: + self._check_content_type() + decoder = SSEDecoder() + lines = cast(AsyncGenerator[str, None], self._response.aiter_lines()) + try: + async for line in lines: + line = line.rstrip("\n") + sse = decoder.decode(line) + if sse is not None: + yield sse + finally: + await lines.aclose() + + +@contextmanager +def connect_sse(client: httpx.Client, method: str, url: str, **kwargs: Any) -> Iterator[EventSource]: + headers = kwargs.pop("headers", {}) + headers["Accept"] = "text/event-stream" + headers["Cache-Control"] = "no-store" + + with client.stream(method, url, headers=headers, **kwargs) as response: + yield EventSource(response) + + +@asynccontextmanager +async def aconnect_sse( + client: httpx.AsyncClient, + method: str, + url: str, + **kwargs: Any, +) -> AsyncIterator[EventSource]: + headers = kwargs.pop("headers", {}) + headers["Accept"] = "text/event-stream" + headers["Cache-Control"] = "no-store" + + async with client.stream(method, url, headers=headers, **kwargs) as response: + yield EventSource(response) diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/http_sse/_decoders.py b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/http_sse/_decoders.py new file mode 100644 index 000000000000..339b08901381 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/http_sse/_decoders.py @@ -0,0 +1,61 @@ +# This file was auto-generated by Fern from our API Definition. + +from typing import List, Optional + +from ._models import ServerSentEvent + + +class SSEDecoder: + def __init__(self) -> None: + self._event = "" + self._data: List[str] = [] + self._last_event_id = "" + self._retry: Optional[int] = None + + def decode(self, line: str) -> Optional[ServerSentEvent]: + # See: https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation # noqa: E501 + + if not line: + if not self._event and not self._data and not self._last_event_id and self._retry is None: + return None + + sse = ServerSentEvent( + event=self._event, + data="\n".join(self._data), + id=self._last_event_id, + retry=self._retry, + ) + + # NOTE: as per the SSE spec, do not reset last_event_id. + self._event = "" + self._data = [] + self._retry = None + + return sse + + if line.startswith(":"): + return None + + fieldname, _, value = line.partition(":") + + if value.startswith(" "): + value = value[1:] + + if fieldname == "event": + self._event = value + elif fieldname == "data": + self._data.append(value) + elif fieldname == "id": + if "\0" in value: + pass + else: + self._last_event_id = value + elif fieldname == "retry": + try: + self._retry = int(value) + except (TypeError, ValueError): + pass + else: + pass # Field is ignored. + + return None diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/http_sse/_exceptions.py b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/http_sse/_exceptions.py new file mode 100644 index 000000000000..81605a8a65ed --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/http_sse/_exceptions.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import httpx + + +class SSEError(httpx.TransportError): + pass diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/http_sse/_models.py b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/http_sse/_models.py new file mode 100644 index 000000000000..1af57f8fd0d2 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/http_sse/_models.py @@ -0,0 +1,17 @@ +# This file was auto-generated by Fern from our API Definition. + +import json +from dataclasses import dataclass +from typing import Any, Optional + + +@dataclass(frozen=True) +class ServerSentEvent: + event: str = "message" + data: str = "" + id: str = "" + retry: Optional[int] = None + + def json(self) -> Any: + """Parse the data field as JSON.""" + return json.loads(self.data) diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/jsonable_encoder.py b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/jsonable_encoder.py new file mode 100644 index 000000000000..afee3662d836 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/jsonable_encoder.py @@ -0,0 +1,100 @@ +# This file was auto-generated by Fern from our API Definition. + +""" +jsonable_encoder converts a Python object to a JSON-friendly dict +(e.g. datetimes to strings, Pydantic models to dicts). + +Taken from FastAPI, and made a bit simpler +https://github.com/tiangolo/fastapi/blob/master/fastapi/encoders.py +""" + +import base64 +import dataclasses +import datetime as dt +from enum import Enum +from pathlib import PurePath +from types import GeneratorType +from typing import Any, Callable, Dict, List, Optional, Set, Union + +import pydantic +from .datetime_utils import serialize_datetime +from .pydantic_utilities import ( + IS_PYDANTIC_V2, + encode_by_type, + to_jsonable_with_fallback, +) + +SetIntStr = Set[Union[int, str]] +DictIntStrAny = Dict[Union[int, str], Any] + + +def jsonable_encoder(obj: Any, custom_encoder: Optional[Dict[Any, Callable[[Any], Any]]] = None) -> Any: + custom_encoder = custom_encoder or {} + if custom_encoder: + if type(obj) in custom_encoder: + return custom_encoder[type(obj)](obj) + else: + for encoder_type, encoder_instance in custom_encoder.items(): + if isinstance(obj, encoder_type): + return encoder_instance(obj) + if isinstance(obj, pydantic.BaseModel): + if IS_PYDANTIC_V2: + encoder = getattr(obj.model_config, "json_encoders", {}) # type: ignore # Pydantic v2 + else: + encoder = getattr(obj.__config__, "json_encoders", {}) # type: ignore # Pydantic v1 + if custom_encoder: + encoder.update(custom_encoder) + obj_dict = obj.dict(by_alias=True) + if "__root__" in obj_dict: + obj_dict = obj_dict["__root__"] + if "root" in obj_dict: + obj_dict = obj_dict["root"] + return jsonable_encoder(obj_dict, custom_encoder=encoder) + if dataclasses.is_dataclass(obj): + obj_dict = dataclasses.asdict(obj) # type: ignore + return jsonable_encoder(obj_dict, custom_encoder=custom_encoder) + if isinstance(obj, bytes): + return base64.b64encode(obj).decode("utf-8") + if isinstance(obj, Enum): + return obj.value + if isinstance(obj, PurePath): + return str(obj) + if isinstance(obj, (str, int, float, type(None))): + return obj + if isinstance(obj, dt.datetime): + return serialize_datetime(obj) + if isinstance(obj, dt.date): + return str(obj) + if isinstance(obj, dict): + encoded_dict = {} + allowed_keys = set(obj.keys()) + for key, value in obj.items(): + if key in allowed_keys: + encoded_key = jsonable_encoder(key, custom_encoder=custom_encoder) + encoded_value = jsonable_encoder(value, custom_encoder=custom_encoder) + encoded_dict[encoded_key] = encoded_value + return encoded_dict + if isinstance(obj, (list, set, frozenset, GeneratorType, tuple)): + encoded_list = [] + for item in obj: + encoded_list.append(jsonable_encoder(item, custom_encoder=custom_encoder)) + return encoded_list + + def fallback_serializer(o: Any) -> Any: + attempt_encode = encode_by_type(o) + if attempt_encode is not None: + return attempt_encode + + try: + data = dict(o) + except Exception as e: + errors: List[Exception] = [] + errors.append(e) + try: + data = vars(o) + except Exception as e: + errors.append(e) + raise ValueError(errors) from e + return jsonable_encoder(data, custom_encoder=custom_encoder) + + return to_jsonable_with_fallback(obj, fallback_serializer) diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/pydantic_utilities.py b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/pydantic_utilities.py new file mode 100644 index 000000000000..185e5c4f64be --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/pydantic_utilities.py @@ -0,0 +1,260 @@ +# This file was auto-generated by Fern from our API Definition. + +# nopycln: file +import datetime as dt +from collections import defaultdict +from typing import Any, Callable, ClassVar, Dict, List, Mapping, Optional, Set, Tuple, Type, TypeVar, Union, cast + +import pydantic + +IS_PYDANTIC_V2 = pydantic.VERSION.startswith("2.") + +if IS_PYDANTIC_V2: + from pydantic.v1.datetime_parse import parse_date as parse_date + from pydantic.v1.datetime_parse import parse_datetime as parse_datetime + from pydantic.v1.fields import ModelField as ModelField + from pydantic.v1.json import ENCODERS_BY_TYPE as encoders_by_type # type: ignore[attr-defined] + from pydantic.v1.typing import get_args as get_args + from pydantic.v1.typing import get_origin as get_origin + from pydantic.v1.typing import is_literal_type as is_literal_type + from pydantic.v1.typing import is_union as is_union +else: + from pydantic.datetime_parse import parse_date as parse_date # type: ignore[no-redef] + from pydantic.datetime_parse import parse_datetime as parse_datetime # type: ignore[no-redef] + from pydantic.fields import ModelField as ModelField # type: ignore[attr-defined, no-redef] + from pydantic.json import ENCODERS_BY_TYPE as encoders_by_type # type: ignore[no-redef] + from pydantic.typing import get_args as get_args # type: ignore[no-redef] + from pydantic.typing import get_origin as get_origin # type: ignore[no-redef] + from pydantic.typing import is_literal_type as is_literal_type # type: ignore[no-redef] + from pydantic.typing import is_union as is_union # type: ignore[no-redef] + +from .datetime_utils import serialize_datetime +from .serialization import convert_and_respect_annotation_metadata +from typing_extensions import TypeAlias + +T = TypeVar("T") +Model = TypeVar("Model", bound=pydantic.BaseModel) + + +def parse_obj_as(type_: Type[T], object_: Any) -> T: + dealiased_object = convert_and_respect_annotation_metadata(object_=object_, annotation=type_, direction="read") + if IS_PYDANTIC_V2: + adapter = pydantic.TypeAdapter(type_) # type: ignore[attr-defined] + return adapter.validate_python(dealiased_object) + return pydantic.parse_obj_as(type_, dealiased_object) + + +def to_jsonable_with_fallback(obj: Any, fallback_serializer: Callable[[Any], Any]) -> Any: + if IS_PYDANTIC_V2: + from pydantic_core import to_jsonable_python + + return to_jsonable_python(obj, fallback=fallback_serializer) + return fallback_serializer(obj) + + +class UniversalBaseModel(pydantic.BaseModel): + if IS_PYDANTIC_V2: + model_config: ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( # type: ignore[typeddict-unknown-key] + # Allow fields beginning with `model_` to be used in the model + protected_namespaces=(), + ) + + @pydantic.model_serializer(mode="plain", when_used="json") # type: ignore[attr-defined] + def serialize_model(self) -> Any: # type: ignore[name-defined] + serialized = self.dict() # type: ignore[attr-defined] + data = {k: serialize_datetime(v) if isinstance(v, dt.datetime) else v for k, v in serialized.items()} + return data + + else: + + class Config: + smart_union = True + json_encoders = {dt.datetime: serialize_datetime} + + @classmethod + def model_construct(cls: Type["Model"], _fields_set: Optional[Set[str]] = None, **values: Any) -> "Model": + dealiased_object = convert_and_respect_annotation_metadata(object_=values, annotation=cls, direction="read") + return cls.construct(_fields_set, **dealiased_object) + + @classmethod + def construct(cls: Type["Model"], _fields_set: Optional[Set[str]] = None, **values: Any) -> "Model": + dealiased_object = convert_and_respect_annotation_metadata(object_=values, annotation=cls, direction="read") + if IS_PYDANTIC_V2: + return super().model_construct(_fields_set, **dealiased_object) # type: ignore[misc] + return super().construct(_fields_set, **dealiased_object) + + def json(self, **kwargs: Any) -> str: + kwargs_with_defaults = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + if IS_PYDANTIC_V2: + return super().model_dump_json(**kwargs_with_defaults) # type: ignore[misc] + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: Any) -> Dict[str, Any]: + """ + Override the default dict method to `exclude_unset` by default. This function patches + `exclude_unset` to work include fields within non-None default values. + """ + # Note: the logic here is multiplexed given the levers exposed in Pydantic V1 vs V2 + # Pydantic V1's .dict can be extremely slow, so we do not want to call it twice. + # + # We'd ideally do the same for Pydantic V2, but it shells out to a library to serialize models + # that we have less control over, and this is less intrusive than custom serializers for now. + if IS_PYDANTIC_V2: + kwargs_with_defaults_exclude_unset = { + **kwargs, + "by_alias": True, + "exclude_unset": True, + "exclude_none": False, + } + kwargs_with_defaults_exclude_none = { + **kwargs, + "by_alias": True, + "exclude_none": True, + "exclude_unset": False, + } + dict_dump = deep_union_pydantic_dicts( + super().model_dump(**kwargs_with_defaults_exclude_unset), # type: ignore[misc] + super().model_dump(**kwargs_with_defaults_exclude_none), # type: ignore[misc] + ) + + else: + _fields_set = self.__fields_set__.copy() + + fields = _get_model_fields(self.__class__) + for name, field in fields.items(): + if name not in _fields_set: + default = _get_field_default(field) + + # If the default values are non-null act like they've been set + # This effectively allows exclude_unset to work like exclude_none where + # the latter passes through intentionally set none values. + if default is not None or ("exclude_unset" in kwargs and not kwargs["exclude_unset"]): + _fields_set.add(name) + + if default is not None: + self.__fields_set__.add(name) + + kwargs_with_defaults_exclude_unset_include_fields = { + "by_alias": True, + "exclude_unset": True, + "include": _fields_set, + **kwargs, + } + + dict_dump = super().dict(**kwargs_with_defaults_exclude_unset_include_fields) + + return cast( + Dict[str, Any], + convert_and_respect_annotation_metadata(object_=dict_dump, annotation=self.__class__, direction="write"), + ) + + +def _union_list_of_pydantic_dicts(source: List[Any], destination: List[Any]) -> List[Any]: + converted_list: List[Any] = [] + for i, item in enumerate(source): + destination_value = destination[i] + if isinstance(item, dict): + converted_list.append(deep_union_pydantic_dicts(item, destination_value)) + elif isinstance(item, list): + converted_list.append(_union_list_of_pydantic_dicts(item, destination_value)) + else: + converted_list.append(item) + return converted_list + + +def deep_union_pydantic_dicts(source: Dict[str, Any], destination: Dict[str, Any]) -> Dict[str, Any]: + for key, value in source.items(): + node = destination.setdefault(key, {}) + if isinstance(value, dict): + deep_union_pydantic_dicts(value, node) + # Note: we do not do this same processing for sets given we do not have sets of models + # and given the sets are unordered, the processing of the set and matching objects would + # be non-trivial. + elif isinstance(value, list): + destination[key] = _union_list_of_pydantic_dicts(value, node) + else: + destination[key] = value + + return destination + + +if IS_PYDANTIC_V2: + + class V2RootModel(UniversalBaseModel, pydantic.RootModel): # type: ignore[misc, name-defined, type-arg] + pass + + UniversalRootModel: TypeAlias = V2RootModel # type: ignore[misc] +else: + UniversalRootModel: TypeAlias = UniversalBaseModel # type: ignore[misc, no-redef] + + +def encode_by_type(o: Any) -> Any: + encoders_by_class_tuples: Dict[Callable[[Any], Any], Tuple[Any, ...]] = defaultdict(tuple) + for type_, encoder in encoders_by_type.items(): + encoders_by_class_tuples[encoder] += (type_,) + + if type(o) in encoders_by_type: + return encoders_by_type[type(o)](o) + for encoder, classes_tuple in encoders_by_class_tuples.items(): + if isinstance(o, classes_tuple): + return encoder(o) + + +def update_forward_refs(model: Type["Model"], **localns: Any) -> None: + if IS_PYDANTIC_V2: + model.model_rebuild(raise_errors=False) # type: ignore[attr-defined] + else: + model.update_forward_refs(**localns) + + +# Mirrors Pydantic's internal typing +AnyCallable = Callable[..., Any] + + +def universal_root_validator( + pre: bool = False, +) -> Callable[[AnyCallable], AnyCallable]: + def decorator(func: AnyCallable) -> AnyCallable: + if IS_PYDANTIC_V2: + # In Pydantic v2, for RootModel we always use "before" mode + # The custom validators transform the input value before the model is created + return cast(AnyCallable, pydantic.model_validator(mode="before")(func)) # type: ignore[attr-defined] + return cast(AnyCallable, pydantic.root_validator(pre=pre)(func)) # type: ignore[call-overload] + + return decorator + + +def universal_field_validator(field_name: str, pre: bool = False) -> Callable[[AnyCallable], AnyCallable]: + def decorator(func: AnyCallable) -> AnyCallable: + if IS_PYDANTIC_V2: + return cast(AnyCallable, pydantic.field_validator(field_name, mode="before" if pre else "after")(func)) # type: ignore[attr-defined] + return cast(AnyCallable, pydantic.validator(field_name, pre=pre)(func)) + + return decorator + + +PydanticField = Union[ModelField, pydantic.fields.FieldInfo] + + +def _get_model_fields(model: Type["Model"]) -> Mapping[str, PydanticField]: + if IS_PYDANTIC_V2: + return cast(Mapping[str, PydanticField], model.model_fields) # type: ignore[attr-defined] + return cast(Mapping[str, PydanticField], model.__fields__) + + +def _get_field_default(field: PydanticField) -> Any: + try: + value = field.get_default() # type: ignore[union-attr] + except: + value = field.default + if IS_PYDANTIC_V2: + from pydantic_core import PydanticUndefined + + if value == PydanticUndefined: + return None + return value + return value diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/query_encoder.py b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/query_encoder.py new file mode 100644 index 000000000000..3183001d4046 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/query_encoder.py @@ -0,0 +1,58 @@ +# This file was auto-generated by Fern from our API Definition. + +from typing import Any, Dict, List, Optional, Tuple + +import pydantic + + +# Flattens dicts to be of the form {"key[subkey][subkey2]": value} where value is not a dict +def traverse_query_dict(dict_flat: Dict[str, Any], key_prefix: Optional[str] = None) -> List[Tuple[str, Any]]: + result = [] + for k, v in dict_flat.items(): + key = f"{key_prefix}[{k}]" if key_prefix is not None else k + if isinstance(v, dict): + result.extend(traverse_query_dict(v, key)) + elif isinstance(v, list): + for arr_v in v: + if isinstance(arr_v, dict): + result.extend(traverse_query_dict(arr_v, key)) + else: + result.append((key, arr_v)) + else: + result.append((key, v)) + return result + + +def single_query_encoder(query_key: str, query_value: Any) -> List[Tuple[str, Any]]: + if isinstance(query_value, pydantic.BaseModel) or isinstance(query_value, dict): + if isinstance(query_value, pydantic.BaseModel): + obj_dict = query_value.dict(by_alias=True) + else: + obj_dict = query_value + return traverse_query_dict(obj_dict, query_key) + elif isinstance(query_value, list): + encoded_values: List[Tuple[str, Any]] = [] + for value in query_value: + if isinstance(value, pydantic.BaseModel) or isinstance(value, dict): + if isinstance(value, pydantic.BaseModel): + obj_dict = value.dict(by_alias=True) + elif isinstance(value, dict): + obj_dict = value + + encoded_values.extend(single_query_encoder(query_key, obj_dict)) + else: + encoded_values.append((query_key, value)) + + return encoded_values + + return [(query_key, query_value)] + + +def encode_query(query: Optional[Dict[str, Any]]) -> Optional[List[Tuple[str, Any]]]: + if query is None: + return None + + encoded_query = [] + for k, v in query.items(): + encoded_query.extend(single_query_encoder(k, v)) + return encoded_query diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/remove_none_from_dict.py b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/remove_none_from_dict.py new file mode 100644 index 000000000000..c2298143f14a --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/remove_none_from_dict.py @@ -0,0 +1,11 @@ +# This file was auto-generated by Fern from our API Definition. + +from typing import Any, Dict, Mapping, Optional + + +def remove_none_from_dict(original: Mapping[str, Optional[Any]]) -> Dict[str, Any]: + new: Dict[str, Any] = {} + for key, value in original.items(): + if value is not None: + new[key] = value + return new diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/request_options.py b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/request_options.py new file mode 100644 index 000000000000..1b38804432ba --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/request_options.py @@ -0,0 +1,35 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +try: + from typing import NotRequired # type: ignore +except ImportError: + from typing_extensions import NotRequired + + +class RequestOptions(typing.TypedDict, total=False): + """ + Additional options for request-specific configuration when calling APIs via the SDK. + This is used primarily as an optional final parameter for service functions. + + Attributes: + - timeout_in_seconds: int. The number of seconds to await an API call before timing out. + + - max_retries: int. The max number of retries to attempt if the API call fails. + + - additional_headers: typing.Dict[str, typing.Any]. A dictionary containing additional parameters to spread into the request's header dict + + - additional_query_parameters: typing.Dict[str, typing.Any]. A dictionary containing additional parameters to spread into the request's query parameters dict + + - additional_body_parameters: typing.Dict[str, typing.Any]. A dictionary containing additional parameters to spread into the request's body parameters dict + + - chunk_size: int. The size, in bytes, to process each chunk of data being streamed back within the response. This equates to leveraging `chunk_size` within `requests` or `httpx`, and is only leveraged for file downloads. + """ + + timeout_in_seconds: NotRequired[int] + max_retries: NotRequired[int] + additional_headers: NotRequired[typing.Dict[str, typing.Any]] + additional_query_parameters: NotRequired[typing.Dict[str, typing.Any]] + additional_body_parameters: NotRequired[typing.Dict[str, typing.Any]] + chunk_size: NotRequired[int] diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/serialization.py b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/serialization.py new file mode 100644 index 000000000000..c36e865cc729 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/core/serialization.py @@ -0,0 +1,276 @@ +# This file was auto-generated by Fern from our API Definition. + +import collections +import inspect +import typing + +import pydantic +import typing_extensions + + +class FieldMetadata: + """ + Metadata class used to annotate fields to provide additional information. + + Example: + class MyDict(TypedDict): + field: typing.Annotated[str, FieldMetadata(alias="field_name")] + + Will serialize: `{"field": "value"}` + To: `{"field_name": "value"}` + """ + + alias: str + + def __init__(self, *, alias: str) -> None: + self.alias = alias + + +def convert_and_respect_annotation_metadata( + *, + object_: typing.Any, + annotation: typing.Any, + inner_type: typing.Optional[typing.Any] = None, + direction: typing.Literal["read", "write"], +) -> typing.Any: + """ + Respect the metadata annotations on a field, such as aliasing. This function effectively + manipulates the dict-form of an object to respect the metadata annotations. This is primarily used for + TypedDicts, which cannot support aliasing out of the box, and can be extended for additional + utilities, such as defaults. + + Parameters + ---------- + object_ : typing.Any + + annotation : type + The type we're looking to apply typing annotations from + + inner_type : typing.Optional[type] + + Returns + ------- + typing.Any + """ + + if object_ is None: + return None + if inner_type is None: + inner_type = annotation + + clean_type = _remove_annotations(inner_type) + # Pydantic models + if ( + inspect.isclass(clean_type) + and issubclass(clean_type, pydantic.BaseModel) + and isinstance(object_, typing.Mapping) + ): + return _convert_mapping(object_, clean_type, direction) + # TypedDicts + if typing_extensions.is_typeddict(clean_type) and isinstance(object_, typing.Mapping): + return _convert_mapping(object_, clean_type, direction) + + if ( + typing_extensions.get_origin(clean_type) == typing.Dict + or typing_extensions.get_origin(clean_type) == dict + or clean_type == typing.Dict + ) and isinstance(object_, typing.Dict): + key_type = typing_extensions.get_args(clean_type)[0] + value_type = typing_extensions.get_args(clean_type)[1] + + return { + key: convert_and_respect_annotation_metadata( + object_=value, + annotation=annotation, + inner_type=value_type, + direction=direction, + ) + for key, value in object_.items() + } + + # If you're iterating on a string, do not bother to coerce it to a sequence. + if not isinstance(object_, str): + if ( + typing_extensions.get_origin(clean_type) == typing.Set + or typing_extensions.get_origin(clean_type) == set + or clean_type == typing.Set + ) and isinstance(object_, typing.Set): + inner_type = typing_extensions.get_args(clean_type)[0] + return { + convert_and_respect_annotation_metadata( + object_=item, + annotation=annotation, + inner_type=inner_type, + direction=direction, + ) + for item in object_ + } + elif ( + ( + typing_extensions.get_origin(clean_type) == typing.List + or typing_extensions.get_origin(clean_type) == list + or clean_type == typing.List + ) + and isinstance(object_, typing.List) + ) or ( + ( + typing_extensions.get_origin(clean_type) == typing.Sequence + or typing_extensions.get_origin(clean_type) == collections.abc.Sequence + or clean_type == typing.Sequence + ) + and isinstance(object_, typing.Sequence) + ): + inner_type = typing_extensions.get_args(clean_type)[0] + return [ + convert_and_respect_annotation_metadata( + object_=item, + annotation=annotation, + inner_type=inner_type, + direction=direction, + ) + for item in object_ + ] + + if typing_extensions.get_origin(clean_type) == typing.Union: + # We should be able to ~relatively~ safely try to convert keys against all + # member types in the union, the edge case here is if one member aliases a field + # of the same name to a different name from another member + # Or if another member aliases a field of the same name that another member does not. + for member in typing_extensions.get_args(clean_type): + object_ = convert_and_respect_annotation_metadata( + object_=object_, + annotation=annotation, + inner_type=member, + direction=direction, + ) + return object_ + + annotated_type = _get_annotation(annotation) + if annotated_type is None: + return object_ + + # If the object is not a TypedDict, a Union, or other container (list, set, sequence, etc.) + # Then we can safely call it on the recursive conversion. + return object_ + + +def _convert_mapping( + object_: typing.Mapping[str, object], + expected_type: typing.Any, + direction: typing.Literal["read", "write"], +) -> typing.Mapping[str, object]: + converted_object: typing.Dict[str, object] = {} + try: + annotations = typing_extensions.get_type_hints(expected_type, include_extras=True) + except NameError: + # The TypedDict contains a circular reference, so + # we use the __annotations__ attribute directly. + annotations = getattr(expected_type, "__annotations__", {}) + aliases_to_field_names = _get_alias_to_field_name(annotations) + for key, value in object_.items(): + if direction == "read" and key in aliases_to_field_names: + dealiased_key = aliases_to_field_names.get(key) + if dealiased_key is not None: + type_ = annotations.get(dealiased_key) + else: + type_ = annotations.get(key) + # Note you can't get the annotation by the field name if you're in read mode, so you must check the aliases map + # + # So this is effectively saying if we're in write mode, and we don't have a type, or if we're in read mode and we don't have an alias + # then we can just pass the value through as is + if type_ is None: + converted_object[key] = value + elif direction == "read" and key not in aliases_to_field_names: + converted_object[key] = convert_and_respect_annotation_metadata( + object_=value, annotation=type_, direction=direction + ) + else: + converted_object[_alias_key(key, type_, direction, aliases_to_field_names)] = ( + convert_and_respect_annotation_metadata(object_=value, annotation=type_, direction=direction) + ) + return converted_object + + +def _get_annotation(type_: typing.Any) -> typing.Optional[typing.Any]: + maybe_annotated_type = typing_extensions.get_origin(type_) + if maybe_annotated_type is None: + return None + + if maybe_annotated_type == typing_extensions.NotRequired: + type_ = typing_extensions.get_args(type_)[0] + maybe_annotated_type = typing_extensions.get_origin(type_) + + if maybe_annotated_type == typing_extensions.Annotated: + return type_ + + return None + + +def _remove_annotations(type_: typing.Any) -> typing.Any: + maybe_annotated_type = typing_extensions.get_origin(type_) + if maybe_annotated_type is None: + return type_ + + if maybe_annotated_type == typing_extensions.NotRequired: + return _remove_annotations(typing_extensions.get_args(type_)[0]) + + if maybe_annotated_type == typing_extensions.Annotated: + return _remove_annotations(typing_extensions.get_args(type_)[0]) + + return type_ + + +def get_alias_to_field_mapping(type_: typing.Any) -> typing.Dict[str, str]: + annotations = typing_extensions.get_type_hints(type_, include_extras=True) + return _get_alias_to_field_name(annotations) + + +def get_field_to_alias_mapping(type_: typing.Any) -> typing.Dict[str, str]: + annotations = typing_extensions.get_type_hints(type_, include_extras=True) + return _get_field_to_alias_name(annotations) + + +def _get_alias_to_field_name( + field_to_hint: typing.Dict[str, typing.Any], +) -> typing.Dict[str, str]: + aliases = {} + for field, hint in field_to_hint.items(): + maybe_alias = _get_alias_from_type(hint) + if maybe_alias is not None: + aliases[maybe_alias] = field + return aliases + + +def _get_field_to_alias_name( + field_to_hint: typing.Dict[str, typing.Any], +) -> typing.Dict[str, str]: + aliases = {} + for field, hint in field_to_hint.items(): + maybe_alias = _get_alias_from_type(hint) + if maybe_alias is not None: + aliases[field] = maybe_alias + return aliases + + +def _get_alias_from_type(type_: typing.Any) -> typing.Optional[str]: + maybe_annotated_type = _get_annotation(type_) + + if maybe_annotated_type is not None: + # The actual annotations are 1 onward, the first is the annotated type + annotations = typing_extensions.get_args(maybe_annotated_type)[1:] + + for annotation in annotations: + if isinstance(annotation, FieldMetadata) and annotation.alias is not None: + return annotation.alias + return None + + +def _alias_key( + key: str, + type_: typing.Any, + direction: typing.Literal["read", "write"], + aliases_to_field_names: typing.Dict[str, str], +) -> str: + if direction == "read": + return aliases_to_field_names.get(key, key) + return _get_alias_from_type(type_=type_) or key diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/nested/__init__.py b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/nested/__init__.py new file mode 100644 index 000000000000..87547c8ef9a8 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/nested/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from . import api +_dynamic_imports: typing.Dict[str, str] = {"api": ".api"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["api"] diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/nested/api/__init__.py b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/nested/api/__init__.py new file mode 100644 index 000000000000..5cde0202dcf3 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/nested/api/__init__.py @@ -0,0 +1,4 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/nested/api/client.py b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/nested/api/client.py new file mode 100644 index 000000000000..8bc9507829f2 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/nested/api/client.py @@ -0,0 +1,93 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from .raw_client import AsyncRawApiClient, RawApiClient + + +class ApiClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawApiClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawApiClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawApiClient + """ + return self._raw_client + + def get_something(self, *, request_options: typing.Optional[RequestOptions] = None) -> None: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + from seed import SeedInferredAuthImplicit + + client = SeedInferredAuthImplicit( + base_url="https://yourhost.com/path/to/api", + ) + client.nested.api.get_something() + """ + _response = self._raw_client.get_something(request_options=request_options) + return _response.data + + +class AsyncApiClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawApiClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawApiClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawApiClient + """ + return self._raw_client + + async def get_something(self, *, request_options: typing.Optional[RequestOptions] = None) -> None: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + import asyncio + + from seed import AsyncSeedInferredAuthImplicit + + client = AsyncSeedInferredAuthImplicit( + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.nested.api.get_something() + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_something(request_options=request_options) + return _response.data diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/nested/api/raw_client.py b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/nested/api/raw_client.py new file mode 100644 index 000000000000..d7f7becabd87 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/nested/api/raw_client.py @@ -0,0 +1,69 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +from ...core.api_error import ApiError +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.http_response import AsyncHttpResponse, HttpResponse +from ...core.request_options import RequestOptions + + +class RawApiClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def get_something(self, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[None]: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[None] + """ + _response = self._client_wrapper.httpx_client.request( + "nested/get-something", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return HttpResponse(response=_response, data=None) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + +class AsyncRawApiClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def get_something( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[None]: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[None] + """ + _response = await self._client_wrapper.httpx_client.request( + "nested/get-something", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return AsyncHttpResponse(response=_response, data=None) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/nested/client.py b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/nested/client.py new file mode 100644 index 000000000000..b6b69853720e --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/nested/client.py @@ -0,0 +1,63 @@ +# This file was auto-generated by Fern from our API Definition. + +from __future__ import annotations + +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from .raw_client import AsyncRawNestedClient, RawNestedClient + +if typing.TYPE_CHECKING: + from .api.client import ApiClient, AsyncApiClient + + +class NestedClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawNestedClient(client_wrapper=client_wrapper) + self._client_wrapper = client_wrapper + self._api: typing.Optional[ApiClient] = None + + @property + def with_raw_response(self) -> RawNestedClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawNestedClient + """ + return self._raw_client + + @property + def api(self): + if self._api is None: + from .api.client import ApiClient # noqa: E402 + + self._api = ApiClient(client_wrapper=self._client_wrapper) + return self._api + + +class AsyncNestedClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawNestedClient(client_wrapper=client_wrapper) + self._client_wrapper = client_wrapper + self._api: typing.Optional[AsyncApiClient] = None + + @property + def with_raw_response(self) -> AsyncRawNestedClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawNestedClient + """ + return self._raw_client + + @property + def api(self): + if self._api is None: + from .api.client import AsyncApiClient # noqa: E402 + + self._api = AsyncApiClient(client_wrapper=self._client_wrapper) + return self._api diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/nested/raw_client.py b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/nested/raw_client.py new file mode 100644 index 000000000000..27d31b97385d --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/nested/raw_client.py @@ -0,0 +1,13 @@ +# This file was auto-generated by Fern from our API Definition. + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper + + +class RawNestedClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + +class AsyncRawNestedClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/nested_no_auth/__init__.py b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/nested_no_auth/__init__.py new file mode 100644 index 000000000000..87547c8ef9a8 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/nested_no_auth/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from . import api +_dynamic_imports: typing.Dict[str, str] = {"api": ".api"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["api"] diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/nested_no_auth/api/__init__.py b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/nested_no_auth/api/__init__.py new file mode 100644 index 000000000000..5cde0202dcf3 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/nested_no_auth/api/__init__.py @@ -0,0 +1,4 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/nested_no_auth/api/client.py b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/nested_no_auth/api/client.py new file mode 100644 index 000000000000..259807d8a015 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/nested_no_auth/api/client.py @@ -0,0 +1,93 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from .raw_client import AsyncRawApiClient, RawApiClient + + +class ApiClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawApiClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawApiClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawApiClient + """ + return self._raw_client + + def get_something(self, *, request_options: typing.Optional[RequestOptions] = None) -> None: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + from seed import SeedInferredAuthImplicit + + client = SeedInferredAuthImplicit( + base_url="https://yourhost.com/path/to/api", + ) + client.nested_no_auth.api.get_something() + """ + _response = self._raw_client.get_something(request_options=request_options) + return _response.data + + +class AsyncApiClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawApiClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawApiClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawApiClient + """ + return self._raw_client + + async def get_something(self, *, request_options: typing.Optional[RequestOptions] = None) -> None: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + import asyncio + + from seed import AsyncSeedInferredAuthImplicit + + client = AsyncSeedInferredAuthImplicit( + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.nested_no_auth.api.get_something() + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_something(request_options=request_options) + return _response.data diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/nested_no_auth/api/raw_client.py b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/nested_no_auth/api/raw_client.py new file mode 100644 index 000000000000..4fbe653ac421 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/nested_no_auth/api/raw_client.py @@ -0,0 +1,69 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +from ...core.api_error import ApiError +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.http_response import AsyncHttpResponse, HttpResponse +from ...core.request_options import RequestOptions + + +class RawApiClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def get_something(self, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[None]: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[None] + """ + _response = self._client_wrapper.httpx_client.request( + "nested-no-auth/get-something", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return HttpResponse(response=_response, data=None) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + +class AsyncRawApiClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def get_something( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[None]: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[None] + """ + _response = await self._client_wrapper.httpx_client.request( + "nested-no-auth/get-something", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return AsyncHttpResponse(response=_response, data=None) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/nested_no_auth/client.py b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/nested_no_auth/client.py new file mode 100644 index 000000000000..d72921720357 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/nested_no_auth/client.py @@ -0,0 +1,63 @@ +# This file was auto-generated by Fern from our API Definition. + +from __future__ import annotations + +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from .raw_client import AsyncRawNestedNoAuthClient, RawNestedNoAuthClient + +if typing.TYPE_CHECKING: + from .api.client import ApiClient, AsyncApiClient + + +class NestedNoAuthClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawNestedNoAuthClient(client_wrapper=client_wrapper) + self._client_wrapper = client_wrapper + self._api: typing.Optional[ApiClient] = None + + @property + def with_raw_response(self) -> RawNestedNoAuthClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawNestedNoAuthClient + """ + return self._raw_client + + @property + def api(self): + if self._api is None: + from .api.client import ApiClient # noqa: E402 + + self._api = ApiClient(client_wrapper=self._client_wrapper) + return self._api + + +class AsyncNestedNoAuthClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawNestedNoAuthClient(client_wrapper=client_wrapper) + self._client_wrapper = client_wrapper + self._api: typing.Optional[AsyncApiClient] = None + + @property + def with_raw_response(self) -> AsyncRawNestedNoAuthClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawNestedNoAuthClient + """ + return self._raw_client + + @property + def api(self): + if self._api is None: + from .api.client import AsyncApiClient # noqa: E402 + + self._api = AsyncApiClient(client_wrapper=self._client_wrapper) + return self._api diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/nested_no_auth/raw_client.py b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/nested_no_auth/raw_client.py new file mode 100644 index 000000000000..0c7a80a10e7a --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/nested_no_auth/raw_client.py @@ -0,0 +1,13 @@ +# This file was auto-generated by Fern from our API Definition. + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper + + +class RawNestedNoAuthClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + +class AsyncRawNestedNoAuthClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/py.typed b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/py.typed new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/simple/__init__.py b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/simple/__init__.py new file mode 100644 index 000000000000..5cde0202dcf3 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/simple/__init__.py @@ -0,0 +1,4 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/simple/client.py b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/simple/client.py new file mode 100644 index 000000000000..d3cd5f2c50f7 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/simple/client.py @@ -0,0 +1,93 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.request_options import RequestOptions +from .raw_client import AsyncRawSimpleClient, RawSimpleClient + + +class SimpleClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawSimpleClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawSimpleClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawSimpleClient + """ + return self._raw_client + + def get_something(self, *, request_options: typing.Optional[RequestOptions] = None) -> None: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + from seed import SeedInferredAuthImplicit + + client = SeedInferredAuthImplicit( + base_url="https://yourhost.com/path/to/api", + ) + client.simple.get_something() + """ + _response = self._raw_client.get_something(request_options=request_options) + return _response.data + + +class AsyncSimpleClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawSimpleClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawSimpleClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawSimpleClient + """ + return self._raw_client + + async def get_something(self, *, request_options: typing.Optional[RequestOptions] = None) -> None: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + import asyncio + + from seed import AsyncSeedInferredAuthImplicit + + client = AsyncSeedInferredAuthImplicit( + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.simple.get_something() + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_something(request_options=request_options) + return _response.data diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/simple/raw_client.py b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/simple/raw_client.py new file mode 100644 index 000000000000..aa82d3dd5984 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/simple/raw_client.py @@ -0,0 +1,69 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +from ..core.api_error import ApiError +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.http_response import AsyncHttpResponse, HttpResponse +from ..core.request_options import RequestOptions + + +class RawSimpleClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def get_something(self, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[None]: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[None] + """ + _response = self._client_wrapper.httpx_client.request( + "get-something", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return HttpResponse(response=_response, data=None) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + +class AsyncRawSimpleClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def get_something( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[None]: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[None] + """ + _response = await self._client_wrapper.httpx_client.request( + "get-something", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return AsyncHttpResponse(response=_response, data=None) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/seed/python-sdk/inferred-auth-implicit-reference/src/seed/version.py b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/version.py new file mode 100644 index 000000000000..261f9287c21b --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/src/seed/version.py @@ -0,0 +1,3 @@ +from importlib import metadata + +__version__ = metadata.version("fern_inferred-auth-implicit-reference") diff --git a/seed/python-sdk/inferred-auth-implicit-reference/tests/custom/test_client.py b/seed/python-sdk/inferred-auth-implicit-reference/tests/custom/test_client.py new file mode 100644 index 000000000000..ab04ce6393ef --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/tests/custom/test_client.py @@ -0,0 +1,7 @@ +import pytest + + +# Get started with writing tests with pytest at https://docs.pytest.org +@pytest.mark.skip(reason="Unimplemented") +def test_client() -> None: + assert True diff --git a/seed/python-sdk/inferred-auth-implicit-reference/tests/utils/__init__.py b/seed/python-sdk/inferred-auth-implicit-reference/tests/utils/__init__.py new file mode 100644 index 000000000000..f3ea2659bb1c --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/tests/utils/__init__.py @@ -0,0 +1,2 @@ +# This file was auto-generated by Fern from our API Definition. + diff --git a/seed/python-sdk/inferred-auth-implicit-reference/tests/utils/assets/models/__init__.py b/seed/python-sdk/inferred-auth-implicit-reference/tests/utils/assets/models/__init__.py new file mode 100644 index 000000000000..2cf01263529d --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/tests/utils/assets/models/__init__.py @@ -0,0 +1,21 @@ +# This file was auto-generated by Fern from our API Definition. + +# This file was auto-generated by Fern from our API Definition. + +from .circle import CircleParams +from .object_with_defaults import ObjectWithDefaultsParams +from .object_with_optional_field import ObjectWithOptionalFieldParams +from .shape import Shape_CircleParams, Shape_SquareParams, ShapeParams +from .square import SquareParams +from .undiscriminated_shape import UndiscriminatedShapeParams + +__all__ = [ + "CircleParams", + "ObjectWithDefaultsParams", + "ObjectWithOptionalFieldParams", + "ShapeParams", + "Shape_CircleParams", + "Shape_SquareParams", + "SquareParams", + "UndiscriminatedShapeParams", +] diff --git a/seed/python-sdk/inferred-auth-implicit-reference/tests/utils/assets/models/circle.py b/seed/python-sdk/inferred-auth-implicit-reference/tests/utils/assets/models/circle.py new file mode 100644 index 000000000000..74ecf38c308b --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/tests/utils/assets/models/circle.py @@ -0,0 +1,11 @@ +# This file was auto-generated by Fern from our API Definition. + +# This file was auto-generated by Fern from our API Definition. + +import typing_extensions + +from seed.core.serialization import FieldMetadata + + +class CircleParams(typing_extensions.TypedDict): + radius_measurement: typing_extensions.Annotated[float, FieldMetadata(alias="radiusMeasurement")] diff --git a/seed/python-sdk/inferred-auth-implicit-reference/tests/utils/assets/models/color.py b/seed/python-sdk/inferred-auth-implicit-reference/tests/utils/assets/models/color.py new file mode 100644 index 000000000000..2aa2c4c52f0c --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/tests/utils/assets/models/color.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +# This file was auto-generated by Fern from our API Definition. + +import typing + +Color = typing.Union[typing.Literal["red", "blue"], typing.Any] diff --git a/seed/python-sdk/inferred-auth-implicit-reference/tests/utils/assets/models/object_with_defaults.py b/seed/python-sdk/inferred-auth-implicit-reference/tests/utils/assets/models/object_with_defaults.py new file mode 100644 index 000000000000..a977b1d2aa1c --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/tests/utils/assets/models/object_with_defaults.py @@ -0,0 +1,15 @@ +# This file was auto-generated by Fern from our API Definition. + +# This file was auto-generated by Fern from our API Definition. + +import typing_extensions + + +class ObjectWithDefaultsParams(typing_extensions.TypedDict): + """ + Defines properties with default values and validation rules. + """ + + decimal: typing_extensions.NotRequired[float] + string: typing_extensions.NotRequired[str] + required_string: str diff --git a/seed/python-sdk/inferred-auth-implicit-reference/tests/utils/assets/models/object_with_optional_field.py b/seed/python-sdk/inferred-auth-implicit-reference/tests/utils/assets/models/object_with_optional_field.py new file mode 100644 index 000000000000..6b5608bc05b6 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/tests/utils/assets/models/object_with_optional_field.py @@ -0,0 +1,35 @@ +# This file was auto-generated by Fern from our API Definition. + +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing +import uuid + +import typing_extensions +from .color import Color +from .shape import ShapeParams +from .undiscriminated_shape import UndiscriminatedShapeParams + +from seed.core.serialization import FieldMetadata + + +class ObjectWithOptionalFieldParams(typing_extensions.TypedDict): + literal: typing.Literal["lit_one"] + string: typing_extensions.NotRequired[str] + integer: typing_extensions.NotRequired[int] + long_: typing_extensions.NotRequired[typing_extensions.Annotated[int, FieldMetadata(alias="long")]] + double: typing_extensions.NotRequired[float] + bool_: typing_extensions.NotRequired[typing_extensions.Annotated[bool, FieldMetadata(alias="bool")]] + datetime: typing_extensions.NotRequired[dt.datetime] + date: typing_extensions.NotRequired[dt.date] + uuid_: typing_extensions.NotRequired[typing_extensions.Annotated[uuid.UUID, FieldMetadata(alias="uuid")]] + base_64: typing_extensions.NotRequired[typing_extensions.Annotated[str, FieldMetadata(alias="base64")]] + list_: typing_extensions.NotRequired[typing_extensions.Annotated[typing.Sequence[str], FieldMetadata(alias="list")]] + set_: typing_extensions.NotRequired[typing_extensions.Annotated[typing.Set[str], FieldMetadata(alias="set")]] + map_: typing_extensions.NotRequired[typing_extensions.Annotated[typing.Dict[int, str], FieldMetadata(alias="map")]] + enum: typing_extensions.NotRequired[Color] + union: typing_extensions.NotRequired[ShapeParams] + second_union: typing_extensions.NotRequired[ShapeParams] + undiscriminated_union: typing_extensions.NotRequired[UndiscriminatedShapeParams] + any: typing.Optional[typing.Any] diff --git a/seed/python-sdk/inferred-auth-implicit-reference/tests/utils/assets/models/shape.py b/seed/python-sdk/inferred-auth-implicit-reference/tests/utils/assets/models/shape.py new file mode 100644 index 000000000000..7e70010a251f --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/tests/utils/assets/models/shape.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +# This file was auto-generated by Fern from our API Definition. + +from __future__ import annotations + +import typing + +import typing_extensions + +from seed.core.serialization import FieldMetadata + + +class Base(typing_extensions.TypedDict): + id: str + + +class Shape_CircleParams(Base): + shape_type: typing_extensions.Annotated[typing.Literal["circle"], FieldMetadata(alias="shapeType")] + radius_measurement: typing_extensions.Annotated[float, FieldMetadata(alias="radiusMeasurement")] + + +class Shape_SquareParams(Base): + shape_type: typing_extensions.Annotated[typing.Literal["square"], FieldMetadata(alias="shapeType")] + length_measurement: typing_extensions.Annotated[float, FieldMetadata(alias="lengthMeasurement")] + + +ShapeParams = typing.Union[Shape_CircleParams, Shape_SquareParams] diff --git a/seed/python-sdk/inferred-auth-implicit-reference/tests/utils/assets/models/square.py b/seed/python-sdk/inferred-auth-implicit-reference/tests/utils/assets/models/square.py new file mode 100644 index 000000000000..71c7d25fd4ad --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/tests/utils/assets/models/square.py @@ -0,0 +1,11 @@ +# This file was auto-generated by Fern from our API Definition. + +# This file was auto-generated by Fern from our API Definition. + +import typing_extensions + +from seed.core.serialization import FieldMetadata + + +class SquareParams(typing_extensions.TypedDict): + length_measurement: typing_extensions.Annotated[float, FieldMetadata(alias="lengthMeasurement")] diff --git a/seed/python-sdk/inferred-auth-implicit-reference/tests/utils/assets/models/undiscriminated_shape.py b/seed/python-sdk/inferred-auth-implicit-reference/tests/utils/assets/models/undiscriminated_shape.py new file mode 100644 index 000000000000..99f12b300d1d --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/tests/utils/assets/models/undiscriminated_shape.py @@ -0,0 +1,10 @@ +# This file was auto-generated by Fern from our API Definition. + +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .circle import CircleParams +from .square import SquareParams + +UndiscriminatedShapeParams = typing.Union[CircleParams, SquareParams] diff --git a/seed/python-sdk/inferred-auth-implicit-reference/tests/utils/test_http_client.py b/seed/python-sdk/inferred-auth-implicit-reference/tests/utils/test_http_client.py new file mode 100644 index 000000000000..cf967de21db4 --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/tests/utils/test_http_client.py @@ -0,0 +1,253 @@ +# This file was auto-generated by Fern from our API Definition. + +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict +from seed.core.request_options import RequestOptions + + +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + +def get_request_options() -> RequestOptions: + return {"additional_body_parameters": {"see you": "later"}} + + +def get_request_options_with_none() -> RequestOptions: + return {"additional_body_parameters": {"see you": "later", "optional": None}} + + +def test_get_json_request_body() -> None: + json_body, data_body = get_request_body(json={"hello": "world"}, data=None, request_options=None, omit=None) + assert json_body == {"hello": "world"} + assert data_body is None + + json_body_extras, data_body_extras = get_request_body( + json={"goodbye": "world"}, data=None, request_options=get_request_options(), omit=None + ) + + assert json_body_extras == {"goodbye": "world", "see you": "later"} + assert data_body_extras is None + + +def test_get_files_request_body() -> None: + json_body, data_body = get_request_body(json=None, data={"hello": "world"}, request_options=None, omit=None) + assert data_body == {"hello": "world"} + assert json_body is None + + json_body_extras, data_body_extras = get_request_body( + json=None, data={"goodbye": "world"}, request_options=get_request_options(), omit=None + ) + + assert data_body_extras == {"goodbye": "world", "see you": "later"} + assert json_body_extras is None + + +def test_get_none_request_body() -> None: + json_body, data_body = get_request_body(json=None, data=None, request_options=None, omit=None) + assert data_body is None + assert json_body is None + + json_body_extras, data_body_extras = get_request_body( + json=None, data=None, request_options=get_request_options(), omit=None + ) + + assert json_body_extras == {"see you": "later"} + assert data_body_extras is None + + +def test_get_empty_json_request_body() -> None: + unrelated_request_options: RequestOptions = {"max_retries": 3} + json_body, data_body = get_request_body(json=None, data=None, request_options=unrelated_request_options, omit=None) + assert json_body is None + assert data_body is None + + json_body_extras, data_body_extras = get_request_body( + json={}, data=None, request_options=unrelated_request_options, omit=None + ) + + assert json_body_extras is None + assert data_body_extras is None + + +def test_json_body_preserves_none_values() -> None: + """Test that JSON bodies preserve None values (they become JSON null).""" + json_body, data_body = get_request_body( + json={"hello": "world", "optional": None}, data=None, request_options=None, omit=None + ) + # JSON bodies should preserve None values + assert json_body == {"hello": "world", "optional": None} + assert data_body is None + + +def test_data_body_preserves_none_values_without_multipart() -> None: + """Test that data bodies preserve None values when not using multipart. + + The filtering of None values happens in HttpClient.request/stream methods, + not in get_request_body. This test verifies get_request_body doesn't filter None. + """ + json_body, data_body = get_request_body( + json=None, data={"hello": "world", "optional": None}, request_options=None, omit=None + ) + # get_request_body should preserve None values in data body + # The filtering happens later in HttpClient.request when multipart is detected + assert data_body == {"hello": "world", "optional": None} + assert json_body is None + + +def test_remove_none_from_dict_filters_none_values() -> None: + """Test that remove_none_from_dict correctly filters out None values.""" + original = {"hello": "world", "optional": None, "another": "value", "also_none": None} + filtered = remove_none_from_dict(original) + assert filtered == {"hello": "world", "another": "value"} + # Original should not be modified + assert original == {"hello": "world", "optional": None, "another": "value", "also_none": None} + + +def test_remove_none_from_dict_empty_dict() -> None: + """Test that remove_none_from_dict handles empty dict.""" + assert remove_none_from_dict({}) == {} + + +def test_remove_none_from_dict_all_none() -> None: + """Test that remove_none_from_dict handles dict with all None values.""" + assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/inferred-auth-implicit-reference/tests/utils/test_query_encoding.py b/seed/python-sdk/inferred-auth-implicit-reference/tests/utils/test_query_encoding.py new file mode 100644 index 000000000000..ef5fd7094f9b --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/tests/utils/test_query_encoding.py @@ -0,0 +1,36 @@ +# This file was auto-generated by Fern from our API Definition. + +from seed.core.query_encoder import encode_query + + +def test_query_encoding_deep_objects() -> None: + assert encode_query({"hello world": "hello world"}) == [("hello world", "hello world")] + assert encode_query({"hello_world": {"hello": "world"}}) == [("hello_world[hello]", "world")] + assert encode_query({"hello_world": {"hello": {"world": "today"}, "test": "this"}, "hi": "there"}) == [ + ("hello_world[hello][world]", "today"), + ("hello_world[test]", "this"), + ("hi", "there"), + ] + + +def test_query_encoding_deep_object_arrays() -> None: + assert encode_query({"objects": [{"key": "hello", "value": "world"}, {"key": "foo", "value": "bar"}]}) == [ + ("objects[key]", "hello"), + ("objects[value]", "world"), + ("objects[key]", "foo"), + ("objects[value]", "bar"), + ] + assert encode_query( + {"users": [{"name": "string", "tags": ["string"]}, {"name": "string2", "tags": ["string2", "string3"]}]} + ) == [ + ("users[name]", "string"), + ("users[tags]", "string"), + ("users[name]", "string2"), + ("users[tags]", "string2"), + ("users[tags]", "string3"), + ] + + +def test_encode_query_with_none() -> None: + encoded = encode_query(None) + assert encoded is None diff --git a/seed/python-sdk/inferred-auth-implicit-reference/tests/utils/test_serialization.py b/seed/python-sdk/inferred-auth-implicit-reference/tests/utils/test_serialization.py new file mode 100644 index 000000000000..b298db89c4bd --- /dev/null +++ b/seed/python-sdk/inferred-auth-implicit-reference/tests/utils/test_serialization.py @@ -0,0 +1,72 @@ +# This file was auto-generated by Fern from our API Definition. + +from typing import Any, List + +from .assets.models import ObjectWithOptionalFieldParams, ShapeParams + +from seed.core.serialization import convert_and_respect_annotation_metadata + +UNION_TEST: ShapeParams = {"radius_measurement": 1.0, "shape_type": "circle", "id": "1"} +UNION_TEST_CONVERTED = {"shapeType": "circle", "radiusMeasurement": 1.0, "id": "1"} + + +def test_convert_and_respect_annotation_metadata() -> None: + data: ObjectWithOptionalFieldParams = { + "string": "string", + "long_": 12345, + "bool_": True, + "literal": "lit_one", + "any": "any", + } + converted = convert_and_respect_annotation_metadata( + object_=data, annotation=ObjectWithOptionalFieldParams, direction="write" + ) + assert converted == {"string": "string", "long": 12345, "bool": True, "literal": "lit_one", "any": "any"} + + +def test_convert_and_respect_annotation_metadata_in_list() -> None: + data: List[ObjectWithOptionalFieldParams] = [ + {"string": "string", "long_": 12345, "bool_": True, "literal": "lit_one", "any": "any"}, + {"string": "another string", "long_": 67890, "list_": [], "literal": "lit_one", "any": "any"}, + ] + converted = convert_and_respect_annotation_metadata( + object_=data, annotation=List[ObjectWithOptionalFieldParams], direction="write" + ) + + assert converted == [ + {"string": "string", "long": 12345, "bool": True, "literal": "lit_one", "any": "any"}, + {"string": "another string", "long": 67890, "list": [], "literal": "lit_one", "any": "any"}, + ] + + +def test_convert_and_respect_annotation_metadata_in_nested_object() -> None: + data: ObjectWithOptionalFieldParams = { + "string": "string", + "long_": 12345, + "union": UNION_TEST, + "literal": "lit_one", + "any": "any", + } + converted = convert_and_respect_annotation_metadata( + object_=data, annotation=ObjectWithOptionalFieldParams, direction="write" + ) + + assert converted == { + "string": "string", + "long": 12345, + "union": UNION_TEST_CONVERTED, + "literal": "lit_one", + "any": "any", + } + + +def test_convert_and_respect_annotation_metadata_in_union() -> None: + converted = convert_and_respect_annotation_metadata(object_=UNION_TEST, annotation=ShapeParams, direction="write") + + assert converted == UNION_TEST_CONVERTED + + +def test_convert_and_respect_annotation_metadata_with_empty_object() -> None: + data: Any = {} + converted = convert_and_respect_annotation_metadata(object_=data, annotation=ShapeParams, direction="write") + assert converted == data diff --git a/seed/python-sdk/inferred-auth-implicit/src/seed/core/http_client.py b/seed/python-sdk/inferred-auth-implicit/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/inferred-auth-implicit/src/seed/core/http_client.py +++ b/seed/python-sdk/inferred-auth-implicit/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/inferred-auth-implicit/tests/utils/test_http_client.py b/seed/python-sdk/inferred-auth-implicit/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/inferred-auth-implicit/tests/utils/test_http_client.py +++ b/seed/python-sdk/inferred-auth-implicit/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/license/src/seed/core/http_client.py b/seed/python-sdk/license/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/license/src/seed/core/http_client.py +++ b/seed/python-sdk/license/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/license/tests/utils/test_http_client.py b/seed/python-sdk/license/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/license/tests/utils/test_http_client.py +++ b/seed/python-sdk/license/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/literal/no-custom-config/src/seed/core/http_client.py b/seed/python-sdk/literal/no-custom-config/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/literal/no-custom-config/src/seed/core/http_client.py +++ b/seed/python-sdk/literal/no-custom-config/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/literal/no-custom-config/tests/utils/test_http_client.py b/seed/python-sdk/literal/no-custom-config/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/literal/no-custom-config/tests/utils/test_http_client.py +++ b/seed/python-sdk/literal/no-custom-config/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/literal/use_typeddict_requests/src/seed/core/http_client.py b/seed/python-sdk/literal/use_typeddict_requests/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/literal/use_typeddict_requests/src/seed/core/http_client.py +++ b/seed/python-sdk/literal/use_typeddict_requests/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/literal/use_typeddict_requests/tests/utils/test_http_client.py b/seed/python-sdk/literal/use_typeddict_requests/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/literal/use_typeddict_requests/tests/utils/test_http_client.py +++ b/seed/python-sdk/literal/use_typeddict_requests/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/literals-unions/src/seed/core/http_client.py b/seed/python-sdk/literals-unions/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/literals-unions/src/seed/core/http_client.py +++ b/seed/python-sdk/literals-unions/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/literals-unions/tests/utils/test_http_client.py b/seed/python-sdk/literals-unions/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/literals-unions/tests/utils/test_http_client.py +++ b/seed/python-sdk/literals-unions/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/mixed-case/src/seed/core/http_client.py b/seed/python-sdk/mixed-case/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/mixed-case/src/seed/core/http_client.py +++ b/seed/python-sdk/mixed-case/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/mixed-case/tests/utils/test_http_client.py b/seed/python-sdk/mixed-case/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/mixed-case/tests/utils/test_http_client.py +++ b/seed/python-sdk/mixed-case/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/mixed-file-directory/exclude_types_from_init_exports/src/seed/core/http_client.py b/seed/python-sdk/mixed-file-directory/exclude_types_from_init_exports/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/mixed-file-directory/exclude_types_from_init_exports/src/seed/core/http_client.py +++ b/seed/python-sdk/mixed-file-directory/exclude_types_from_init_exports/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/mixed-file-directory/exclude_types_from_init_exports/tests/utils/test_http_client.py b/seed/python-sdk/mixed-file-directory/exclude_types_from_init_exports/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/mixed-file-directory/exclude_types_from_init_exports/tests/utils/test_http_client.py +++ b/seed/python-sdk/mixed-file-directory/exclude_types_from_init_exports/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/mixed-file-directory/no-custom-config/src/seed/core/http_client.py b/seed/python-sdk/mixed-file-directory/no-custom-config/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/mixed-file-directory/no-custom-config/src/seed/core/http_client.py +++ b/seed/python-sdk/mixed-file-directory/no-custom-config/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/mixed-file-directory/no-custom-config/tests/utils/test_http_client.py b/seed/python-sdk/mixed-file-directory/no-custom-config/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/mixed-file-directory/no-custom-config/tests/utils/test_http_client.py +++ b/seed/python-sdk/mixed-file-directory/no-custom-config/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/multi-line-docs/src/seed/core/http_client.py b/seed/python-sdk/multi-line-docs/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/multi-line-docs/src/seed/core/http_client.py +++ b/seed/python-sdk/multi-line-docs/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/multi-line-docs/tests/utils/test_http_client.py b/seed/python-sdk/multi-line-docs/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/multi-line-docs/tests/utils/test_http_client.py +++ b/seed/python-sdk/multi-line-docs/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/multi-url-environment-no-default/src/seed/core/http_client.py b/seed/python-sdk/multi-url-environment-no-default/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/multi-url-environment-no-default/src/seed/core/http_client.py +++ b/seed/python-sdk/multi-url-environment-no-default/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/multi-url-environment-no-default/tests/utils/test_http_client.py b/seed/python-sdk/multi-url-environment-no-default/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/multi-url-environment-no-default/tests/utils/test_http_client.py +++ b/seed/python-sdk/multi-url-environment-no-default/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/multi-url-environment/src/seed/core/http_client.py b/seed/python-sdk/multi-url-environment/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/multi-url-environment/src/seed/core/http_client.py +++ b/seed/python-sdk/multi-url-environment/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/multi-url-environment/tests/utils/test_http_client.py b/seed/python-sdk/multi-url-environment/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/multi-url-environment/tests/utils/test_http_client.py +++ b/seed/python-sdk/multi-url-environment/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/multiple-request-bodies/src/seed/core/http_client.py b/seed/python-sdk/multiple-request-bodies/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/multiple-request-bodies/src/seed/core/http_client.py +++ b/seed/python-sdk/multiple-request-bodies/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/multiple-request-bodies/tests/utils/test_http_client.py b/seed/python-sdk/multiple-request-bodies/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/multiple-request-bodies/tests/utils/test_http_client.py +++ b/seed/python-sdk/multiple-request-bodies/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/no-environment/src/seed/core/http_client.py b/seed/python-sdk/no-environment/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/no-environment/src/seed/core/http_client.py +++ b/seed/python-sdk/no-environment/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/no-environment/tests/utils/test_http_client.py b/seed/python-sdk/no-environment/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/no-environment/tests/utils/test_http_client.py +++ b/seed/python-sdk/no-environment/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/no-retries/src/seed/core/http_client.py b/seed/python-sdk/no-retries/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/no-retries/src/seed/core/http_client.py +++ b/seed/python-sdk/no-retries/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/no-retries/tests/utils/test_http_client.py b/seed/python-sdk/no-retries/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/no-retries/tests/utils/test_http_client.py +++ b/seed/python-sdk/no-retries/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/nullable-allof-extends/src/seed/core/http_client.py b/seed/python-sdk/nullable-allof-extends/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/nullable-allof-extends/src/seed/core/http_client.py +++ b/seed/python-sdk/nullable-allof-extends/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/nullable-allof-extends/tests/utils/test_http_client.py b/seed/python-sdk/nullable-allof-extends/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/nullable-allof-extends/tests/utils/test_http_client.py +++ b/seed/python-sdk/nullable-allof-extends/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/nullable-optional/src/seed/core/http_client.py b/seed/python-sdk/nullable-optional/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/nullable-optional/src/seed/core/http_client.py +++ b/seed/python-sdk/nullable-optional/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/nullable-optional/tests/utils/test_http_client.py b/seed/python-sdk/nullable-optional/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/nullable-optional/tests/utils/test_http_client.py +++ b/seed/python-sdk/nullable-optional/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/nullable-request-body/src/seed/core/http_client.py b/seed/python-sdk/nullable-request-body/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/nullable-request-body/src/seed/core/http_client.py +++ b/seed/python-sdk/nullable-request-body/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/nullable-request-body/tests/utils/test_http_client.py b/seed/python-sdk/nullable-request-body/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/nullable-request-body/tests/utils/test_http_client.py +++ b/seed/python-sdk/nullable-request-body/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/nullable/no-custom-config/src/seed/core/http_client.py b/seed/python-sdk/nullable/no-custom-config/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/nullable/no-custom-config/src/seed/core/http_client.py +++ b/seed/python-sdk/nullable/no-custom-config/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/nullable/no-custom-config/tests/utils/test_http_client.py b/seed/python-sdk/nullable/no-custom-config/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/nullable/no-custom-config/tests/utils/test_http_client.py +++ b/seed/python-sdk/nullable/no-custom-config/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/nullable/use-typeddict-requests/src/seed/core/http_client.py b/seed/python-sdk/nullable/use-typeddict-requests/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/nullable/use-typeddict-requests/src/seed/core/http_client.py +++ b/seed/python-sdk/nullable/use-typeddict-requests/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/nullable/use-typeddict-requests/tests/utils/test_http_client.py b/seed/python-sdk/nullable/use-typeddict-requests/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/nullable/use-typeddict-requests/tests/utils/test_http_client.py +++ b/seed/python-sdk/nullable/use-typeddict-requests/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/oauth-client-credentials-custom/src/seed/core/http_client.py b/seed/python-sdk/oauth-client-credentials-custom/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/oauth-client-credentials-custom/src/seed/core/http_client.py +++ b/seed/python-sdk/oauth-client-credentials-custom/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/oauth-client-credentials-custom/tests/utils/test_http_client.py b/seed/python-sdk/oauth-client-credentials-custom/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/oauth-client-credentials-custom/tests/utils/test_http_client.py +++ b/seed/python-sdk/oauth-client-credentials-custom/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/oauth-client-credentials-default/src/seed/core/http_client.py b/seed/python-sdk/oauth-client-credentials-default/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/oauth-client-credentials-default/src/seed/core/http_client.py +++ b/seed/python-sdk/oauth-client-credentials-default/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/oauth-client-credentials-default/tests/utils/test_http_client.py b/seed/python-sdk/oauth-client-credentials-default/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/oauth-client-credentials-default/tests/utils/test_http_client.py +++ b/seed/python-sdk/oauth-client-credentials-default/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/oauth-client-credentials-environment-variables/src/seed/core/http_client.py b/seed/python-sdk/oauth-client-credentials-environment-variables/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/oauth-client-credentials-environment-variables/src/seed/core/http_client.py +++ b/seed/python-sdk/oauth-client-credentials-environment-variables/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/oauth-client-credentials-environment-variables/tests/utils/test_http_client.py b/seed/python-sdk/oauth-client-credentials-environment-variables/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/oauth-client-credentials-environment-variables/tests/utils/test_http_client.py +++ b/seed/python-sdk/oauth-client-credentials-environment-variables/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/src/seed/core/http_client.py b/seed/python-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/src/seed/core/http_client.py +++ b/seed/python-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/tests/utils/test_http_client.py b/seed/python-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/tests/utils/test_http_client.py +++ b/seed/python-sdk/oauth-client-credentials-mandatory-auth/no-custom-config/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/oauth-client-credentials-nested-root/src/seed/core/http_client.py b/seed/python-sdk/oauth-client-credentials-nested-root/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/oauth-client-credentials-nested-root/src/seed/core/http_client.py +++ b/seed/python-sdk/oauth-client-credentials-nested-root/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/oauth-client-credentials-nested-root/tests/utils/test_http_client.py b/seed/python-sdk/oauth-client-credentials-nested-root/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/oauth-client-credentials-nested-root/tests/utils/test_http_client.py +++ b/seed/python-sdk/oauth-client-credentials-nested-root/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/oauth-client-credentials-with-variables/src/seed/core/http_client.py b/seed/python-sdk/oauth-client-credentials-with-variables/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/oauth-client-credentials-with-variables/src/seed/core/http_client.py +++ b/seed/python-sdk/oauth-client-credentials-with-variables/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/oauth-client-credentials-with-variables/tests/utils/test_http_client.py b/seed/python-sdk/oauth-client-credentials-with-variables/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/oauth-client-credentials-with-variables/tests/utils/test_http_client.py +++ b/seed/python-sdk/oauth-client-credentials-with-variables/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/oauth-client-credentials/src/seed/core/http_client.py b/seed/python-sdk/oauth-client-credentials/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/oauth-client-credentials/src/seed/core/http_client.py +++ b/seed/python-sdk/oauth-client-credentials/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/oauth-client-credentials/tests/utils/test_http_client.py b/seed/python-sdk/oauth-client-credentials/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/oauth-client-credentials/tests/utils/test_http_client.py +++ b/seed/python-sdk/oauth-client-credentials/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/object/src/seed/core/http_client.py b/seed/python-sdk/object/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/object/src/seed/core/http_client.py +++ b/seed/python-sdk/object/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/object/tests/utils/test_http_client.py b/seed/python-sdk/object/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/object/tests/utils/test_http_client.py +++ b/seed/python-sdk/object/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/objects-with-imports/src/seed/core/http_client.py b/seed/python-sdk/objects-with-imports/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/objects-with-imports/src/seed/core/http_client.py +++ b/seed/python-sdk/objects-with-imports/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/objects-with-imports/tests/utils/test_http_client.py b/seed/python-sdk/objects-with-imports/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/objects-with-imports/tests/utils/test_http_client.py +++ b/seed/python-sdk/objects-with-imports/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/optional/src/seed/core/http_client.py b/seed/python-sdk/optional/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/optional/src/seed/core/http_client.py +++ b/seed/python-sdk/optional/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/optional/tests/utils/test_http_client.py b/seed/python-sdk/optional/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/optional/tests/utils/test_http_client.py +++ b/seed/python-sdk/optional/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/package-yml/src/seed/core/http_client.py b/seed/python-sdk/package-yml/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/package-yml/src/seed/core/http_client.py +++ b/seed/python-sdk/package-yml/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/package-yml/tests/utils/test_http_client.py b/seed/python-sdk/package-yml/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/package-yml/tests/utils/test_http_client.py +++ b/seed/python-sdk/package-yml/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/pagination-custom/src/seed/core/http_client.py b/seed/python-sdk/pagination-custom/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/pagination-custom/src/seed/core/http_client.py +++ b/seed/python-sdk/pagination-custom/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/pagination-custom/tests/utils/test_http_client.py b/seed/python-sdk/pagination-custom/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/pagination-custom/tests/utils/test_http_client.py +++ b/seed/python-sdk/pagination-custom/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/pagination/no-custom-config/tests/utils/test_http_client.py b/seed/python-sdk/pagination/no-custom-config/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/pagination/no-custom-config/tests/utils/test_http_client.py +++ b/seed/python-sdk/pagination/no-custom-config/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/pagination/no-inheritance-for-extended-models/tests/utils/test_http_client.py b/seed/python-sdk/pagination/no-inheritance-for-extended-models/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/pagination/no-inheritance-for-extended-models/tests/utils/test_http_client.py +++ b/seed/python-sdk/pagination/no-inheritance-for-extended-models/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/path-parameters/src/seed/core/http_client.py b/seed/python-sdk/path-parameters/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/path-parameters/src/seed/core/http_client.py +++ b/seed/python-sdk/path-parameters/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/path-parameters/tests/utils/test_http_client.py b/seed/python-sdk/path-parameters/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/path-parameters/tests/utils/test_http_client.py +++ b/seed/python-sdk/path-parameters/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/plain-text/src/seed/core/http_client.py b/seed/python-sdk/plain-text/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/plain-text/src/seed/core/http_client.py +++ b/seed/python-sdk/plain-text/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/plain-text/tests/utils/test_http_client.py b/seed/python-sdk/plain-text/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/plain-text/tests/utils/test_http_client.py +++ b/seed/python-sdk/plain-text/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/property-access/src/seed/core/http_client.py b/seed/python-sdk/property-access/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/property-access/src/seed/core/http_client.py +++ b/seed/python-sdk/property-access/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/property-access/tests/utils/test_http_client.py b/seed/python-sdk/property-access/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/property-access/tests/utils/test_http_client.py +++ b/seed/python-sdk/property-access/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/public-object/src/seed/core/http_client.py b/seed/python-sdk/public-object/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/public-object/src/seed/core/http_client.py +++ b/seed/python-sdk/public-object/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/public-object/tests/utils/test_http_client.py b/seed/python-sdk/public-object/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/public-object/tests/utils/test_http_client.py +++ b/seed/python-sdk/public-object/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/python-backslash-escape/src/seed/core/http_client.py b/seed/python-sdk/python-backslash-escape/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/python-backslash-escape/src/seed/core/http_client.py +++ b/seed/python-sdk/python-backslash-escape/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/python-backslash-escape/tests/utils/test_http_client.py b/seed/python-sdk/python-backslash-escape/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/python-backslash-escape/tests/utils/test_http_client.py +++ b/seed/python-sdk/python-backslash-escape/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/query-parameters-openapi-as-objects/no-custom-config/src/seed/core/http_client.py b/seed/python-sdk/query-parameters-openapi-as-objects/no-custom-config/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/query-parameters-openapi-as-objects/no-custom-config/src/seed/core/http_client.py +++ b/seed/python-sdk/query-parameters-openapi-as-objects/no-custom-config/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/query-parameters-openapi-as-objects/no-custom-config/tests/utils/test_http_client.py b/seed/python-sdk/query-parameters-openapi-as-objects/no-custom-config/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/query-parameters-openapi-as-objects/no-custom-config/tests/utils/test_http_client.py +++ b/seed/python-sdk/query-parameters-openapi-as-objects/no-custom-config/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/query-parameters-openapi/no-custom-config/src/seed/core/http_client.py b/seed/python-sdk/query-parameters-openapi/no-custom-config/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/query-parameters-openapi/no-custom-config/src/seed/core/http_client.py +++ b/seed/python-sdk/query-parameters-openapi/no-custom-config/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/query-parameters-openapi/no-custom-config/tests/utils/test_http_client.py b/seed/python-sdk/query-parameters-openapi/no-custom-config/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/query-parameters-openapi/no-custom-config/tests/utils/test_http_client.py +++ b/seed/python-sdk/query-parameters-openapi/no-custom-config/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/query-parameters/no-custom-config/src/seed/core/http_client.py b/seed/python-sdk/query-parameters/no-custom-config/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/query-parameters/no-custom-config/src/seed/core/http_client.py +++ b/seed/python-sdk/query-parameters/no-custom-config/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/query-parameters/no-custom-config/tests/utils/test_http_client.py b/seed/python-sdk/query-parameters/no-custom-config/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/query-parameters/no-custom-config/tests/utils/test_http_client.py +++ b/seed/python-sdk/query-parameters/no-custom-config/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/request-parameters/src/seed/core/http_client.py b/seed/python-sdk/request-parameters/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/request-parameters/src/seed/core/http_client.py +++ b/seed/python-sdk/request-parameters/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/request-parameters/tests/utils/test_http_client.py b/seed/python-sdk/request-parameters/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/request-parameters/tests/utils/test_http_client.py +++ b/seed/python-sdk/request-parameters/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/required-nullable/src/seed/core/http_client.py b/seed/python-sdk/required-nullable/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/required-nullable/src/seed/core/http_client.py +++ b/seed/python-sdk/required-nullable/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/required-nullable/tests/utils/test_http_client.py b/seed/python-sdk/required-nullable/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/required-nullable/tests/utils/test_http_client.py +++ b/seed/python-sdk/required-nullable/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/reserved-keywords/src/seed/core/http_client.py b/seed/python-sdk/reserved-keywords/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/reserved-keywords/src/seed/core/http_client.py +++ b/seed/python-sdk/reserved-keywords/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/reserved-keywords/tests/utils/test_http_client.py b/seed/python-sdk/reserved-keywords/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/reserved-keywords/tests/utils/test_http_client.py +++ b/seed/python-sdk/reserved-keywords/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/response-property/src/seed/core/http_client.py b/seed/python-sdk/response-property/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/response-property/src/seed/core/http_client.py +++ b/seed/python-sdk/response-property/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/response-property/tests/utils/test_http_client.py b/seed/python-sdk/response-property/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/response-property/tests/utils/test_http_client.py +++ b/seed/python-sdk/response-property/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/server-sent-event-examples/src/seed/core/http_client.py b/seed/python-sdk/server-sent-event-examples/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/server-sent-event-examples/src/seed/core/http_client.py +++ b/seed/python-sdk/server-sent-event-examples/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/server-sent-event-examples/tests/utils/test_http_client.py b/seed/python-sdk/server-sent-event-examples/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/server-sent-event-examples/tests/utils/test_http_client.py +++ b/seed/python-sdk/server-sent-event-examples/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/server-sent-events/with-wire-tests/src/seed/core/http_client.py b/seed/python-sdk/server-sent-events/with-wire-tests/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/server-sent-events/with-wire-tests/src/seed/core/http_client.py +++ b/seed/python-sdk/server-sent-events/with-wire-tests/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/server-sent-events/with-wire-tests/tests/utils/test_http_client.py b/seed/python-sdk/server-sent-events/with-wire-tests/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/server-sent-events/with-wire-tests/tests/utils/test_http_client.py +++ b/seed/python-sdk/server-sent-events/with-wire-tests/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/simple-api/src/seed/core/http_client.py b/seed/python-sdk/simple-api/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/simple-api/src/seed/core/http_client.py +++ b/seed/python-sdk/simple-api/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/simple-api/tests/utils/test_http_client.py b/seed/python-sdk/simple-api/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/simple-api/tests/utils/test_http_client.py +++ b/seed/python-sdk/simple-api/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/simple-fhir/no-inheritance-for-extended-models/src/seed/core/http_client.py b/seed/python-sdk/simple-fhir/no-inheritance-for-extended-models/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/simple-fhir/no-inheritance-for-extended-models/src/seed/core/http_client.py +++ b/seed/python-sdk/simple-fhir/no-inheritance-for-extended-models/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/simple-fhir/no-inheritance-for-extended-models/tests/utils/test_http_client.py b/seed/python-sdk/simple-fhir/no-inheritance-for-extended-models/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/simple-fhir/no-inheritance-for-extended-models/tests/utils/test_http_client.py +++ b/seed/python-sdk/simple-fhir/no-inheritance-for-extended-models/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/single-url-environment-default/src/seed/core/http_client.py b/seed/python-sdk/single-url-environment-default/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/single-url-environment-default/src/seed/core/http_client.py +++ b/seed/python-sdk/single-url-environment-default/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/single-url-environment-default/tests/utils/test_http_client.py b/seed/python-sdk/single-url-environment-default/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/single-url-environment-default/tests/utils/test_http_client.py +++ b/seed/python-sdk/single-url-environment-default/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/single-url-environment-no-default/src/seed/core/http_client.py b/seed/python-sdk/single-url-environment-no-default/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/single-url-environment-no-default/src/seed/core/http_client.py +++ b/seed/python-sdk/single-url-environment-no-default/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/single-url-environment-no-default/tests/utils/test_http_client.py b/seed/python-sdk/single-url-environment-no-default/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/single-url-environment-no-default/tests/utils/test_http_client.py +++ b/seed/python-sdk/single-url-environment-no-default/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/streaming-parameter/src/seed/core/http_client.py b/seed/python-sdk/streaming-parameter/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/streaming-parameter/src/seed/core/http_client.py +++ b/seed/python-sdk/streaming-parameter/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/streaming-parameter/tests/utils/test_http_client.py b/seed/python-sdk/streaming-parameter/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/streaming-parameter/tests/utils/test_http_client.py +++ b/seed/python-sdk/streaming-parameter/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/streaming/no-custom-config/src/seed/core/http_client.py b/seed/python-sdk/streaming/no-custom-config/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/streaming/no-custom-config/src/seed/core/http_client.py +++ b/seed/python-sdk/streaming/no-custom-config/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/streaming/no-custom-config/tests/utils/test_http_client.py b/seed/python-sdk/streaming/no-custom-config/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/streaming/no-custom-config/tests/utils/test_http_client.py +++ b/seed/python-sdk/streaming/no-custom-config/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/streaming/skip-pydantic-validation/src/seed/core/http_client.py b/seed/python-sdk/streaming/skip-pydantic-validation/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/streaming/skip-pydantic-validation/src/seed/core/http_client.py +++ b/seed/python-sdk/streaming/skip-pydantic-validation/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/streaming/skip-pydantic-validation/tests/utils/test_http_client.py b/seed/python-sdk/streaming/skip-pydantic-validation/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/streaming/skip-pydantic-validation/tests/utils/test_http_client.py +++ b/seed/python-sdk/streaming/skip-pydantic-validation/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/trace/src/seed/core/http_client.py b/seed/python-sdk/trace/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/trace/src/seed/core/http_client.py +++ b/seed/python-sdk/trace/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/trace/tests/utils/test_http_client.py b/seed/python-sdk/trace/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/trace/tests/utils/test_http_client.py +++ b/seed/python-sdk/trace/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/undiscriminated-union-with-response-property/src/seed/core/http_client.py b/seed/python-sdk/undiscriminated-union-with-response-property/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/undiscriminated-union-with-response-property/src/seed/core/http_client.py +++ b/seed/python-sdk/undiscriminated-union-with-response-property/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/undiscriminated-union-with-response-property/tests/utils/test_http_client.py b/seed/python-sdk/undiscriminated-union-with-response-property/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/undiscriminated-union-with-response-property/tests/utils/test_http_client.py +++ b/seed/python-sdk/undiscriminated-union-with-response-property/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/undiscriminated-unions/src/seed/core/http_client.py b/seed/python-sdk/undiscriminated-unions/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/undiscriminated-unions/src/seed/core/http_client.py +++ b/seed/python-sdk/undiscriminated-unions/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/undiscriminated-unions/tests/utils/test_http_client.py b/seed/python-sdk/undiscriminated-unions/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/undiscriminated-unions/tests/utils/test_http_client.py +++ b/seed/python-sdk/undiscriminated-unions/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/unions-with-local-date/src/seed/core/http_client.py b/seed/python-sdk/unions-with-local-date/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/unions-with-local-date/src/seed/core/http_client.py +++ b/seed/python-sdk/unions-with-local-date/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/unions-with-local-date/tests/utils/test_http_client.py b/seed/python-sdk/unions-with-local-date/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/unions-with-local-date/tests/utils/test_http_client.py +++ b/seed/python-sdk/unions-with-local-date/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/unions/no-custom-config/src/seed/core/http_client.py b/seed/python-sdk/unions/no-custom-config/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/unions/no-custom-config/src/seed/core/http_client.py +++ b/seed/python-sdk/unions/no-custom-config/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/unions/no-custom-config/tests/utils/test_http_client.py b/seed/python-sdk/unions/no-custom-config/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/unions/no-custom-config/tests/utils/test_http_client.py +++ b/seed/python-sdk/unions/no-custom-config/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/unions/union-naming-v1/src/seed/core/http_client.py b/seed/python-sdk/unions/union-naming-v1/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/unions/union-naming-v1/src/seed/core/http_client.py +++ b/seed/python-sdk/unions/union-naming-v1/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/unions/union-naming-v1/tests/utils/test_http_client.py b/seed/python-sdk/unions/union-naming-v1/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/unions/union-naming-v1/tests/utils/test_http_client.py +++ b/seed/python-sdk/unions/union-naming-v1/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/unions/union-utils/src/seed/core/http_client.py b/seed/python-sdk/unions/union-utils/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/unions/union-utils/src/seed/core/http_client.py +++ b/seed/python-sdk/unions/union-utils/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/unions/union-utils/tests/utils/test_http_client.py b/seed/python-sdk/unions/union-utils/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/unions/union-utils/tests/utils/test_http_client.py +++ b/seed/python-sdk/unions/union-utils/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/unknown/src/seed/core/http_client.py b/seed/python-sdk/unknown/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/unknown/src/seed/core/http_client.py +++ b/seed/python-sdk/unknown/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/unknown/tests/utils/test_http_client.py b/seed/python-sdk/unknown/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/unknown/tests/utils/test_http_client.py +++ b/seed/python-sdk/unknown/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/url-form-encoded/src/seed/core/http_client.py b/seed/python-sdk/url-form-encoded/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/url-form-encoded/src/seed/core/http_client.py +++ b/seed/python-sdk/url-form-encoded/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/url-form-encoded/tests/utils/test_http_client.py b/seed/python-sdk/url-form-encoded/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/url-form-encoded/tests/utils/test_http_client.py +++ b/seed/python-sdk/url-form-encoded/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/validation/no-custom-config/src/seed/core/http_client.py b/seed/python-sdk/validation/no-custom-config/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/validation/no-custom-config/src/seed/core/http_client.py +++ b/seed/python-sdk/validation/no-custom-config/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/validation/no-custom-config/tests/utils/test_http_client.py b/seed/python-sdk/validation/no-custom-config/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/validation/no-custom-config/tests/utils/test_http_client.py +++ b/seed/python-sdk/validation/no-custom-config/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/validation/with-defaults/src/seed/core/http_client.py b/seed/python-sdk/validation/with-defaults/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/validation/with-defaults/src/seed/core/http_client.py +++ b/seed/python-sdk/validation/with-defaults/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/validation/with-defaults/tests/utils/test_http_client.py b/seed/python-sdk/validation/with-defaults/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/validation/with-defaults/tests/utils/test_http_client.py +++ b/seed/python-sdk/validation/with-defaults/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/variables/src/seed/core/http_client.py b/seed/python-sdk/variables/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/variables/src/seed/core/http_client.py +++ b/seed/python-sdk/variables/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/variables/tests/utils/test_http_client.py b/seed/python-sdk/variables/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/variables/tests/utils/test_http_client.py +++ b/seed/python-sdk/variables/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/version-no-default/src/seed/core/http_client.py b/seed/python-sdk/version-no-default/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/version-no-default/src/seed/core/http_client.py +++ b/seed/python-sdk/version-no-default/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/version-no-default/tests/utils/test_http_client.py b/seed/python-sdk/version-no-default/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/version-no-default/tests/utils/test_http_client.py +++ b/seed/python-sdk/version-no-default/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/version/src/seed/core/http_client.py b/seed/python-sdk/version/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/version/src/seed/core/http_client.py +++ b/seed/python-sdk/version/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/version/tests/utils/test_http_client.py b/seed/python-sdk/version/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/version/tests/utils/test_http_client.py +++ b/seed/python-sdk/version/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/websocket-bearer-auth/src/seed/core/http_client.py b/seed/python-sdk/websocket-bearer-auth/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/websocket-bearer-auth/src/seed/core/http_client.py +++ b/seed/python-sdk/websocket-bearer-auth/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/websocket-bearer-auth/tests/utils/test_http_client.py b/seed/python-sdk/websocket-bearer-auth/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/websocket-bearer-auth/tests/utils/test_http_client.py +++ b/seed/python-sdk/websocket-bearer-auth/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/websocket-inferred-auth/src/seed/core/http_client.py b/seed/python-sdk/websocket-inferred-auth/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/websocket-inferred-auth/src/seed/core/http_client.py +++ b/seed/python-sdk/websocket-inferred-auth/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/websocket-inferred-auth/tests/utils/test_http_client.py b/seed/python-sdk/websocket-inferred-auth/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/websocket-inferred-auth/tests/utils/test_http_client.py +++ b/seed/python-sdk/websocket-inferred-auth/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/websocket/websocket-base/src/seed/core/http_client.py b/seed/python-sdk/websocket/websocket-base/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/websocket/websocket-base/src/seed/core/http_client.py +++ b/seed/python-sdk/websocket/websocket-base/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/websocket/websocket-base/tests/utils/test_http_client.py b/seed/python-sdk/websocket/websocket-base/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/websocket/websocket-base/tests/utils/test_http_client.py +++ b/seed/python-sdk/websocket/websocket-base/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/python-sdk/websocket/websocket-with_generated_clients/src/seed/core/http_client.py b/seed/python-sdk/websocket/websocket-with_generated_clients/src/seed/core/http_client.py index f4a7c0710387..bf19fcc243b6 100644 --- a/seed/python-sdk/websocket/websocket-with_generated_clients/src/seed/core/http_client.py +++ b/seed/python-sdk/websocket/websocket-with_generated_clients/src/seed/core/http_client.py @@ -261,6 +261,26 @@ def request( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + response = self.httpx_client.request( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -273,23 +293,7 @@ def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -360,6 +364,26 @@ def stream( data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -372,23 +396,7 @@ def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -473,6 +481,26 @@ async def request( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + # Add the input to each of these and do None-safety checks response = await self.httpx_client.request( method=method, @@ -486,23 +514,7 @@ async def request( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -575,6 +587,26 @@ async def stream( # Get headers (supports async token providers) _headers = await self._get_headers() + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + async with self.httpx_client.stream( method=method, url=urllib.parse.urljoin(f"{base_url}/", path), @@ -587,23 +619,7 @@ async def stream( } ) ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) - ) - ) - ), + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/seed/python-sdk/websocket/websocket-with_generated_clients/tests/utils/test_http_client.py b/seed/python-sdk/websocket/websocket-with_generated_clients/tests/utils/test_http_client.py index 79d01a455762..cf967de21db4 100644 --- a/seed/python-sdk/websocket/websocket-with_generated_clients/tests/utils/test_http_client.py +++ b/seed/python-sdk/websocket/websocket-with_generated_clients/tests/utils/test_http_client.py @@ -1,9 +1,43 @@ # This file was auto-generated by Fern from our API Definition. -from seed.core.http_client import get_request_body, remove_none_from_dict +from typing import Any, Dict + +import pytest + +from seed.core.http_client import AsyncHttpClient, HttpClient, get_request_body, remove_none_from_dict from seed.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} @@ -107,3 +141,113 @@ def test_remove_none_from_dict_empty_dict() -> None: def test_remove_none_from_dict_all_none() -> None: """Test that remove_none_from_dict handles dict with all None values.""" assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] diff --git a/seed/ruby-model/inferred-auth-implicit-api-key/.rubocop.yml b/seed/ruby-model/inferred-auth-implicit-api-key/.rubocop.yml new file mode 100644 index 000000000000..1e12390028ff --- /dev/null +++ b/seed/ruby-model/inferred-auth-implicit-api-key/.rubocop.yml @@ -0,0 +1,36 @@ +AllCops: + TargetRubyVersion: 2.7 + +Style/StringLiterals: + Enabled: true + EnforcedStyle: double_quotes + +Style/StringLiteralsInInterpolation: + Enabled: true + EnforcedStyle: double_quotes + +Layout/FirstHashElementLineBreak: + Enabled: true + +Layout/MultilineHashKeyLineBreaks: + Enabled: true + +# Generated files may be more complex than standard, disable +# these rules for now as a known limitation. +Metrics/ParameterLists: + Enabled: false + +Metrics/MethodLength: + Enabled: false + +Metrics/AbcSize: + Enabled: false + +Metrics/ClassLength: + Enabled: false + +Metrics/CyclomaticComplexity: + Enabled: false + +Metrics/PerceivedComplexity: + Enabled: false diff --git a/seed/ruby-model/inferred-auth-implicit-api-key/Gemfile b/seed/ruby-model/inferred-auth-implicit-api-key/Gemfile new file mode 100644 index 000000000000..49bd9cd0173c --- /dev/null +++ b/seed/ruby-model/inferred-auth-implicit-api-key/Gemfile @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + +gemspec + +gem "minitest", "~> 5.0" +gem "rake", "~> 13.0" +gem "rubocop", "~> 1.21" diff --git a/seed/ruby-model/inferred-auth-implicit-api-key/Rakefile b/seed/ruby-model/inferred-auth-implicit-api-key/Rakefile new file mode 100644 index 000000000000..2bdbce0cf2cb --- /dev/null +++ b/seed/ruby-model/inferred-auth-implicit-api-key/Rakefile @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require "rake/testtask" +require "rubocop/rake_task" + +task default: %i[test rubocop] + +Rake::TestTask.new do |t| + t.pattern = "./test/**/test_*.rb" +end + +RuboCop::RakeTask.new diff --git a/seed/ruby-model/inferred-auth-implicit-api-key/lib/gemconfig.rb b/seed/ruby-model/inferred-auth-implicit-api-key/lib/gemconfig.rb new file mode 100644 index 000000000000..6dd618f03ace --- /dev/null +++ b/seed/ruby-model/inferred-auth-implicit-api-key/lib/gemconfig.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module SeedInferredAuthImplicitApiKeyClient + module Gemconfig + VERSION = "" + AUTHORS = [""].freeze + EMAIL = "" + SUMMARY = "" + DESCRIPTION = "" + HOMEPAGE = "https://github.com/REPO/URL" + SOURCE_CODE_URI = "https://github.com/REPO/URL" + CHANGELOG_URI = "https://github.com/REPO/URL/blob/master/CHANGELOG.md" + end +end diff --git a/seed/ruby-model/inferred-auth-implicit-api-key/lib/seed_inferred_auth_implicit_api_key_client.rb b/seed/ruby-model/inferred-auth-implicit-api-key/lib/seed_inferred_auth_implicit_api_key_client.rb new file mode 100644 index 000000000000..c3fa20f3faf6 --- /dev/null +++ b/seed/ruby-model/inferred-auth-implicit-api-key/lib/seed_inferred_auth_implicit_api_key_client.rb @@ -0,0 +1,3 @@ +# frozen_string_literal: true + +require_relative "seed_inferred_auth_implicit_api_key_client/auth/types/token_response" diff --git a/seed/ruby-model/inferred-auth-implicit-api-key/lib/seed_inferred_auth_implicit_api_key_client/auth/types/token_response.rb b/seed/ruby-model/inferred-auth-implicit-api-key/lib/seed_inferred_auth_implicit_api_key_client/auth/types/token_response.rb new file mode 100644 index 000000000000..98df076c411e --- /dev/null +++ b/seed/ruby-model/inferred-auth-implicit-api-key/lib/seed_inferred_auth_implicit_api_key_client/auth/types/token_response.rb @@ -0,0 +1,89 @@ +# frozen_string_literal: true + +require "ostruct" +require "json" + +module SeedInferredAuthImplicitApiKeyClient + class Auth + # An auth token response. + class TokenResponse + # @return [String] + attr_reader :access_token + # @return [String] + attr_reader :token_type + # @return [Integer] + attr_reader :expires_in + # @return [String] + attr_reader :scope + # @return [OpenStruct] Additional properties unmapped to the current class definition + attr_reader :additional_properties + # @return [Object] + attr_reader :_field_set + protected :_field_set + + OMIT = Object.new + + # @param access_token [String] + # @param token_type [String] + # @param expires_in [Integer] + # @param scope [String] + # @param additional_properties [OpenStruct] Additional properties unmapped to the current class definition + # @return [SeedInferredAuthImplicitApiKeyClient::Auth::TokenResponse] + def initialize(access_token:, token_type:, expires_in:, scope: OMIT, additional_properties: nil) + @access_token = access_token + @token_type = token_type + @expires_in = expires_in + @scope = scope if scope != OMIT + @additional_properties = additional_properties + @_field_set = { + "access_token": access_token, + "token_type": token_type, + "expires_in": expires_in, + "scope": scope + }.reject do |_k, v| + v == OMIT + end + end + + # Deserialize a JSON object to an instance of TokenResponse + # + # @param json_object [String] + # @return [SeedInferredAuthImplicitApiKeyClient::Auth::TokenResponse] + def self.from_json(json_object:) + struct = JSON.parse(json_object, object_class: OpenStruct) + parsed_json = JSON.parse(json_object) + access_token = parsed_json["access_token"] + token_type = parsed_json["token_type"] + expires_in = parsed_json["expires_in"] + scope = parsed_json["scope"] + new( + access_token: access_token, + token_type: token_type, + expires_in: expires_in, + scope: scope, + additional_properties: struct + ) + end + + # Serialize an instance of TokenResponse to a JSON object + # + # @return [String] + def to_json(*_args) + @_field_set&.to_json + end + + # Leveraged for Union-type generation, validate_raw attempts to parse the given + # hash and check each fields type against the current object's property + # definitions. + # + # @param obj [Object] + # @return [Void] + def self.validate_raw(obj:) + obj.access_token.is_a?(String) != false || raise("Passed value for field obj.access_token is not the expected type, validation failed.") + obj.token_type.is_a?(String) != false || raise("Passed value for field obj.token_type is not the expected type, validation failed.") + obj.expires_in.is_a?(Integer) != false || raise("Passed value for field obj.expires_in is not the expected type, validation failed.") + obj.scope&.is_a?(String) != false || raise("Passed value for field obj.scope is not the expected type, validation failed.") + end + end + end +end diff --git a/seed/ruby-model/inferred-auth-implicit-api-key/seed_inferred_auth_implicit_api_key_client.gemspec b/seed/ruby-model/inferred-auth-implicit-api-key/seed_inferred_auth_implicit_api_key_client.gemspec new file mode 100644 index 000000000000..c8e0f3f4549f --- /dev/null +++ b/seed/ruby-model/inferred-auth-implicit-api-key/seed_inferred_auth_implicit_api_key_client.gemspec @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +require_relative "lib/gemconfig" + +Gem::Specification.new do |spec| + spec.name = "seed_inferred_auth_implicit_api_key_client" + spec.version = SeedInferredAuthImplicitApiKeyClient::Gemconfig::VERSION + spec.authors = SeedInferredAuthImplicitApiKeyClient::Gemconfig::AUTHORS + spec.email = SeedInferredAuthImplicitApiKeyClient::Gemconfig::EMAIL + spec.summary = SeedInferredAuthImplicitApiKeyClient::Gemconfig::SUMMARY + spec.description = SeedInferredAuthImplicitApiKeyClient::Gemconfig::DESCRIPTION + spec.homepage = SeedInferredAuthImplicitApiKeyClient::Gemconfig::HOMEPAGE + spec.required_ruby_version = ">= 2.7.0" + spec.metadata["homepage_uri"] = spec.homepage + spec.metadata["source_code_uri"] = SeedInferredAuthImplicitApiKeyClient::Gemconfig::SOURCE_CODE_URI + spec.metadata["changelog_uri"] = SeedInferredAuthImplicitApiKeyClient::Gemconfig::CHANGELOG_URI + spec.files = Dir.glob("lib/**/*") + spec.bindir = "exe" + spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) } + spec.require_paths = ["lib"] +end diff --git a/seed/ruby-model/inferred-auth-implicit-api-key/snippet.json b/seed/ruby-model/inferred-auth-implicit-api-key/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/ruby-model/inferred-auth-implicit-api-key/test/test_helper.rb b/seed/ruby-model/inferred-auth-implicit-api-key/test/test_helper.rb new file mode 100644 index 000000000000..19da4908e5bd --- /dev/null +++ b/seed/ruby-model/inferred-auth-implicit-api-key/test/test_helper.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +$LOAD_PATH.unshift File.expand_path("../lib", __dir__) + +require "minitest/autorun" +require "seed_inferred_auth_implicit_api_key_client" diff --git a/seed/ruby-model/inferred-auth-implicit-api-key/test/test_seed_inferred_auth_implicit_api_key_client.rb b/seed/ruby-model/inferred-auth-implicit-api-key/test/test_seed_inferred_auth_implicit_api_key_client.rb new file mode 100644 index 000000000000..be0b6414acd9 --- /dev/null +++ b/seed/ruby-model/inferred-auth-implicit-api-key/test/test_seed_inferred_auth_implicit_api_key_client.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require_relative "test_helper" +require "seed_inferred_auth_implicit_api_key_client" + +# Basic SeedInferredAuthImplicitApiKeyClient tests +class TestSeedInferredAuthImplicitApiKeyClient < Minitest::Test + def test_function + # SeedInferredAuthImplicitApiKeyClient::Client.new + end +end diff --git a/seed/ruby-model/inferred-auth-implicit-reference/.rubocop.yml b/seed/ruby-model/inferred-auth-implicit-reference/.rubocop.yml new file mode 100644 index 000000000000..1e12390028ff --- /dev/null +++ b/seed/ruby-model/inferred-auth-implicit-reference/.rubocop.yml @@ -0,0 +1,36 @@ +AllCops: + TargetRubyVersion: 2.7 + +Style/StringLiterals: + Enabled: true + EnforcedStyle: double_quotes + +Style/StringLiteralsInInterpolation: + Enabled: true + EnforcedStyle: double_quotes + +Layout/FirstHashElementLineBreak: + Enabled: true + +Layout/MultilineHashKeyLineBreaks: + Enabled: true + +# Generated files may be more complex than standard, disable +# these rules for now as a known limitation. +Metrics/ParameterLists: + Enabled: false + +Metrics/MethodLength: + Enabled: false + +Metrics/AbcSize: + Enabled: false + +Metrics/ClassLength: + Enabled: false + +Metrics/CyclomaticComplexity: + Enabled: false + +Metrics/PerceivedComplexity: + Enabled: false diff --git a/seed/ruby-model/inferred-auth-implicit-reference/Gemfile b/seed/ruby-model/inferred-auth-implicit-reference/Gemfile new file mode 100644 index 000000000000..49bd9cd0173c --- /dev/null +++ b/seed/ruby-model/inferred-auth-implicit-reference/Gemfile @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + +gemspec + +gem "minitest", "~> 5.0" +gem "rake", "~> 13.0" +gem "rubocop", "~> 1.21" diff --git a/seed/ruby-model/inferred-auth-implicit-reference/Rakefile b/seed/ruby-model/inferred-auth-implicit-reference/Rakefile new file mode 100644 index 000000000000..2bdbce0cf2cb --- /dev/null +++ b/seed/ruby-model/inferred-auth-implicit-reference/Rakefile @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require "rake/testtask" +require "rubocop/rake_task" + +task default: %i[test rubocop] + +Rake::TestTask.new do |t| + t.pattern = "./test/**/test_*.rb" +end + +RuboCop::RakeTask.new diff --git a/seed/ruby-model/inferred-auth-implicit-reference/lib/gemconfig.rb b/seed/ruby-model/inferred-auth-implicit-reference/lib/gemconfig.rb new file mode 100644 index 000000000000..5f7f0f028bbc --- /dev/null +++ b/seed/ruby-model/inferred-auth-implicit-reference/lib/gemconfig.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module SeedInferredAuthImplicitClient + module Gemconfig + VERSION = "" + AUTHORS = [""].freeze + EMAIL = "" + SUMMARY = "" + DESCRIPTION = "" + HOMEPAGE = "https://github.com/REPO/URL" + SOURCE_CODE_URI = "https://github.com/REPO/URL" + CHANGELOG_URI = "https://github.com/REPO/URL/blob/master/CHANGELOG.md" + end +end diff --git a/seed/ruby-model/inferred-auth-implicit-reference/lib/seed_inferred_auth_implicit_client.rb b/seed/ruby-model/inferred-auth-implicit-reference/lib/seed_inferred_auth_implicit_client.rb new file mode 100644 index 000000000000..1f2dadef03bf --- /dev/null +++ b/seed/ruby-model/inferred-auth-implicit-reference/lib/seed_inferred_auth_implicit_client.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +require_relative "seed_inferred_auth_implicit_client/auth/types/get_token_request" +require_relative "seed_inferred_auth_implicit_client/auth/types/refresh_token_request" +require_relative "seed_inferred_auth_implicit_client/auth/types/token_response" diff --git a/seed/ruby-model/inferred-auth-implicit-reference/lib/seed_inferred_auth_implicit_client/auth/types/get_token_request.rb b/seed/ruby-model/inferred-auth-implicit-reference/lib/seed_inferred_auth_implicit_client/auth/types/get_token_request.rb new file mode 100644 index 000000000000..fd9af8eb5ef1 --- /dev/null +++ b/seed/ruby-model/inferred-auth-implicit-reference/lib/seed_inferred_auth_implicit_client/auth/types/get_token_request.rb @@ -0,0 +1,97 @@ +# frozen_string_literal: true + +require "ostruct" +require "json" + +module SeedInferredAuthImplicitClient + class Auth + # A request to obtain an OAuth token. + class GetTokenRequest + # @return [String] + attr_reader :client_id + # @return [String] + attr_reader :client_secret + # @return [String] + attr_reader :audience + # @return [String] + attr_reader :grant_type + # @return [String] + attr_reader :scope + # @return [OpenStruct] Additional properties unmapped to the current class definition + attr_reader :additional_properties + # @return [Object] + attr_reader :_field_set + protected :_field_set + + OMIT = Object.new + + # @param client_id [String] + # @param client_secret [String] + # @param audience [String] + # @param grant_type [String] + # @param scope [String] + # @param additional_properties [OpenStruct] Additional properties unmapped to the current class definition + # @return [SeedInferredAuthImplicitClient::Auth::GetTokenRequest] + def initialize(client_id:, client_secret:, audience:, grant_type:, scope: OMIT, additional_properties: nil) + @client_id = client_id + @client_secret = client_secret + @audience = audience + @grant_type = grant_type + @scope = scope if scope != OMIT + @additional_properties = additional_properties + @_field_set = { + "client_id": client_id, + "client_secret": client_secret, + "audience": audience, + "grant_type": grant_type, + "scope": scope + }.reject do |_k, v| + v == OMIT + end + end + + # Deserialize a JSON object to an instance of GetTokenRequest + # + # @param json_object [String] + # @return [SeedInferredAuthImplicitClient::Auth::GetTokenRequest] + def self.from_json(json_object:) + struct = JSON.parse(json_object, object_class: OpenStruct) + parsed_json = JSON.parse(json_object) + client_id = parsed_json["client_id"] + client_secret = parsed_json["client_secret"] + audience = parsed_json["audience"] + grant_type = parsed_json["grant_type"] + scope = parsed_json["scope"] + new( + client_id: client_id, + client_secret: client_secret, + audience: audience, + grant_type: grant_type, + scope: scope, + additional_properties: struct + ) + end + + # Serialize an instance of GetTokenRequest to a JSON object + # + # @return [String] + def to_json(*_args) + @_field_set&.to_json + end + + # Leveraged for Union-type generation, validate_raw attempts to parse the given + # hash and check each fields type against the current object's property + # definitions. + # + # @param obj [Object] + # @return [Void] + def self.validate_raw(obj:) + obj.client_id.is_a?(String) != false || raise("Passed value for field obj.client_id is not the expected type, validation failed.") + obj.client_secret.is_a?(String) != false || raise("Passed value for field obj.client_secret is not the expected type, validation failed.") + obj.audience.is_a?(String) != false || raise("Passed value for field obj.audience is not the expected type, validation failed.") + obj.grant_type.is_a?(String) != false || raise("Passed value for field obj.grant_type is not the expected type, validation failed.") + obj.scope&.is_a?(String) != false || raise("Passed value for field obj.scope is not the expected type, validation failed.") + end + end + end +end diff --git a/seed/ruby-model/inferred-auth-implicit-reference/lib/seed_inferred_auth_implicit_client/auth/types/refresh_token_request.rb b/seed/ruby-model/inferred-auth-implicit-reference/lib/seed_inferred_auth_implicit_client/auth/types/refresh_token_request.rb new file mode 100644 index 000000000000..f97f26d86d97 --- /dev/null +++ b/seed/ruby-model/inferred-auth-implicit-reference/lib/seed_inferred_auth_implicit_client/auth/types/refresh_token_request.rb @@ -0,0 +1,106 @@ +# frozen_string_literal: true + +require "ostruct" +require "json" + +module SeedInferredAuthImplicitClient + class Auth + # A request to refresh an OAuth token. + class RefreshTokenRequest + # @return [String] + attr_reader :client_id + # @return [String] + attr_reader :client_secret + # @return [String] + attr_reader :refresh_token + # @return [String] + attr_reader :audience + # @return [String] + attr_reader :grant_type + # @return [String] + attr_reader :scope + # @return [OpenStruct] Additional properties unmapped to the current class definition + attr_reader :additional_properties + # @return [Object] + attr_reader :_field_set + protected :_field_set + + OMIT = Object.new + + # @param client_id [String] + # @param client_secret [String] + # @param refresh_token [String] + # @param audience [String] + # @param grant_type [String] + # @param scope [String] + # @param additional_properties [OpenStruct] Additional properties unmapped to the current class definition + # @return [SeedInferredAuthImplicitClient::Auth::RefreshTokenRequest] + def initialize(client_id:, client_secret:, refresh_token:, audience:, grant_type:, scope: OMIT, + additional_properties: nil) + @client_id = client_id + @client_secret = client_secret + @refresh_token = refresh_token + @audience = audience + @grant_type = grant_type + @scope = scope if scope != OMIT + @additional_properties = additional_properties + @_field_set = { + "client_id": client_id, + "client_secret": client_secret, + "refresh_token": refresh_token, + "audience": audience, + "grant_type": grant_type, + "scope": scope + }.reject do |_k, v| + v == OMIT + end + end + + # Deserialize a JSON object to an instance of RefreshTokenRequest + # + # @param json_object [String] + # @return [SeedInferredAuthImplicitClient::Auth::RefreshTokenRequest] + def self.from_json(json_object:) + struct = JSON.parse(json_object, object_class: OpenStruct) + parsed_json = JSON.parse(json_object) + client_id = parsed_json["client_id"] + client_secret = parsed_json["client_secret"] + refresh_token = parsed_json["refresh_token"] + audience = parsed_json["audience"] + grant_type = parsed_json["grant_type"] + scope = parsed_json["scope"] + new( + client_id: client_id, + client_secret: client_secret, + refresh_token: refresh_token, + audience: audience, + grant_type: grant_type, + scope: scope, + additional_properties: struct + ) + end + + # Serialize an instance of RefreshTokenRequest to a JSON object + # + # @return [String] + def to_json(*_args) + @_field_set&.to_json + end + + # Leveraged for Union-type generation, validate_raw attempts to parse the given + # hash and check each fields type against the current object's property + # definitions. + # + # @param obj [Object] + # @return [Void] + def self.validate_raw(obj:) + obj.client_id.is_a?(String) != false || raise("Passed value for field obj.client_id is not the expected type, validation failed.") + obj.client_secret.is_a?(String) != false || raise("Passed value for field obj.client_secret is not the expected type, validation failed.") + obj.refresh_token.is_a?(String) != false || raise("Passed value for field obj.refresh_token is not the expected type, validation failed.") + obj.audience.is_a?(String) != false || raise("Passed value for field obj.audience is not the expected type, validation failed.") + obj.grant_type.is_a?(String) != false || raise("Passed value for field obj.grant_type is not the expected type, validation failed.") + obj.scope&.is_a?(String) != false || raise("Passed value for field obj.scope is not the expected type, validation failed.") + end + end + end +end diff --git a/seed/ruby-model/inferred-auth-implicit-reference/lib/seed_inferred_auth_implicit_client/auth/types/token_response.rb b/seed/ruby-model/inferred-auth-implicit-reference/lib/seed_inferred_auth_implicit_client/auth/types/token_response.rb new file mode 100644 index 000000000000..a794ac0fe0c7 --- /dev/null +++ b/seed/ruby-model/inferred-auth-implicit-reference/lib/seed_inferred_auth_implicit_client/auth/types/token_response.rb @@ -0,0 +1,81 @@ +# frozen_string_literal: true + +require "ostruct" +require "json" + +module SeedInferredAuthImplicitClient + class Auth + # An OAuth token response. + class TokenResponse + # @return [String] + attr_reader :access_token + # @return [Integer] + attr_reader :expires_in + # @return [String] + attr_reader :refresh_token + # @return [OpenStruct] Additional properties unmapped to the current class definition + attr_reader :additional_properties + # @return [Object] + attr_reader :_field_set + protected :_field_set + + OMIT = Object.new + + # @param access_token [String] + # @param expires_in [Integer] + # @param refresh_token [String] + # @param additional_properties [OpenStruct] Additional properties unmapped to the current class definition + # @return [SeedInferredAuthImplicitClient::Auth::TokenResponse] + def initialize(access_token:, expires_in:, refresh_token: OMIT, additional_properties: nil) + @access_token = access_token + @expires_in = expires_in + @refresh_token = refresh_token if refresh_token != OMIT + @additional_properties = additional_properties + @_field_set = { + "access_token": access_token, + "expires_in": expires_in, + "refresh_token": refresh_token + }.reject do |_k, v| + v == OMIT + end + end + + # Deserialize a JSON object to an instance of TokenResponse + # + # @param json_object [String] + # @return [SeedInferredAuthImplicitClient::Auth::TokenResponse] + def self.from_json(json_object:) + struct = JSON.parse(json_object, object_class: OpenStruct) + parsed_json = JSON.parse(json_object) + access_token = parsed_json["access_token"] + expires_in = parsed_json["expires_in"] + refresh_token = parsed_json["refresh_token"] + new( + access_token: access_token, + expires_in: expires_in, + refresh_token: refresh_token, + additional_properties: struct + ) + end + + # Serialize an instance of TokenResponse to a JSON object + # + # @return [String] + def to_json(*_args) + @_field_set&.to_json + end + + # Leveraged for Union-type generation, validate_raw attempts to parse the given + # hash and check each fields type against the current object's property + # definitions. + # + # @param obj [Object] + # @return [Void] + def self.validate_raw(obj:) + obj.access_token.is_a?(String) != false || raise("Passed value for field obj.access_token is not the expected type, validation failed.") + obj.expires_in.is_a?(Integer) != false || raise("Passed value for field obj.expires_in is not the expected type, validation failed.") + obj.refresh_token&.is_a?(String) != false || raise("Passed value for field obj.refresh_token is not the expected type, validation failed.") + end + end + end +end diff --git a/seed/ruby-model/inferred-auth-implicit-reference/seed_inferred_auth_implicit_client.gemspec b/seed/ruby-model/inferred-auth-implicit-reference/seed_inferred_auth_implicit_client.gemspec new file mode 100644 index 000000000000..414338ad6724 --- /dev/null +++ b/seed/ruby-model/inferred-auth-implicit-reference/seed_inferred_auth_implicit_client.gemspec @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +require_relative "lib/gemconfig" + +Gem::Specification.new do |spec| + spec.name = "seed_inferred_auth_implicit_client" + spec.version = SeedInferredAuthImplicitClient::Gemconfig::VERSION + spec.authors = SeedInferredAuthImplicitClient::Gemconfig::AUTHORS + spec.email = SeedInferredAuthImplicitClient::Gemconfig::EMAIL + spec.summary = SeedInferredAuthImplicitClient::Gemconfig::SUMMARY + spec.description = SeedInferredAuthImplicitClient::Gemconfig::DESCRIPTION + spec.homepage = SeedInferredAuthImplicitClient::Gemconfig::HOMEPAGE + spec.required_ruby_version = ">= 2.7.0" + spec.metadata["homepage_uri"] = spec.homepage + spec.metadata["source_code_uri"] = SeedInferredAuthImplicitClient::Gemconfig::SOURCE_CODE_URI + spec.metadata["changelog_uri"] = SeedInferredAuthImplicitClient::Gemconfig::CHANGELOG_URI + spec.files = Dir.glob("lib/**/*") + spec.bindir = "exe" + spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) } + spec.require_paths = ["lib"] +end diff --git a/seed/ruby-model/inferred-auth-implicit-reference/snippet.json b/seed/ruby-model/inferred-auth-implicit-reference/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/ruby-model/inferred-auth-implicit-reference/test/test_helper.rb b/seed/ruby-model/inferred-auth-implicit-reference/test/test_helper.rb new file mode 100644 index 000000000000..57ce7cc1220e --- /dev/null +++ b/seed/ruby-model/inferred-auth-implicit-reference/test/test_helper.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +$LOAD_PATH.unshift File.expand_path("../lib", __dir__) + +require "minitest/autorun" +require "seed_inferred_auth_implicit_client" diff --git a/seed/ruby-model/inferred-auth-implicit-reference/test/test_seed_inferred_auth_implicit_client.rb b/seed/ruby-model/inferred-auth-implicit-reference/test/test_seed_inferred_auth_implicit_client.rb new file mode 100644 index 000000000000..c0248866bf31 --- /dev/null +++ b/seed/ruby-model/inferred-auth-implicit-reference/test/test_seed_inferred_auth_implicit_client.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require_relative "test_helper" +require "seed_inferred_auth_implicit_client" + +# Basic SeedInferredAuthImplicitClient tests +class TestSeedInferredAuthImplicitClient < Minitest::Test + def test_function + # SeedInferredAuthImplicitClient::Client.new + end +end diff --git a/seed/ruby-model/nullable-allof-extends/.rubocop.yml b/seed/ruby-model/nullable-allof-extends/.rubocop.yml new file mode 100644 index 000000000000..1e12390028ff --- /dev/null +++ b/seed/ruby-model/nullable-allof-extends/.rubocop.yml @@ -0,0 +1,36 @@ +AllCops: + TargetRubyVersion: 2.7 + +Style/StringLiterals: + Enabled: true + EnforcedStyle: double_quotes + +Style/StringLiteralsInInterpolation: + Enabled: true + EnforcedStyle: double_quotes + +Layout/FirstHashElementLineBreak: + Enabled: true + +Layout/MultilineHashKeyLineBreaks: + Enabled: true + +# Generated files may be more complex than standard, disable +# these rules for now as a known limitation. +Metrics/ParameterLists: + Enabled: false + +Metrics/MethodLength: + Enabled: false + +Metrics/AbcSize: + Enabled: false + +Metrics/ClassLength: + Enabled: false + +Metrics/CyclomaticComplexity: + Enabled: false + +Metrics/PerceivedComplexity: + Enabled: false diff --git a/seed/ruby-model/nullable-allof-extends/Gemfile b/seed/ruby-model/nullable-allof-extends/Gemfile new file mode 100644 index 000000000000..49bd9cd0173c --- /dev/null +++ b/seed/ruby-model/nullable-allof-extends/Gemfile @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + +gemspec + +gem "minitest", "~> 5.0" +gem "rake", "~> 13.0" +gem "rubocop", "~> 1.21" diff --git a/seed/ruby-model/nullable-allof-extends/Rakefile b/seed/ruby-model/nullable-allof-extends/Rakefile new file mode 100644 index 000000000000..2bdbce0cf2cb --- /dev/null +++ b/seed/ruby-model/nullable-allof-extends/Rakefile @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require "rake/testtask" +require "rubocop/rake_task" + +task default: %i[test rubocop] + +Rake::TestTask.new do |t| + t.pattern = "./test/**/test_*.rb" +end + +RuboCop::RakeTask.new diff --git a/seed/ruby-model/nullable-allof-extends/lib/gemconfig.rb b/seed/ruby-model/nullable-allof-extends/lib/gemconfig.rb new file mode 100644 index 000000000000..b4c1f83d5b0b --- /dev/null +++ b/seed/ruby-model/nullable-allof-extends/lib/gemconfig.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module SeedApiClient + module Gemconfig + VERSION = "" + AUTHORS = [""].freeze + EMAIL = "" + SUMMARY = "" + DESCRIPTION = "" + HOMEPAGE = "https://github.com/REPO/URL" + SOURCE_CODE_URI = "https://github.com/REPO/URL" + CHANGELOG_URI = "https://github.com/REPO/URL/blob/master/CHANGELOG.md" + end +end diff --git a/seed/ruby-model/nullable-allof-extends/lib/seed_api_client.rb b/seed/ruby-model/nullable-allof-extends/lib/seed_api_client.rb new file mode 100644 index 000000000000..7a43d202f792 --- /dev/null +++ b/seed/ruby-model/nullable-allof-extends/lib/seed_api_client.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +require_relative "seed_api_client/types/root_object" +require_relative "seed_api_client/types/normal_object" +require_relative "seed_api_client/types/nullable_object" diff --git a/seed/ruby-model/nullable-allof-extends/lib/seed_api_client/types/normal_object.rb b/seed/ruby-model/nullable-allof-extends/lib/seed_api_client/types/normal_object.rb new file mode 100644 index 000000000000..c4cd78d8984f --- /dev/null +++ b/seed/ruby-model/nullable-allof-extends/lib/seed_api_client/types/normal_object.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +require "ostruct" +require "json" + +module SeedApiClient + # A standard object with no nullable issues. + class NormalObject + # @return [String] + attr_reader :normal_field + # @return [OpenStruct] Additional properties unmapped to the current class definition + attr_reader :additional_properties + # @return [Object] + attr_reader :_field_set + protected :_field_set + + OMIT = Object.new + + # @param normal_field [String] + # @param additional_properties [OpenStruct] Additional properties unmapped to the current class definition + # @return [SeedApiClient::NormalObject] + def initialize(normal_field: OMIT, additional_properties: nil) + @normal_field = normal_field if normal_field != OMIT + @additional_properties = additional_properties + @_field_set = { "normalField": normal_field }.reject do |_k, v| + v == OMIT + end + end + + # Deserialize a JSON object to an instance of NormalObject + # + # @param json_object [String] + # @return [SeedApiClient::NormalObject] + def self.from_json(json_object:) + struct = JSON.parse(json_object, object_class: OpenStruct) + parsed_json = JSON.parse(json_object) + normal_field = parsed_json["normalField"] + new(normal_field: normal_field, additional_properties: struct) + end + + # Serialize an instance of NormalObject to a JSON object + # + # @return [String] + def to_json(*_args) + @_field_set&.to_json + end + + # Leveraged for Union-type generation, validate_raw attempts to parse the given + # hash and check each fields type against the current object's property + # definitions. + # + # @param obj [Object] + # @return [Void] + def self.validate_raw(obj:) + obj.normal_field&.is_a?(String) != false || raise("Passed value for field obj.normal_field is not the expected type, validation failed.") + end + end +end diff --git a/seed/ruby-model/nullable-allof-extends/lib/seed_api_client/types/nullable_object.rb b/seed/ruby-model/nullable-allof-extends/lib/seed_api_client/types/nullable_object.rb new file mode 100644 index 000000000000..2fd2eeb6d14f --- /dev/null +++ b/seed/ruby-model/nullable-allof-extends/lib/seed_api_client/types/nullable_object.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +require "ostruct" +require "json" + +module SeedApiClient + # This schema has nullable:true at the top level. + class NullableObject + # @return [String] + attr_reader :nullable_field + # @return [OpenStruct] Additional properties unmapped to the current class definition + attr_reader :additional_properties + # @return [Object] + attr_reader :_field_set + protected :_field_set + + OMIT = Object.new + + # @param nullable_field [String] + # @param additional_properties [OpenStruct] Additional properties unmapped to the current class definition + # @return [SeedApiClient::NullableObject] + def initialize(nullable_field: OMIT, additional_properties: nil) + @nullable_field = nullable_field if nullable_field != OMIT + @additional_properties = additional_properties + @_field_set = { "nullableField": nullable_field }.reject do |_k, v| + v == OMIT + end + end + + # Deserialize a JSON object to an instance of NullableObject + # + # @param json_object [String] + # @return [SeedApiClient::NullableObject] + def self.from_json(json_object:) + struct = JSON.parse(json_object, object_class: OpenStruct) + parsed_json = JSON.parse(json_object) + nullable_field = parsed_json["nullableField"] + new(nullable_field: nullable_field, additional_properties: struct) + end + + # Serialize an instance of NullableObject to a JSON object + # + # @return [String] + def to_json(*_args) + @_field_set&.to_json + end + + # Leveraged for Union-type generation, validate_raw attempts to parse the given + # hash and check each fields type against the current object's property + # definitions. + # + # @param obj [Object] + # @return [Void] + def self.validate_raw(obj:) + obj.nullable_field&.is_a?(String) != false || raise("Passed value for field obj.nullable_field is not the expected type, validation failed.") + end + end +end diff --git a/seed/ruby-model/nullable-allof-extends/lib/seed_api_client/types/root_object.rb b/seed/ruby-model/nullable-allof-extends/lib/seed_api_client/types/root_object.rb new file mode 100644 index 000000000000..50b951acd87c --- /dev/null +++ b/seed/ruby-model/nullable-allof-extends/lib/seed_api_client/types/root_object.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: true + +require "ostruct" +require "json" + +module SeedApiClient + # Object inheriting from a nullable schema via allOf. + class RootObject + # @return [String] + attr_reader :normal_field + # @return [String] + attr_reader :nullable_field + # @return [OpenStruct] Additional properties unmapped to the current class definition + attr_reader :additional_properties + # @return [Object] + attr_reader :_field_set + protected :_field_set + + OMIT = Object.new + + # @param normal_field [String] + # @param nullable_field [String] + # @param additional_properties [OpenStruct] Additional properties unmapped to the current class definition + # @return [SeedApiClient::RootObject] + def initialize(normal_field: OMIT, nullable_field: OMIT, additional_properties: nil) + @normal_field = normal_field if normal_field != OMIT + @nullable_field = nullable_field if nullable_field != OMIT + @additional_properties = additional_properties + @_field_set = { "normalField": normal_field, "nullableField": nullable_field }.reject do |_k, v| + v == OMIT + end + end + + # Deserialize a JSON object to an instance of RootObject + # + # @param json_object [String] + # @return [SeedApiClient::RootObject] + def self.from_json(json_object:) + struct = JSON.parse(json_object, object_class: OpenStruct) + parsed_json = JSON.parse(json_object) + normal_field = parsed_json["normalField"] + nullable_field = parsed_json["nullableField"] + new( + normal_field: normal_field, + nullable_field: nullable_field, + additional_properties: struct + ) + end + + # Serialize an instance of RootObject to a JSON object + # + # @return [String] + def to_json(*_args) + @_field_set&.to_json + end + + # Leveraged for Union-type generation, validate_raw attempts to parse the given + # hash and check each fields type against the current object's property + # definitions. + # + # @param obj [Object] + # @return [Void] + def self.validate_raw(obj:) + obj.normal_field&.is_a?(String) != false || raise("Passed value for field obj.normal_field is not the expected type, validation failed.") + obj.nullable_field&.is_a?(String) != false || raise("Passed value for field obj.nullable_field is not the expected type, validation failed.") + end + end +end diff --git a/seed/ruby-model/nullable-allof-extends/seed_api_client.gemspec b/seed/ruby-model/nullable-allof-extends/seed_api_client.gemspec new file mode 100644 index 000000000000..70ca9e56ece4 --- /dev/null +++ b/seed/ruby-model/nullable-allof-extends/seed_api_client.gemspec @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +require_relative "lib/gemconfig" + +Gem::Specification.new do |spec| + spec.name = "seed_api_client" + spec.version = SeedApiClient::Gemconfig::VERSION + spec.authors = SeedApiClient::Gemconfig::AUTHORS + spec.email = SeedApiClient::Gemconfig::EMAIL + spec.summary = SeedApiClient::Gemconfig::SUMMARY + spec.description = SeedApiClient::Gemconfig::DESCRIPTION + spec.homepage = SeedApiClient::Gemconfig::HOMEPAGE + spec.required_ruby_version = ">= 2.7.0" + spec.metadata["homepage_uri"] = spec.homepage + spec.metadata["source_code_uri"] = SeedApiClient::Gemconfig::SOURCE_CODE_URI + spec.metadata["changelog_uri"] = SeedApiClient::Gemconfig::CHANGELOG_URI + spec.files = Dir.glob("lib/**/*") + spec.bindir = "exe" + spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) } + spec.require_paths = ["lib"] +end diff --git a/seed/ruby-model/nullable-allof-extends/snippet.json b/seed/ruby-model/nullable-allof-extends/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/ruby-model/nullable-allof-extends/test/test_helper.rb b/seed/ruby-model/nullable-allof-extends/test/test_helper.rb new file mode 100644 index 000000000000..a8adf4207943 --- /dev/null +++ b/seed/ruby-model/nullable-allof-extends/test/test_helper.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +$LOAD_PATH.unshift File.expand_path("../lib", __dir__) + +require "minitest/autorun" +require "seed_api_client" diff --git a/seed/ruby-model/nullable-allof-extends/test/test_seed_api_client.rb b/seed/ruby-model/nullable-allof-extends/test/test_seed_api_client.rb new file mode 100644 index 000000000000..a151098d1089 --- /dev/null +++ b/seed/ruby-model/nullable-allof-extends/test/test_seed_api_client.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require_relative "test_helper" +require "seed_api_client" + +# Basic SeedApiClient tests +class TestSeedApiClient < Minitest::Test + def test_function + # SeedApiClient::Client.new + end +end diff --git a/seed/ruby-model/oauth-client-credentials-mandatory-auth/.rubocop.yml b/seed/ruby-model/oauth-client-credentials-mandatory-auth/.rubocop.yml new file mode 100644 index 000000000000..1e12390028ff --- /dev/null +++ b/seed/ruby-model/oauth-client-credentials-mandatory-auth/.rubocop.yml @@ -0,0 +1,36 @@ +AllCops: + TargetRubyVersion: 2.7 + +Style/StringLiterals: + Enabled: true + EnforcedStyle: double_quotes + +Style/StringLiteralsInInterpolation: + Enabled: true + EnforcedStyle: double_quotes + +Layout/FirstHashElementLineBreak: + Enabled: true + +Layout/MultilineHashKeyLineBreaks: + Enabled: true + +# Generated files may be more complex than standard, disable +# these rules for now as a known limitation. +Metrics/ParameterLists: + Enabled: false + +Metrics/MethodLength: + Enabled: false + +Metrics/AbcSize: + Enabled: false + +Metrics/ClassLength: + Enabled: false + +Metrics/CyclomaticComplexity: + Enabled: false + +Metrics/PerceivedComplexity: + Enabled: false diff --git a/seed/ruby-model/oauth-client-credentials-mandatory-auth/Gemfile b/seed/ruby-model/oauth-client-credentials-mandatory-auth/Gemfile new file mode 100644 index 000000000000..49bd9cd0173c --- /dev/null +++ b/seed/ruby-model/oauth-client-credentials-mandatory-auth/Gemfile @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + +gemspec + +gem "minitest", "~> 5.0" +gem "rake", "~> 13.0" +gem "rubocop", "~> 1.21" diff --git a/seed/ruby-model/oauth-client-credentials-mandatory-auth/Rakefile b/seed/ruby-model/oauth-client-credentials-mandatory-auth/Rakefile new file mode 100644 index 000000000000..2bdbce0cf2cb --- /dev/null +++ b/seed/ruby-model/oauth-client-credentials-mandatory-auth/Rakefile @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require "rake/testtask" +require "rubocop/rake_task" + +task default: %i[test rubocop] + +Rake::TestTask.new do |t| + t.pattern = "./test/**/test_*.rb" +end + +RuboCop::RakeTask.new diff --git a/seed/ruby-model/oauth-client-credentials-mandatory-auth/lib/gemconfig.rb b/seed/ruby-model/oauth-client-credentials-mandatory-auth/lib/gemconfig.rb new file mode 100644 index 000000000000..4275848260be --- /dev/null +++ b/seed/ruby-model/oauth-client-credentials-mandatory-auth/lib/gemconfig.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module SeedOauthClientCredentialsMandatoryAuthClient + module Gemconfig + VERSION = "" + AUTHORS = [""].freeze + EMAIL = "" + SUMMARY = "" + DESCRIPTION = "" + HOMEPAGE = "https://github.com/REPO/URL" + SOURCE_CODE_URI = "https://github.com/REPO/URL" + CHANGELOG_URI = "https://github.com/REPO/URL/blob/master/CHANGELOG.md" + end +end diff --git a/seed/ruby-model/oauth-client-credentials-mandatory-auth/lib/seed_oauth_client_credentials_mandatory_auth_client.rb b/seed/ruby-model/oauth-client-credentials-mandatory-auth/lib/seed_oauth_client_credentials_mandatory_auth_client.rb new file mode 100644 index 000000000000..a379993f48ae --- /dev/null +++ b/seed/ruby-model/oauth-client-credentials-mandatory-auth/lib/seed_oauth_client_credentials_mandatory_auth_client.rb @@ -0,0 +1,3 @@ +# frozen_string_literal: true + +require_relative "seed_oauth_client_credentials_mandatory_auth_client/auth/types/token_response" diff --git a/seed/ruby-model/oauth-client-credentials-mandatory-auth/lib/seed_oauth_client_credentials_mandatory_auth_client/auth/types/token_response.rb b/seed/ruby-model/oauth-client-credentials-mandatory-auth/lib/seed_oauth_client_credentials_mandatory_auth_client/auth/types/token_response.rb new file mode 100644 index 000000000000..0d16d2ad91e4 --- /dev/null +++ b/seed/ruby-model/oauth-client-credentials-mandatory-auth/lib/seed_oauth_client_credentials_mandatory_auth_client/auth/types/token_response.rb @@ -0,0 +1,81 @@ +# frozen_string_literal: true + +require "ostruct" +require "json" + +module SeedOauthClientCredentialsMandatoryAuthClient + class Auth + # An OAuth token response. + class TokenResponse + # @return [String] + attr_reader :access_token + # @return [Integer] + attr_reader :expires_in + # @return [String] + attr_reader :refresh_token + # @return [OpenStruct] Additional properties unmapped to the current class definition + attr_reader :additional_properties + # @return [Object] + attr_reader :_field_set + protected :_field_set + + OMIT = Object.new + + # @param access_token [String] + # @param expires_in [Integer] + # @param refresh_token [String] + # @param additional_properties [OpenStruct] Additional properties unmapped to the current class definition + # @return [SeedOauthClientCredentialsMandatoryAuthClient::Auth::TokenResponse] + def initialize(access_token:, expires_in:, refresh_token: OMIT, additional_properties: nil) + @access_token = access_token + @expires_in = expires_in + @refresh_token = refresh_token if refresh_token != OMIT + @additional_properties = additional_properties + @_field_set = { + "access_token": access_token, + "expires_in": expires_in, + "refresh_token": refresh_token + }.reject do |_k, v| + v == OMIT + end + end + + # Deserialize a JSON object to an instance of TokenResponse + # + # @param json_object [String] + # @return [SeedOauthClientCredentialsMandatoryAuthClient::Auth::TokenResponse] + def self.from_json(json_object:) + struct = JSON.parse(json_object, object_class: OpenStruct) + parsed_json = JSON.parse(json_object) + access_token = parsed_json["access_token"] + expires_in = parsed_json["expires_in"] + refresh_token = parsed_json["refresh_token"] + new( + access_token: access_token, + expires_in: expires_in, + refresh_token: refresh_token, + additional_properties: struct + ) + end + + # Serialize an instance of TokenResponse to a JSON object + # + # @return [String] + def to_json(*_args) + @_field_set&.to_json + end + + # Leveraged for Union-type generation, validate_raw attempts to parse the given + # hash and check each fields type against the current object's property + # definitions. + # + # @param obj [Object] + # @return [Void] + def self.validate_raw(obj:) + obj.access_token.is_a?(String) != false || raise("Passed value for field obj.access_token is not the expected type, validation failed.") + obj.expires_in.is_a?(Integer) != false || raise("Passed value for field obj.expires_in is not the expected type, validation failed.") + obj.refresh_token&.is_a?(String) != false || raise("Passed value for field obj.refresh_token is not the expected type, validation failed.") + end + end + end +end diff --git a/seed/ruby-model/oauth-client-credentials-mandatory-auth/seed_oauth_client_credentials_mandatory_auth_client.gemspec b/seed/ruby-model/oauth-client-credentials-mandatory-auth/seed_oauth_client_credentials_mandatory_auth_client.gemspec new file mode 100644 index 000000000000..ba6c81d49c66 --- /dev/null +++ b/seed/ruby-model/oauth-client-credentials-mandatory-auth/seed_oauth_client_credentials_mandatory_auth_client.gemspec @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +require_relative "lib/gemconfig" + +Gem::Specification.new do |spec| + spec.name = "seed_oauth_client_credentials_mandatory_auth_client" + spec.version = SeedOauthClientCredentialsMandatoryAuthClient::Gemconfig::VERSION + spec.authors = SeedOauthClientCredentialsMandatoryAuthClient::Gemconfig::AUTHORS + spec.email = SeedOauthClientCredentialsMandatoryAuthClient::Gemconfig::EMAIL + spec.summary = SeedOauthClientCredentialsMandatoryAuthClient::Gemconfig::SUMMARY + spec.description = SeedOauthClientCredentialsMandatoryAuthClient::Gemconfig::DESCRIPTION + spec.homepage = SeedOauthClientCredentialsMandatoryAuthClient::Gemconfig::HOMEPAGE + spec.required_ruby_version = ">= 2.7.0" + spec.metadata["homepage_uri"] = spec.homepage + spec.metadata["source_code_uri"] = SeedOauthClientCredentialsMandatoryAuthClient::Gemconfig::SOURCE_CODE_URI + spec.metadata["changelog_uri"] = SeedOauthClientCredentialsMandatoryAuthClient::Gemconfig::CHANGELOG_URI + spec.files = Dir.glob("lib/**/*") + spec.bindir = "exe" + spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) } + spec.require_paths = ["lib"] +end diff --git a/seed/ruby-model/oauth-client-credentials-mandatory-auth/snippet.json b/seed/ruby-model/oauth-client-credentials-mandatory-auth/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/ruby-model/oauth-client-credentials-mandatory-auth/test/test_helper.rb b/seed/ruby-model/oauth-client-credentials-mandatory-auth/test/test_helper.rb new file mode 100644 index 000000000000..802377e6e137 --- /dev/null +++ b/seed/ruby-model/oauth-client-credentials-mandatory-auth/test/test_helper.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +$LOAD_PATH.unshift File.expand_path("../lib", __dir__) + +require "minitest/autorun" +require "seed_oauth_client_credentials_mandatory_auth_client" diff --git a/seed/ruby-model/oauth-client-credentials-mandatory-auth/test/test_seed_oauth_client_credentials_mandatory_auth_client.rb b/seed/ruby-model/oauth-client-credentials-mandatory-auth/test/test_seed_oauth_client_credentials_mandatory_auth_client.rb new file mode 100644 index 000000000000..127496edf302 --- /dev/null +++ b/seed/ruby-model/oauth-client-credentials-mandatory-auth/test/test_seed_oauth_client_credentials_mandatory_auth_client.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require_relative "test_helper" +require "seed_oauth_client_credentials_mandatory_auth_client" + +# Basic SeedOauthClientCredentialsMandatoryAuthClient tests +class TestSeedOauthClientCredentialsMandatoryAuthClient < Minitest::Test + def test_function + # SeedOauthClientCredentialsMandatoryAuthClient::Client.new + end +end diff --git a/seed/ruby-model/pagination/lib/seed_pagination_client.rb b/seed/ruby-model/pagination/lib/seed_pagination_client.rb index 8256e288b652..efd20d83d04f 100644 --- a/seed/ruby-model/pagination/lib/seed_pagination_client.rb +++ b/seed/ruby-model/pagination/lib/seed_pagination_client.rb @@ -40,6 +40,7 @@ require_relative "seed_pagination_client/users/types/list_users_extended_response" require_relative "seed_pagination_client/users/types/list_users_extended_optional_list_response" require_relative "seed_pagination_client/users/types/list_users_pagination_response" +require_relative "seed_pagination_client/users/types/list_users_optional_data_pagination_response" require_relative "seed_pagination_client/users/types/list_users_mixed_type_pagination_response" require_relative "seed_pagination_client/users/types/page" require_relative "seed_pagination_client/users/types/next_page" diff --git a/seed/ruby-model/pagination/lib/seed_pagination_client/users/types/list_users_optional_data_pagination_response.rb b/seed/ruby-model/pagination/lib/seed_pagination_client/users/types/list_users_optional_data_pagination_response.rb new file mode 100644 index 000000000000..bd8eea2648eb --- /dev/null +++ b/seed/ruby-model/pagination/lib/seed_pagination_client/users/types/list_users_optional_data_pagination_response.rb @@ -0,0 +1,100 @@ +# frozen_string_literal: true + +require_relative "page" +require_relative "user" +require "ostruct" +require "json" + +module SeedPaginationClient + class Users + class ListUsersOptionalDataPaginationResponse + # @return [Boolean] + attr_reader :has_next_page + # @return [SeedPaginationClient::Users::Page] + attr_reader :page + # @return [Integer] The totall number of /users + attr_reader :total_count + # @return [Array] + attr_reader :data + # @return [OpenStruct] Additional properties unmapped to the current class definition + attr_reader :additional_properties + # @return [Object] + attr_reader :_field_set + protected :_field_set + + OMIT = Object.new + + # @param has_next_page [Boolean] + # @param page [SeedPaginationClient::Users::Page] + # @param total_count [Integer] The totall number of /users + # @param data [Array] + # @param additional_properties [OpenStruct] Additional properties unmapped to the current class definition + # @return [SeedPaginationClient::Users::ListUsersOptionalDataPaginationResponse] + def initialize(total_count:, has_next_page: OMIT, page: OMIT, data: OMIT, additional_properties: nil) + @has_next_page = has_next_page if has_next_page != OMIT + @page = page if page != OMIT + @total_count = total_count + @data = data if data != OMIT + @additional_properties = additional_properties + @_field_set = { + "hasNextPage": has_next_page, + "page": page, + "total_count": total_count, + "data": data + }.reject do |_k, v| + v == OMIT + end + end + + # Deserialize a JSON object to an instance of + # ListUsersOptionalDataPaginationResponse + # + # @param json_object [String] + # @return [SeedPaginationClient::Users::ListUsersOptionalDataPaginationResponse] + def self.from_json(json_object:) + struct = JSON.parse(json_object, object_class: OpenStruct) + parsed_json = JSON.parse(json_object) + has_next_page = parsed_json["hasNextPage"] + if parsed_json["page"].nil? + page = nil + else + page = parsed_json["page"].to_json + page = SeedPaginationClient::Users::Page.from_json(json_object: page) + end + total_count = parsed_json["total_count"] + data = parsed_json["data"]&.map do |item| + item = item.to_json + SeedPaginationClient::Users::User.from_json(json_object: item) + end + new( + has_next_page: has_next_page, + page: page, + total_count: total_count, + data: data, + additional_properties: struct + ) + end + + # Serialize an instance of ListUsersOptionalDataPaginationResponse to a JSON + # object + # + # @return [String] + def to_json(*_args) + @_field_set&.to_json + end + + # Leveraged for Union-type generation, validate_raw attempts to parse the given + # hash and check each fields type against the current object's property + # definitions. + # + # @param obj [Object] + # @return [Void] + def self.validate_raw(obj:) + obj.has_next_page&.is_a?(Boolean) != false || raise("Passed value for field obj.has_next_page is not the expected type, validation failed.") + obj.page.nil? || SeedPaginationClient::Users::Page.validate_raw(obj: obj.page) + obj.total_count.is_a?(Integer) != false || raise("Passed value for field obj.total_count is not the expected type, validation failed.") + obj.data&.is_a?(Array) != false || raise("Passed value for field obj.data is not the expected type, validation failed.") + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/.fern/metadata.json b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/.fern/metadata.json new file mode 100644 index 000000000000..ce04af614b97 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/.fern/metadata.json @@ -0,0 +1,5 @@ +{ + "cliVersion": "DUMMY", + "generatorName": "fernapi/fern-ruby-sdk-v2", + "generatorVersion": "latest" +} \ No newline at end of file diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/.github/workflows/ci.yml b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/.github/workflows/ci.yml new file mode 100644 index 000000000000..c52a42e0dd03 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/.github/workflows/ci.yml @@ -0,0 +1,37 @@ +name: ci + +on: [push, pull_request] + +jobs: + lint: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: "3.3" + bundler-cache: true + + - name: Run Rubocop + run: bundle exec rubocop + + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: "3.3" + bundler-cache: true + + - name: Run Tests + run: bundle exec rake test + diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/.gitignore b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/.gitignore new file mode 100644 index 000000000000..c111b331371a --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/.gitignore @@ -0,0 +1 @@ +*.gem diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/.rubocop.yml b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/.rubocop.yml new file mode 100644 index 000000000000..2c5567ea193e --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/.rubocop.yml @@ -0,0 +1,60 @@ +plugins: + - rubocop-minitest + +AllCops: + TargetRubyVersion: 3.3 + NewCops: enable + +Style/StringLiterals: + EnforcedStyle: double_quotes + +Style/StringLiteralsInInterpolation: + EnforcedStyle: double_quotes + +Style/AccessModifierDeclarations: + Enabled: false + +Lint/ConstantDefinitionInBlock: + Enabled: false + +Metrics/AbcSize: + Enabled: false + +Metrics/BlockLength: + Enabled: false + +Metrics/ClassLength: + Enabled: false + +Metrics/MethodLength: + Enabled: false + +Metrics/ParameterLists: + Enabled: false + +Metrics/PerceivedComplexity: + Enabled: false + +Metrics/CyclomaticComplexity: + Enabled: false + +Metrics/ModuleLength: + Enabled: false + +Layout/LineLength: + Enabled: false + +Naming/VariableNumber: + EnforcedStyle: snake_case + +Style/Documentation: + Enabled: false + +Style/Lambda: + EnforcedStyle: literal + +Minitest/MultipleAssertions: + Enabled: false + +Minitest/UselessAssertion: + Enabled: false diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/Gemfile b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/Gemfile new file mode 100644 index 000000000000..29b144d77f48 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/Gemfile @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + +gemspec + +group :test, :development do + gem "rake", "~> 13.0" + + gem "minitest", "~> 5.16" + gem "minitest-rg" + + gem "rubocop", "~> 1.21" + gem "rubocop-minitest" + + gem "pry" + + gem "webmock" +end + +# Load custom Gemfile configuration if it exists +custom_gemfile = File.join(__dir__, "Gemfile.custom") +eval_gemfile(custom_gemfile) if File.exist?(custom_gemfile) diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/Gemfile.custom b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/Gemfile.custom new file mode 100644 index 000000000000..11bdfaf13f2d --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/Gemfile.custom @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +# Custom Gemfile configuration file +# This file is automatically loaded by the main Gemfile. You can add custom gems, +# groups, or other Gemfile configurations here. If you do make changes to this file, +# you will need to add it to the .fernignore file to prevent your changes from being +# overwritten by the generator. + +# Example usage: +# group :test, :development do +# gem 'custom-gem', '~> 2.0' +# end + +# Add your custom gem dependencies here \ No newline at end of file diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/README.md b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/README.md new file mode 100644 index 000000000000..107649607431 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/README.md @@ -0,0 +1,95 @@ +# Seed Ruby Library + +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FRuby) + +The Seed Ruby library provides convenient access to the Seed APIs from Ruby. + +## Table of Contents + +- [Reference](#reference) +- [Usage](#usage) +- [Environments](#environments) +- [Errors](#errors) +- [Advanced](#advanced) + - [Timeouts](#timeouts) +- [Contributing](#contributing) + +## Reference + +A full reference for this library is available [here](./reference.md). + +## Usage + +Instantiate and use the client with the following: + +```ruby +require "seed" + +client = Seed::Client.new(); + +client.auth.get_token(api_key: 'api_key'); +``` + +## Environments + +This SDK allows you to configure different custom URLs for API requests. You can specify your own custom URL. + +### Custom URL +```ruby +require "seed" + +client = Seed::Client.new( + base_url: "https://example.com" +) +``` + +## Errors + +Failed API calls will raise errors that can be rescued from granularly. + +```ruby +require "seed" + +client = Seed::Client.new( + base_url: "https://example.com" +) + +begin + result = client.auth.get_token +rescue Seed::Errors::TimeoutError + puts "API didn't respond before our timeout elapsed" +rescue Seed::Errors::ServiceUnavailableError + puts "API returned status 503, is probably overloaded, try again later" +rescue Seed::Errors::ServerError + puts "API returned some other 5xx status, this is probably a bug" +rescue Seed::Errors::ResponseError => e + puts "API returned an unexpected status other than 5xx: #{e.code} #{e.message}" +rescue Seed::Errors::ApiError => e + puts "Some other error occurred when calling the API: #{e.message}" +end +``` + +## Advanced + +### Timeouts + +The SDK defaults to a 60 second timeout. Use the `timeout` option to configure this behavior. + +```ruby +require "seed" + +response = client.auth.get_token( + ..., + timeout: 30 # 30 second timeout +) +``` + +## Contributing + +While we value open-source contributions to this SDK, this library is generated programmatically. +Additions made directly to this library would have to be moved over to our generation code, +otherwise they would be overwritten upon the next generated release. Feel free to open a PR as +a proof of concept, but know that we will not be able to merge it as-is. We suggest opening +an issue first to discuss with us! + +On the other hand, contributions to the README are always very welcome! \ No newline at end of file diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/Rakefile b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/Rakefile new file mode 100644 index 000000000000..9bdd4a6ce80b --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/Rakefile @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +require "bundler/gem_tasks" +require "minitest/test_task" + +Minitest::TestTask.create + +require "rubocop/rake_task" + +RuboCop::RakeTask.new + +task default: %i[test] + +task lint: %i[rubocop] + +# Run only the custom test file +Minitest::TestTask.create(:customtest) do |t| + t.libs << "test" + t.test_globs = ["test/custom.test.rb"] +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/custom.gemspec.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/custom.gemspec.rb new file mode 100644 index 000000000000..86d8efd3cd3c --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/custom.gemspec.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +# Custom gemspec configuration file +# This file is automatically loaded by the main gemspec file. The 'spec' variable is available +# in this context from the main gemspec file. You can modify this file to add custom metadata, +# dependencies, or other gemspec configurations. If you do make changes to this file, you will +# need to add it to the .fernignore file to prevent your changes from being overwritten. + +def add_custom_gemspec_data(spec) + # Example custom configurations (uncomment and modify as needed) + + # spec.authors = ["Your name"] + # spec.email = ["your.email@example.com"] + # spec.homepage = "https://github.com/your-org/seed-ruby" + # spec.license = "Your license" +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/dynamic-snippets/example0/snippet.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/dynamic-snippets/example0/snippet.rb new file mode 100644 index 000000000000..a2dc0a1fd0bc --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/dynamic-snippets/example0/snippet.rb @@ -0,0 +1,5 @@ +require "seed" + +client = Seed::Client.new(base_url: 'https://api.fern.com'); + +client.auth.get_token(api_key: 'api_key'); diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/dynamic-snippets/example1/snippet.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/dynamic-snippets/example1/snippet.rb new file mode 100644 index 000000000000..ea8d55d2c117 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/dynamic-snippets/example1/snippet.rb @@ -0,0 +1,5 @@ +require "seed" + +client = Seed::Client.new(base_url: 'https://api.fern.com'); + +client.nested_no_auth.api.get_something(); diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/dynamic-snippets/example2/snippet.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/dynamic-snippets/example2/snippet.rb new file mode 100644 index 000000000000..29c334d11e52 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/dynamic-snippets/example2/snippet.rb @@ -0,0 +1,5 @@ +require "seed" + +client = Seed::Client.new(base_url: 'https://api.fern.com'); + +client.nested.api.get_something(); diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/dynamic-snippets/example3/snippet.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/dynamic-snippets/example3/snippet.rb new file mode 100644 index 000000000000..2040da7289cc --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/dynamic-snippets/example3/snippet.rb @@ -0,0 +1,5 @@ +require "seed" + +client = Seed::Client.new(base_url: 'https://api.fern.com'); + +client.simple.get_something(); diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed.rb new file mode 100644 index 000000000000..5ec12183dffd --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +require "json" +require "net/http" +require "securerandom" + +require_relative "seed/internal/json/serializable" +require_relative "seed/internal/types/type" +require_relative "seed/internal/types/utils" +require_relative "seed/internal/types/union" +require_relative "seed/internal/errors/constraint_error" +require_relative "seed/internal/errors/type_error" +require_relative "seed/internal/http/base_request" +require_relative "seed/internal/json/request" +require_relative "seed/internal/http/raw_client" +require_relative "seed/internal/multipart/multipart_encoder" +require_relative "seed/internal/multipart/multipart_form_data_part" +require_relative "seed/internal/multipart/multipart_form_data" +require_relative "seed/internal/multipart/multipart_request" +require_relative "seed/internal/types/model/field" +require_relative "seed/internal/types/model" +require_relative "seed/internal/types/array" +require_relative "seed/internal/types/boolean" +require_relative "seed/internal/types/enum" +require_relative "seed/internal/types/hash" +require_relative "seed/internal/types/unknown" +require_relative "seed/errors/api_error" +require_relative "seed/errors/response_error" +require_relative "seed/errors/client_error" +require_relative "seed/errors/redirect_error" +require_relative "seed/errors/server_error" +require_relative "seed/errors/timeout_error" +require_relative "seed/internal/iterators/item_iterator" +require_relative "seed/internal/iterators/cursor_item_iterator" +require_relative "seed/internal/iterators/offset_item_iterator" +require_relative "seed/internal/iterators/cursor_page_iterator" +require_relative "seed/internal/iterators/offset_page_iterator" +require_relative "seed/auth/types/token_response" +require_relative "seed/client" +require_relative "seed/auth/client" +require_relative "seed/auth/types/get_token_request" +require_relative "seed/nested_no_auth/client" +require_relative "seed/nested_no_auth/api/client" +require_relative "seed/nested/client" +require_relative "seed/nested/api/client" +require_relative "seed/simple/client" +require_relative "seed/internal/inferred_auth_provider" diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/auth/client.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/auth/client.rb new file mode 100644 index 000000000000..8382d06db36f --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/auth/client.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +module Seed + module Auth + class Client + # @param client [Seed::Internal::Http::RawClient] + # + # @return [void] + def initialize(client:) + @client = client + end + + # @param request_options [Hash] + # @param params [Hash] + # @option request_options [String] :base_url + # @option request_options [Hash{String => Object}] :additional_headers + # @option request_options [Hash{String => Object}] :additional_query_parameters + # @option request_options [Hash{String => Object}] :additional_body_parameters + # @option request_options [Integer] :timeout_in_seconds + # @option params [String] :api_key + # + # @return [Seed::Auth::Types::TokenResponse] + def get_token(request_options: {}, **_params) + request = Seed::Internal::JSON::Request.new( + base_url: request_options[:base_url], + method: "POST", + path: "/token", + request_options: request_options + ) + begin + response = @client.send(request) + rescue Net::HTTPRequestTimeout + raise Seed::Errors::TimeoutError + end + code = response.code.to_i + if code.between?(200, 299) + Seed::Auth::Types::TokenResponse.load(response.body) + else + error_class = Seed::Errors::ResponseError.subclass_for_code(code) + raise error_class.new(response.body, code: code) + end + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/auth/types/get_token_request.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/auth/types/get_token_request.rb new file mode 100644 index 000000000000..9939319f710a --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/auth/types/get_token_request.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Seed + module Auth + module Types + class GetTokenRequest < Internal::Types::Model + field :api_key, -> { String }, optional: false, nullable: false, api_name: "X-Api-Key" + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/auth/types/token_response.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/auth/types/token_response.rb new file mode 100644 index 000000000000..2efb830e2ebb --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/auth/types/token_response.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Seed + module Auth + module Types + # An auth token response. + class TokenResponse < Internal::Types::Model + field :access_token, -> { String }, optional: false, nullable: false + field :token_type, -> { String }, optional: false, nullable: false + field :expires_in, -> { Integer }, optional: false, nullable: false + field :scope, -> { String }, optional: true, nullable: false + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/client.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/client.rb new file mode 100644 index 000000000000..7577d94a2f21 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/client.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +module Seed + class Client + # @param base_url [String, nil] + # @param api_key [String] + # + # @return [void] + def initialize(base_url:, api_key:) + # Create an unauthenticated client for the auth endpoint + auth_raw_client = Seed::Internal::Http::RawClient.new( + base_url: base_url, + headers: { + "X-Fern-Language" => "Ruby", + "X-Api-Key" => api_key + } + ) + + # Create the auth client for token retrieval + auth_client = Seed::Auth::Client.new(client: auth_raw_client) + + # Create the auth provider with the auth client and credentials + @auth_provider = Seed::Internal::InferredAuthProvider.new( + auth_client: auth_client, + options: { base_url: base_url, api_key: api_key } + ) + + @raw_client = Seed::Internal::Http::RawClient.new( + base_url: base_url, + headers: { + "User-Agent" => "fern_inferred-auth-implicit-api-key/0.0.1", + "X-Fern-Language" => "Ruby" + }.merge(@auth_provider.auth_headers) + ) + end + + # @return [Seed::Auth::Client] + def auth + @auth ||= Seed::Auth::Client.new(client: @raw_client) + end + + # @return [Seed::NestedNoAuth::Client] + def nested_no_auth + @nested_no_auth ||= Seed::NestedNoAuth::Client.new(client: @raw_client) + end + + # @return [Seed::Nested::Client] + def nested + @nested ||= Seed::Nested::Client.new(client: @raw_client) + end + + # @return [Seed::Simple::Client] + def simple + @simple ||= Seed::Simple::Client.new(client: @raw_client) + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/errors/api_error.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/errors/api_error.rb new file mode 100644 index 000000000000..b8ba53889b36 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/errors/api_error.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module Seed + module Errors + class ApiError < StandardError + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/errors/client_error.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/errors/client_error.rb new file mode 100644 index 000000000000..c3c6033641e2 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/errors/client_error.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module Seed + module Errors + class ClientError < ResponseError + end + + class UnauthorizedError < ClientError + end + + class ForbiddenError < ClientError + end + + class NotFoundError < ClientError + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/errors/redirect_error.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/errors/redirect_error.rb new file mode 100644 index 000000000000..f663c01e7615 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/errors/redirect_error.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module Seed + module Errors + class RedirectError < ResponseError + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/errors/response_error.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/errors/response_error.rb new file mode 100644 index 000000000000..beb4a1baf959 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/errors/response_error.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +module Seed + module Errors + class ResponseError < ApiError + attr_reader :code + + def initialize(msg, code:) + @code = code + super(msg) + end + + def inspect + "#<#{self.class.name} @code=#{code} @body=#{message}>" + end + + # Returns the most appropriate error class for the given code. + # + # @return [Class] + def self.subclass_for_code(code) + case code + when 300..399 + RedirectError + when 401 + UnauthorizedError + when 403 + ForbiddenError + when 404 + NotFoundError + when 400..499 + ClientError + when 503 + ServiceUnavailableError + when 500..599 + ServerError + else + ResponseError + end + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/errors/server_error.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/errors/server_error.rb new file mode 100644 index 000000000000..1838027cdeab --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/errors/server_error.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Seed + module Errors + class ServerError < ResponseError + end + + class ServiceUnavailableError < ApiError + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/errors/timeout_error.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/errors/timeout_error.rb new file mode 100644 index 000000000000..ec3a24bb7e96 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/errors/timeout_error.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module Seed + module Errors + class TimeoutError < ApiError + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/errors/constraint_error.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/errors/constraint_error.rb new file mode 100644 index 000000000000..e2f0bd66ac37 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/errors/constraint_error.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module Seed + module Internal + module Errors + class ConstraintError < StandardError + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/errors/type_error.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/errors/type_error.rb new file mode 100644 index 000000000000..6aec80f59f05 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/errors/type_error.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module Seed + module Internal + module Errors + class TypeError < StandardError + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/http/base_request.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/http/base_request.rb new file mode 100644 index 000000000000..5f65f1327023 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/http/base_request.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Seed + module Internal + module Http + # @api private + class BaseRequest + attr_reader :base_url, :path, :method, :headers, :query, :request_options + + # @param base_url [String] The base URL for the request + # @param path [String] The path for the request + # @param method [String] The HTTP method for the request (:get, :post, etc.) + # @param headers [Hash] Additional headers for the request (optional) + # @param query [Hash] Query parameters for the request (optional) + # @param request_options [Seed::RequestOptions, Hash{Symbol=>Object}, nil] + def initialize(base_url:, path:, method:, headers: {}, query: {}, request_options: {}) + @base_url = base_url + @path = path + @method = method + @headers = headers + @query = query + @request_options = request_options + end + + # Child classes should implement: + # - encode_headers: Returns the encoded HTTP request headers. + # - encode_body: Returns the encoded HTTP request body. + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/http/raw_client.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/http/raw_client.rb new file mode 100644 index 000000000000..3370a92b608b --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/http/raw_client.rb @@ -0,0 +1,106 @@ +# frozen_string_literal: true + +module Seed + module Internal + module Http + # @api private + class RawClient + # @param base_url [String] The base url for the request. + # @param max_retries [Integer] The number of times to retry a failed request, defaults to 2. + # @param timeout [Float] The timeout for the request, defaults to 60.0 seconds. + # @param headers [Hash] The headers for the request. + def initialize(base_url:, max_retries: 2, timeout: 60.0, headers: {}) + @base_url = base_url + @max_retries = max_retries + @timeout = timeout + @default_headers = { + "X-Fern-Language": "Ruby", + "X-Fern-SDK-Name": "seed", + "X-Fern-SDK-Version": "0.0.1" + }.merge(headers) + end + + # @param request [Seed::Internal::Http::BaseRequest] The HTTP request. + # @return [HTTP::Response] The HTTP response. + def send(request) + url = build_url(request) + + http_request = build_http_request( + url:, + method: request.method, + headers: request.encode_headers, + body: request.encode_body + ) + + conn = connect(url) + conn.open_timeout = @timeout + conn.read_timeout = @timeout + conn.write_timeout = @timeout + conn.continue_timeout = @timeout + + conn.request(http_request) + end + + # @param request [Seed::Internal::Http::BaseRequest] The HTTP request. + # @return [URI::Generic] The URL. + def build_url(request) + path = request.path.start_with?("/") ? request.path[1..] : request.path + base = request.base_url || @base_url + url = "#{base.chomp("/")}/#{path}" + url = "#{url}?#{encode_query(request.query)}" if request.query&.any? + URI.parse(url) + end + + # @param url [URI::Generic] The url to the resource. + # @param method [String] The HTTP method to use. + # @param headers [Hash] The headers for the request. + # @param body [String, nil] The body for the request. + # @return [HTTP::Request] The HTTP request. + def build_http_request(url:, method:, headers: {}, body: nil) + request = Net::HTTPGenericRequest.new( + method, + !body.nil?, + method != "HEAD", + url + ) + + request_headers = @default_headers.merge(headers) + request_headers.each { |name, value| request[name] = value } + request.body = body if body + + request + end + + # @param query [Hash] The query for the request. + # @return [String, nil] The encoded query. + def encode_query(query) + query.to_h.empty? ? nil : URI.encode_www_form(query) + end + + # @param url [URI::Generic] The url to connect to. + # @return [Net::HTTP] The HTTP connection. + def connect(url) + is_https = (url.scheme == "https") + + port = if url.port + url.port + elsif is_https + Net::HTTP.https_default_port + else + Net::HTTP.http_default_port + end + + http = Net::HTTP.new(url.host, port) + http.use_ssl = is_https + http.max_retries = @max_retries + http + end + + # @return [String] + def inspect + "#<#{self.class.name}:0x#{object_id.to_s(16)} @base_url=#{@base_url.inspect}>" + end + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/inferred_auth_provider.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/inferred_auth_provider.rb new file mode 100644 index 000000000000..6a57817fd1c8 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/inferred_auth_provider.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +module Seed + module Internal + class InferredAuthProvider + BUFFER_IN_SECONDS = 120 # 2 minutes + + # @param auth_client [untyped] + # @param options [Hash[String, untyped]] + # + # @return [void] + def initialize(auth_client:, options:) + @auth_client = auth_client + @options = options + @access_token = nil + @expires_at = nil + end + + # Returns a cached access token, refreshing if necessary. + # Refreshes the token if it's nil, or if we're within the buffer period before expiration. + # + # @return [String] + def token + return refresh if @access_token.nil? || token_needs_refresh? + + @access_token + end + + # Returns the authentication headers to be included in requests. + # + # @return [Hash[String, String]] + def auth_headers + access_token = token + { + "Authorization" => "Bearer #{access_token}" + } + end + # Checks if the token needs to be refreshed. + # Returns true if the token will expire within the buffer period. + # + # @return [Boolean] + private def token_needs_refresh? + return true if @expires_at.nil? + + Time.now >= (@expires_at - BUFFER_IN_SECONDS) + end + # Refreshes the access token by calling the token endpoint. + # + # @return [String] + private def refresh + request_params = { + api_key: @options[:api_key] + } + + token_response = @auth_client.get_token(**request_params, request_options: { base_url: @options[:base_url] }) + + @access_token = token_response.access_token + @expires_at = Time.now + token_response.expires_in + + @access_token + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/iterators/cursor_item_iterator.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/iterators/cursor_item_iterator.rb new file mode 100644 index 000000000000..ab627ffc7025 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/iterators/cursor_item_iterator.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module Seed + module Internal + class CursorItemIterator < ItemIterator + # Instantiates a CursorItemIterator, an Enumerable class which wraps calls to a cursor-based paginated API and yields individual items from it. + # + # @param initial_cursor [String] The initial cursor to use when iterating, if any. + # @param cursor_field [Symbol] The field in API responses to extract the next cursor from. + # @param item_field [Symbol] The field in API responses to extract the items to iterate over. + # @param block [Proc] A block which is responsible for receiving a cursor to use and returning the given page from the API. + # @return [Seed::Internal::CursorItemIterator] + def initialize(initial_cursor:, cursor_field:, item_field:, &) + super() + @item_field = item_field + @page_iterator = CursorPageIterator.new(initial_cursor:, cursor_field:, &) + @page = nil + end + + # Returns the CursorPageIterator mediating access to the underlying API. + # + # @return [Seed::Internal::CursorPageIterator] + def pages + @page_iterator + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/iterators/cursor_page_iterator.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/iterators/cursor_page_iterator.rb new file mode 100644 index 000000000000..f479a749fef9 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/iterators/cursor_page_iterator.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +module Seed + module Internal + class CursorPageIterator + include Enumerable + + # Instantiates a CursorPageIterator, an Enumerable class which wraps calls to a cursor-based paginated API and yields pages of items. + # + # @param initial_cursor [String] The initial cursor to use when iterating, if any. + # @param cursor_field [Symbol] The name of the field in API responses to extract the next cursor from. + # @param block [Proc] A block which is responsible for receiving a cursor to use and returning the given page from the API. + # @return [Seed::Internal::CursorPageIterator] + def initialize(initial_cursor:, cursor_field:, &block) + @need_initial_load = initial_cursor.nil? + @cursor = initial_cursor + @cursor_field = cursor_field + @get_next_page = block + end + + # Iterates over each page returned by the API. + # + # @param block [Proc] The block which each retrieved page is yielded to. + # @return [NilClass] + def each(&block) + while (page = next_page) + block.call(page) + end + end + + # Whether another page will be available from the API. + # + # @return [Boolean] + def next? + @need_initial_load || !@cursor.nil? + end + + # Retrieves the next page from the API. + # + # @return [Boolean] + def next_page + return if !@need_initial_load && @cursor.nil? + + @need_initial_load = false + fetched_page = @get_next_page.call(@cursor) + @cursor = fetched_page.send(@cursor_field) + fetched_page + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/iterators/item_iterator.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/iterators/item_iterator.rb new file mode 100644 index 000000000000..1284fb0fd367 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/iterators/item_iterator.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +module Seed + module Internal + class ItemIterator + include Enumerable + + # Iterates over each item returned by the API. + # + # @param block [Proc] The block which each retrieved item is yielded to. + # @return [NilClass] + def each(&block) + while (item = next_element) + block.call(item) + end + end + + # Whether another item will be available from the API. + # + # @return [Boolean] + def next? + load_next_page if @page.nil? + return false if @page.nil? + + return true if any_items_in_cached_page? + + load_next_page + any_items_in_cached_page? + end + + # Retrieves the next item from the API. + def next_element + item = next_item_from_cached_page + return item if item + + load_next_page + next_item_from_cached_page + end + + private + + def next_item_from_cached_page + return unless @page + + @page.send(@item_field).shift + end + + def any_items_in_cached_page? + return false unless @page + + !@page.send(@item_field).empty? + end + + def load_next_page + @page = @page_iterator.next_page + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/iterators/offset_item_iterator.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/iterators/offset_item_iterator.rb new file mode 100644 index 000000000000..f8840246686d --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/iterators/offset_item_iterator.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +module Seed + module Internal + class OffsetItemIterator < ItemIterator + # Instantiates an OffsetItemIterator, an Enumerable class which wraps calls to an offset-based paginated API and yields the individual items from it. + # + # @param initial_page [Integer] The initial page or offset to start from when iterating. + # @param item_field [Symbol] The name of the field in API responses to extract the items to iterate over. + # @param has_next_field [Symbol] The name of the field in API responses containing a boolean of whether another page exists. + # @param step [Boolean] If true, treats the page number as a true offset (i.e. increments the page number by the number of items returned from each call rather than just 1) + # @param block [Proc] A block which is responsible for receiving a page number to use and returning the given page from the API. + # + # @return [Seed::Internal::OffsetItemIterator] + def initialize(initial_page:, item_field:, has_next_field:, step:, &) + super() + @item_field = item_field + @page_iterator = OffsetPageIterator.new(initial_page:, item_field:, has_next_field:, step:, &) + @page = nil + end + + # Returns the OffsetPageIterator that is mediating access to the underlying API. + # + # @return [Seed::Internal::OffsetPageIterator] + def pages + @page_iterator + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/iterators/offset_page_iterator.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/iterators/offset_page_iterator.rb new file mode 100644 index 000000000000..051b65c5774c --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/iterators/offset_page_iterator.rb @@ -0,0 +1,83 @@ +# frozen_string_literal: true + +module Seed + module Internal + class OffsetPageIterator + include Enumerable + + # Instantiates an OffsetPageIterator, an Enumerable class which wraps calls to an offset-based paginated API and yields pages of items from it. + # + # @param initial_page [Integer] The initial page to use when iterating, if any. + # @param item_field [Symbol] The field to pull the list of items to iterate over. + # @param has_next_field [Symbol] The field to pull the boolean of whether a next page exists from, if any. + # @param step [Boolean] If true, treats the page number as a true offset (i.e. increments the page number by the number of items returned from each call rather than just 1) + # @param block [Proc] A block which is responsible for receiving a page number to use and returning the given page from the API. + # @return [Seed::Internal::OffsetPageIterator] + def initialize(initial_page:, item_field:, has_next_field:, step:, &block) + @page_number = initial_page || (step ? 0 : 1) + @item_field = item_field + @has_next_field = has_next_field + @step = step + @get_next_page = block + + # A cache of whether the API has another page, if it gives us that information... + @next_page = nil + # ...or the actual next page, preloaded, if it doesn't. + @has_next_page = nil + end + + # Iterates over each page returned by the API. + # + # @param block [Proc] The block which each retrieved page is yielded to. + # @return [NilClass] + def each(&block) + while (page = next_page) + block.call(page) + end + end + + # Whether another page will be available from the API. + # + # @return [Boolean] + def next? + return @has_next_page unless @has_next_page.nil? + return true if @next_page + + fetched_page = @get_next_page.call(@page_number) + fetched_page_items = fetched_page&.send(@item_field) + if fetched_page_items.nil? || fetched_page_items.empty? + @has_next_page = false + else + @next_page = fetched_page + true + end + end + + # Returns the next page from the API. + def next_page + return nil if @page_number.nil? + + if @next_page + this_page = @next_page + @next_page = nil + else + this_page = @get_next_page.call(@page_number) + end + + @has_next_page = this_page&.send(@has_next_field) if @has_next_field + + items = this_page.send(@item_field) + if items.nil? || items.empty? + @page_number = nil + return nil + elsif @step + @page_number += items.length + else + @page_number += 1 + end + + this_page + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/json/request.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/json/request.rb new file mode 100644 index 000000000000..15773d44c641 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/json/request.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +module Seed + module Internal + module JSON + # @api private + class Request < Seed::Internal::Http::BaseRequest + attr_reader :body + + # @param base_url [String] The base URL for the request + # @param path [String] The path for the request + # @param method [Symbol] The HTTP method for the request (:get, :post, etc.) + # @param headers [Hash] Additional headers for the request (optional) + # @param query [Hash] Query parameters for the request (optional) + # @param body [Object, nil] The JSON request body (optional) + # @param request_options [Seed::RequestOptions, Hash{Symbol=>Object}, nil] + def initialize(base_url:, path:, method:, headers: {}, query: {}, body: nil, request_options: {}) + super(base_url:, path:, method:, headers:, query:, request_options:) + + @body = body + end + + # @return [Hash] The encoded HTTP request headers. + def encode_headers + additional_headers = @request_options&.dig(:additional_headers) || @request_options&.dig("additional_headers") || {} + { + "Content-Type" => "application/json", + "Accept" => "application/json" + }.merge(@headers).merge(additional_headers) + end + + # @return [String, nil] The encoded HTTP request body. + def encode_body + @body.nil? ? nil : ::JSON.generate(@body) + end + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/json/serializable.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/json/serializable.rb new file mode 100644 index 000000000000..f80a15fb962c --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/json/serializable.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Seed + module Internal + module JSON + module Serializable + # Loads data from JSON into its deserialized form + # + # @param str [String] Raw JSON to load into an object + # @return [Object] + def load(str) + raise NotImplementedError + end + + # Dumps data from its deserialized form into JSON + # + # @param value [Object] The deserialized value + # @return [String] + def dump(value) + raise NotImplementedError + end + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/multipart/multipart_encoder.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/multipart/multipart_encoder.rb new file mode 100644 index 000000000000..307ad7436a57 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/multipart/multipart_encoder.rb @@ -0,0 +1,141 @@ +# frozen_string_literal: true + +module Seed + module Internal + module Multipart + # Encodes parameters into a `multipart/form-data` payload as described by RFC + # 2388: + # + # https://tools.ietf.org/html/rfc2388 + # + # This is most useful for transferring file-like objects. + # + # Parameters should be added with `#encode`. When ready, use `#body` to get + # the encoded result and `#content_type` to get the value that should be + # placed in the `Content-Type` header of a subsequent request (which includes + # a boundary value). + # + # This abstraction is heavily inspired by Stripe's multipart/form-data implementation, + # which can be found here: + # + # https://github.com/stripe/stripe-ruby/blob/ca00b676f04ac421cf5cb5ff0325f243651677b6/lib/stripe/multipart_encoder.rb#L18 + # + # @api private + class Encoder + CONTENT_TYPE = "multipart/form-data" + CRLF = "\r\n" + + attr_reader :boundary, :body + + def initialize + # Chose the same number of random bytes that Go uses in its standard + # library implementation. Easily enough entropy to ensure that it won't + # be present in a file we're sending. + @boundary = SecureRandom.hex(30) + + @body = String.new + @closed = false + @first_field = true + end + + # Gets the content type string including the boundary. + # + # @return [String] The content type with boundary + def content_type + "#{CONTENT_TYPE}; boundary=#{@boundary}" + end + + # Encode the given FormData object into a multipart/form-data payload. + # + # @param form_data [FormData] The form data to encode + # @return [String] The encoded body. + def encode(form_data) + return "" if form_data.parts.empty? + + form_data.parts.each do |part| + write_part(part) + end + close + + @body + end + + # Writes a FormDataPart to the encoder. + # + # @param part [FormDataPart] The part to write + # @return [nil] + def write_part(part) + raise "Cannot write to closed encoder" if @closed + + write_field( + name: part.name, + data: part.contents, + filename: part.filename, + headers: part.headers + ) + + nil + end + + # Writes a field to the encoder. + # + # @param name [String] The field name + # @param data [String] The field data + # @param filename [String, nil] Optional filename + # @param headers [Hash, nil] Optional additional headers + # @return [nil] + def write_field(name:, data:, filename: nil, headers: nil) + raise "Cannot write to closed encoder" if @closed + + if @first_field + @first_field = false + else + @body << CRLF + end + + @body << "--#{@boundary}#{CRLF}" + @body << %(Content-Disposition: form-data; name="#{escape(name.to_s)}") + @body << %(; filename="#{escape(filename)}") if filename + @body << CRLF + + if headers + headers.each do |key, value| + @body << "#{key}: #{value}#{CRLF}" + end + elsif filename + # Default content type for files. + @body << "Content-Type: application/octet-stream#{CRLF}" + end + + @body << CRLF + @body << data.to_s + + nil + end + + # Finalizes the encoder by writing the final boundary. + # + # @return [nil] + def close + raise "Encoder already closed" if @closed + + @body << CRLF + @body << "--#{@boundary}--" + @closed = true + + nil + end + + private + + # Escapes quotes for use in header values and replaces line breaks with spaces. + # + # @param str [String] The string to escape + # @return [String] The escaped string + def escape(str) + str.to_s.gsub('"', "%22").tr("\n", " ").tr("\r", " ") + end + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/multipart/multipart_form_data.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/multipart/multipart_form_data.rb new file mode 100644 index 000000000000..5be1bb25341f --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/multipart/multipart_form_data.rb @@ -0,0 +1,78 @@ +# frozen_string_literal: true + +module Seed + module Internal + module Multipart + # @api private + class FormData + # @return [Array] The parts in this multipart form data. + attr_reader :parts + + # @return [Encoder] The encoder for this multipart form data. + private attr_reader :encoder + + def initialize + @encoder = Encoder.new + @parts = [] + end + + # Adds a new part to the multipart form data. + # + # @param name [String] The name of the form field + # @param value [String, Integer, Float, Boolean, #read] The value of the field + # @param content_type [String, nil] Optional content type + # @return [self] Returns self for chaining + def add(name:, value:, content_type: nil) + headers = content_type ? { "Content-Type" => content_type } : nil + add_part(FormDataPart.new(name:, value:, headers:)) + end + + # Adds a file to the multipart form data. + # + # @param name [String] The name of the form field + # @param file [#read] The file or readable object + # @param filename [String, nil] Optional filename (defaults to basename of path for File objects) + # @param content_type [String, nil] Optional content type (e.g. "image/png") + # @return [self] Returns self for chaining + def add_file(name:, file:, filename: nil, content_type: nil) + headers = content_type ? { "Content-Type" => content_type } : nil + filename ||= filename_for(file) + add_part(FormDataPart.new(name:, value: file, filename:, headers:)) + end + + # Adds a pre-created part to the multipart form data. + # + # @param part [FormDataPart] The part to add + # @return [self] Returns self for chaining + def add_part(part) + @parts << part + self + end + + # Gets the content type string including the boundary. + # + # @return [String] The content type with boundary. + def content_type + @encoder.content_type + end + + # Encode the multipart form data into a multipart/form-data payload. + # + # @return [String] The encoded body. + def encode + @encoder.encode(self) + end + + private + + def filename_for(file) + if file.is_a?(::File) || file.respond_to?(:path) + ::File.basename(file.path) + elsif file.respond_to?(:name) + file.name + end + end + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/multipart/multipart_form_data_part.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/multipart/multipart_form_data_part.rb new file mode 100644 index 000000000000..de45416ee087 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/multipart/multipart_form_data_part.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require "securerandom" + +module Seed + module Internal + module Multipart + # @api private + class FormDataPart + attr_reader :name, :contents, :filename, :headers + + # @param name [String] The name of the form field + # @param value [String, Integer, Float, Boolean, File, #read] The value of the field + # @param filename [String, nil] Optional filename for file uploads + # @param headers [Hash, nil] Optional additional headers + def initialize(name:, value:, filename: nil, headers: nil) + @name = name + @contents = convert_to_content(value) + @filename = filename + @headers = headers + end + + # Converts the part to a hash suitable for serialization. + # + # @return [Hash] A hash representation of the part + def to_hash + result = { + name: @name, + contents: @contents + } + result[:filename] = @filename if @filename + result[:headers] = @headers if @headers + result + end + + private + + # Converts various types of values to a content representation + # @param value [String, Integer, Float, Boolean, #read] The value to convert + # @return [String] The string representation of the value + def convert_to_content(value) + if value.respond_to?(:read) + value.read + else + value.to_s + end + end + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/multipart/multipart_request.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/multipart/multipart_request.rb new file mode 100644 index 000000000000..915dada8c56e --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/multipart/multipart_request.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +module Seed + module Internal + module Multipart + # @api private + class Request < Seed::Internal::Http::BaseRequest + attr_reader :body + + # @param base_url [String] The base URL for the request + # @param path [String] The path for the request + # @param method [Symbol] The HTTP method for the request (:get, :post, etc.) + # @param headers [Hash] Additional headers for the request (optional) + # @param query [Hash] Query parameters for the request (optional) + # @param body [MultipartFormData, nil] The multipart form data for the request (optional) + # @param request_options [Seed::RequestOptions, Hash{Symbol=>Object}, nil] + def initialize(base_url:, path:, method:, headers: {}, query: {}, body: nil, request_options: {}) + super(base_url:, path:, method:, headers:, query:, request_options:) + + @body = body + end + + # @return [Hash] The encoded HTTP request headers. + def encode_headers + additional_headers = @request_options&.dig(:additional_headers) || @request_options&.dig("additional_headers") || {} + { + "Content-Type" => @body.content_type + }.merge(@headers).merge(additional_headers) + end + + # @return [String, nil] The encoded HTTP request body. + def encode_body + @body&.encode + end + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/types/array.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/types/array.rb new file mode 100644 index 000000000000..f3c7c1bd9549 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/types/array.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +module Seed + module Internal + module Types + # An array of a specific type + class Array + include Seed::Internal::Types::Type + + attr_reader :type + + class << self + # Instantiates a new `Array` of a given type + # + # @param type [Object] The member type of this array + # + # @return [Seed::Internal::Types::Array] + def [](type) + new(type) + end + end + + # @api private + def initialize(type) + @type = type + end + + # Coerces a value into this array + # + # @param value [Object] + # @option strict [Boolean] + # @return [::Array] + def coerce(value, strict: strict?) + unless value.is_a?(::Array) + raise Errors::TypeError, "cannot coerce `#{value.class}` to Array<#{type}>" if strict + + return value + end + + value.map do |element| + Utils.coerce(type, element, strict: strict) + end + end + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/types/boolean.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/types/boolean.rb new file mode 100644 index 000000000000..de0503d9e56b --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/types/boolean.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +module Seed + module Internal + module Types + module Boolean + extend Seed::Internal::Types::Union + + member TrueClass + member FalseClass + + # Overrides the base coercion method for enums to allow integer and string values to become booleans + # + # @param value [Object] + # @option strict [Boolean] + # @return [Object] + def self.coerce(value, strict: strict?) + case value + when TrueClass, FalseClass + value + when Integer + return value == 1 + when String + return %w[1 true].include?(value) + end + + raise Errors::TypeError, "cannot coerce `#{value.class}` to Boolean" if strict + + value + end + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/types/enum.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/types/enum.rb new file mode 100644 index 000000000000..72e45e4c1f27 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/types/enum.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +module Seed + module Internal + module Types + # Module for defining enums + module Enum + include Type + + # @api private + # + # @return [Array] + def values + @values ||= constants.map { |c| const_get(c) } + end + + # @api private + def finalize! + values + end + + # @api private + def strict? + @strict ||= false + end + + # @api private + def strict! + @strict = true + end + + def coerce(value, strict: strict?) + coerced_value = Utils.coerce(Symbol, value) + + return coerced_value if values.include?(coerced_value) + + raise Errors::TypeError, "`#{value}` not in enum #{self}" if strict + + value + end + + # Parse JSON string and coerce to the enum value + # + # @param str [String] JSON string to parse + # @return [String] The enum value + def load(str) + coerce(::JSON.parse(str)) + end + + def inspect + "#{name}[#{values.join(", ")}]" + end + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/types/hash.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/types/hash.rb new file mode 100644 index 000000000000..d8bffa63ac11 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/types/hash.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +module Seed + module Internal + module Types + class Hash + include Type + + attr_reader :key_type, :value_type + + class << self + def [](key_type, value_type) + new(key_type, value_type) + end + end + + def initialize(key_type, value_type) + @key_type = key_type + @value_type = value_type + end + + def coerce(value, strict: strict?) + unless value.is_a?(::Hash) + raise Errors::TypeError, "not hash" if strict + + return value + end + + value.to_h do |k, v| + [Utils.coerce(key_type, k, strict: strict), Utils.coerce(value_type, v, strict: strict)] + end + end + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/types/model.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/types/model.rb new file mode 100644 index 000000000000..9b1480c7333a --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/types/model.rb @@ -0,0 +1,200 @@ +# frozen_string_literal: true + +module Seed + module Internal + module Types + # @abstract + # + # An abstract model that all data objects will inherit from + class Model + include Type + + class << self + # The defined fields for this model + # + # @api private + # + # @return [Hash] + def fields + @fields ||= if self < Seed::Internal::Types::Model + superclass.fields.dup + else + {} + end + end + + # Any extra fields that have been created from instantiation + # + # @api private + # + # @return [Hash] + def extra_fields + @extra_fields ||= {} + end + + # Define a new field on this model + # + # @param name [Symbol] The name of the field + # @param type [Class] Type of the field + # @option optional [Boolean] If it is an optional field + # @option nullable [Boolean] If it is a nullable field + # @option api_name [Symbol, String] Name in the API of this field. When serializing/deserializing, will use + # this field name + # @return [void] + def field(name, type, optional: false, nullable: false, api_name: nil, default: nil) + add_field_definition(name: name, type: type, optional: optional, nullable: nullable, api_name: api_name, + default: default) + + define_accessor(name) + define_setter(name) + end + + # Define a new literal for this model + # + # @param name [Symbol] + # @param value [Object] + # @option api_name [Symbol, String] + # @return [void] + def literal(name, value, api_name: nil) + add_field_definition(name: name, type: value.class, optional: false, nullable: false, api_name: api_name, + value: value) + + define_accessor(name) + end + + # Adds a new field definition into the class's fields registry + # + # @api private + # + # @param name [Symbol] + # @param type [Class] + # @option optional [Boolean] + # @return [void] + private def add_field_definition(name:, type:, optional:, nullable:, api_name:, default: nil, value: nil) + fields[name.to_sym] = + Field.new(name: name, type: type, optional: optional, nullable: nullable, api_name: api_name, + value: value, default: default) + end + + # Adds a new field definition into the class's extra fields registry + # + # @api private + # + # @param name [Symbol] + # @param type [Class] + # @option required [Boolean] + # @option optional [Boolean] + # @return [void] + def add_extra_field_definition(name:, type:) + return if extra_fields.key?(name.to_sym) + + extra_fields[name.to_sym] = Field.new(name: name, type: type, optional: true, nullable: false) + + define_accessor(name) + define_setter(name) + end + + # @api private + private def define_accessor(name) + method_name = name.to_sym + + define_method(method_name) do + @data[name] + end + end + + # @api private + private def define_setter(name) + method_name = :"#{name}=" + + define_method(method_name) do |val| + @data[name] = val + end + end + + def coerce(value, strict: (respond_to?(:strict?) ? strict? : false)) # rubocop:disable Lint/UnusedMethodArgument + return value if value.is_a?(self) + + return value unless value.is_a?(::Hash) + + new(value) + end + + def load(str) + coerce(::JSON.parse(str, symbolize_names: true)) + end + + def ===(instance) + instance.class.ancestors.include?(self) + end + end + + # Creates a new instance of this model + # TODO: Should all this logic be in `#coerce` instead? + # + # @param values [Hash] + # @option strict [Boolean] + # @return [self] + def initialize(values = {}) + @data = {} + + values = Utils.symbolize_keys(values.dup) + + self.class.fields.each do |field_name, field| + value = values.delete(field.api_name.to_sym) || values.delete(field.api_name) || values.delete(field_name) + + field_value = value || (if field.literal? + field.value + elsif field.default + field.default + end) + + @data[field_name] = Utils.coerce(field.type, field_value) + end + + # Any remaining values in the input become extra fields + values.each do |name, value| + self.class.add_extra_field_definition(name: name, type: value.class) + + @data[name.to_sym] = value + end + end + + def to_h + self.class.fields.merge(self.class.extra_fields).each_with_object({}) do |(name, field), acc| + # If there is a value present in the data, use that value + # If there is a `nil` value present in the data, and it is optional but NOT nullable, exclude key altogether + # If there is a `nil` value present in the data, and it is optional and nullable, use the nil value + + value = @data[name] + + next if value.nil? && field.optional && !field.nullable + + if value.is_a?(::Array) + value = value.map { |item| item.respond_to?(:to_h) ? item.to_h : item } + elsif value.respond_to?(:to_h) + value = value.to_h + end + + acc[field.api_name] = value + end + end + + def ==(other) + self.class == other.class && to_h == other.to_h + end + + # @return [String] + def inspect + attrs = @data.map do |name, value| + field = self.class.fields[name] || self.class.extra_fields[name] + display_value = field&.sensitive? ? "[REDACTED]" : value.inspect + "#{name}=#{display_value}" + end + + "#<#{self.class.name}:0x#{object_id&.to_s(16)} #{attrs.join(" ")}>" + end + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/types/model/field.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/types/model/field.rb new file mode 100644 index 000000000000..6ce0186f6a5d --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/types/model/field.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +module Seed + module Internal + module Types + class Model + # Definition of a field on a model + class Field + SENSITIVE_FIELD_NAMES = %i[ + password secret token api_key apikey access_token refresh_token + client_secret client_id credential bearer authorization + ].freeze + + attr_reader :name, :type, :optional, :nullable, :api_name, :value, :default + + def initialize(name:, type:, optional: false, nullable: false, api_name: nil, value: nil, default: nil) + @name = name.to_sym + @type = type + @optional = optional + @nullable = nullable + @api_name = api_name || name.to_s + @value = value + @default = default + end + + def literal? + !value.nil? + end + + def sensitive? + SENSITIVE_FIELD_NAMES.include?(@name) || + SENSITIVE_FIELD_NAMES.any? { |sensitive| @name.to_s.include?(sensitive.to_s) } + end + end + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/types/type.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/types/type.rb new file mode 100644 index 000000000000..5866caf1dbda --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/types/type.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +module Seed + module Internal + module Types + # @abstract + module Type + include Seed::Internal::JSON::Serializable + + # Coerces a value to this type + # + # @param value [unknown] + # @option strict [Boolean] If we should strictly coerce this value + def coerce(value, strict: strict?) + raise NotImplementedError + end + + # Returns if strictness is on for this type, defaults to `false` + # + # @return [Boolean] + def strict? + @strict ||= false + end + + # Enable strictness by default for this type + # + # @return [void] + def strict! + @strict = true + self + end + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/types/union.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/types/union.rb new file mode 100644 index 000000000000..507565d2210d --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/types/union.rb @@ -0,0 +1,133 @@ +# frozen_string_literal: true + +module Seed + module Internal + module Types + # Define a union between two types + module Union + include Seed::Internal::Types::Type + + def members + @members ||= [] + end + + # Add a member to this union + # + # @param type [Object] + # @option key [Symbol, String] + # @return [void] + def member(type, key: nil) + members.push([key, Utils.wrap_type(type)]) + self + end + + def member?(type) + members.any? { |_key, type_fn| type == type_fn.call } + end + + # Set the discriminant for this union + # + # @param key [Symbol, String] + # @return [void] + def discriminant(key) + @discriminant = key + end + + # @api private + private def discriminated? + !@discriminant.nil? + end + + # Check if value matches a type, handling type wrapper instances + # (Internal::Types::Hash and Internal::Types::Array instances) + # + # @param value [Object] + # @param member_type [Object] + # @return [Boolean] + private def type_matches?(value, member_type) + case member_type + when Seed::Internal::Types::Hash + value.is_a?(::Hash) + when Seed::Internal::Types::Array + value.is_a?(::Array) + when Class, Module + value.is_a?(member_type) + else + false + end + end + + # Resolves the type of a value to be one of the members + # + # @param value [Object] + # @return [Class] + private def resolve_member(value) + if discriminated? && value.is_a?(::Hash) + discriminant_value = value.fetch(@discriminant, nil) + + return if discriminant_value.nil? + + members.to_h[discriminant_value]&.call + else + # First try exact type matching + result = members.find do |_key, mem| + member_type = Utils.unwrap_type(mem) + type_matches?(value, member_type) + end&.last&.call + + return result if result + + # For Hash values, try to coerce into Model member types + if value.is_a?(::Hash) + members.find do |_key, mem| + member_type = Utils.unwrap_type(mem) + # Check if member_type is a Model class + next unless member_type.is_a?(Class) && member_type <= Model + + # Try to coerce the hash into this model type with strict mode + begin + candidate = Utils.coerce(member_type, value, strict: true) + + # Validate that all required (non-optional) fields are present + # This ensures undiscriminated unions properly distinguish between member types + member_type.fields.each do |field_name, field| + raise Errors::TypeError, "Required field `#{field_name}` missing for union member #{member_type.name}" if candidate.instance_variable_get(:@data)[field_name].nil? && !field.optional + end + + true + rescue Errors::TypeError + false + end + end&.last&.call + end + end + end + + def coerce(value, strict: strict?) + type = resolve_member(value) + + unless type + return value unless strict + + if discriminated? + raise Errors::TypeError, + "value of type `#{value.class}` not member of union #{self}" + end + + raise Errors::TypeError, "could not resolve to member of union #{self}" + end + + Utils.coerce(type, value, strict: strict) + end + + # Parse JSON string and coerce to the correct union member type + # + # @param str [String] JSON string to parse + # @return [Object] Coerced value matching a union member + def load(str) + coerce(::JSON.parse(str, symbolize_names: true)) + end + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/types/unknown.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/types/unknown.rb new file mode 100644 index 000000000000..7b58de956da9 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/types/unknown.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Seed + module Internal + module Types + module Unknown + include Seed::Internal::Types::Type + + def coerce(value) + value + end + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/types/utils.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/types/utils.rb new file mode 100644 index 000000000000..4685ec234596 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/internal/types/utils.rb @@ -0,0 +1,101 @@ +# frozen_string_literal: true + +module Seed + module Internal + module Types + # Utilities for dealing with and checking types + module Utils + # Wraps a type into a type function + # + # @param type [Proc, Object] + # @return [Proc] + def self.wrap_type(type) + case type + when Proc + type + else + -> { type } + end + end + + # Resolves a type or type function into a type + # + # @param type [Proc, Object] + # @return [Object] + def self.unwrap_type(type) + type.is_a?(Proc) ? type.call : type + end + + def self.coerce(target, value, strict: false) + type = unwrap_type(target) + + case type + in Array + case value + when ::Array + return type.coerce(value, strict: strict) + when Set, ::Hash + return coerce(type, value.to_a) + end + in Hash + case value + when ::Hash + return type.coerce(value, strict: strict) + when ::Array + return coerce(type, value.to_h) + end + in ->(t) { t <= NilClass } + return nil + in ->(t) { t <= String } + case value + when String, Symbol, Numeric, TrueClass, FalseClass + return value.to_s + end + in ->(t) { t <= Symbol } + case value + when Symbol, String + return value.to_sym + end + in ->(t) { t <= Integer } + case value + when Numeric, String, Time + return value.to_i + end + in ->(t) { t <= Float } + case value + when Numeric, Time, String + return value.to_f + end + in ->(t) { t <= Model } + case value + when type + return value + when ::Hash + return type.coerce(value, strict: strict) + end + in Module + case type + in ->(t) { + t.singleton_class.included_modules.include?(Enum) || + t.singleton_class.included_modules.include?(Union) + } + return type.coerce(value, strict: strict) + else + value + end + else + value + end + + raise Errors::TypeError, "cannot coerce value of type `#{value.class}` to `#{target}`" if strict + + value + end + + def self.symbolize_keys(hash) + hash.transform_keys(&:to_sym) + end + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/nested/api/client.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/nested/api/client.rb new file mode 100644 index 000000000000..f29889a851f1 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/nested/api/client.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +module Seed + module Nested + module Api + class Client + # @param client [Seed::Internal::Http::RawClient] + # + # @return [void] + def initialize(client:) + @client = client + end + + # @param request_options [Hash] + # @param params [Hash] + # @option request_options [String] :base_url + # @option request_options [Hash{String => Object}] :additional_headers + # @option request_options [Hash{String => Object}] :additional_query_parameters + # @option request_options [Hash{String => Object}] :additional_body_parameters + # @option request_options [Integer] :timeout_in_seconds + # + # @return [untyped] + def get_something(request_options: {}, **_params) + request = Seed::Internal::JSON::Request.new( + base_url: request_options[:base_url], + method: "GET", + path: "/nested/get-something", + request_options: request_options + ) + begin + response = @client.send(request) + rescue Net::HTTPRequestTimeout + raise Seed::Errors::TimeoutError + end + code = response.code.to_i + return if code.between?(200, 299) + + error_class = Seed::Errors::ResponseError.subclass_for_code(code) + raise error_class.new(response.body, code: code) + end + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/nested/client.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/nested/client.rb new file mode 100644 index 000000000000..b52615d08246 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/nested/client.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Seed + module Nested + class Client + # @param client [Seed::Internal::Http::RawClient] + # + # @return [void] + def initialize(client:) + @client = client + end + + # @return [Seed::Api::Client] + def api + @api ||= Seed::Nested::Api::Client.new(client: @client) + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/nested_no_auth/api/client.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/nested_no_auth/api/client.rb new file mode 100644 index 000000000000..b668214f9c27 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/nested_no_auth/api/client.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +module Seed + module NestedNoAuth + module Api + class Client + # @param client [Seed::Internal::Http::RawClient] + # + # @return [void] + def initialize(client:) + @client = client + end + + # @param request_options [Hash] + # @param params [Hash] + # @option request_options [String] :base_url + # @option request_options [Hash{String => Object}] :additional_headers + # @option request_options [Hash{String => Object}] :additional_query_parameters + # @option request_options [Hash{String => Object}] :additional_body_parameters + # @option request_options [Integer] :timeout_in_seconds + # + # @return [untyped] + def get_something(request_options: {}, **_params) + request = Seed::Internal::JSON::Request.new( + base_url: request_options[:base_url], + method: "GET", + path: "/nested-no-auth/get-something", + request_options: request_options + ) + begin + response = @client.send(request) + rescue Net::HTTPRequestTimeout + raise Seed::Errors::TimeoutError + end + code = response.code.to_i + return if code.between?(200, 299) + + error_class = Seed::Errors::ResponseError.subclass_for_code(code) + raise error_class.new(response.body, code: code) + end + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/nested_no_auth/client.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/nested_no_auth/client.rb new file mode 100644 index 000000000000..6a790c64056a --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/nested_no_auth/client.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Seed + module NestedNoAuth + class Client + # @param client [Seed::Internal::Http::RawClient] + # + # @return [void] + def initialize(client:) + @client = client + end + + # @return [Seed::Api::Client] + def api + @api ||= Seed::NestedNoAuth::Api::Client.new(client: @client) + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/simple/client.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/simple/client.rb new file mode 100644 index 000000000000..840e4f04acb8 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/simple/client.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +module Seed + module Simple + class Client + # @param client [Seed::Internal::Http::RawClient] + # + # @return [void] + def initialize(client:) + @client = client + end + + # @param request_options [Hash] + # @param params [Hash] + # @option request_options [String] :base_url + # @option request_options [Hash{String => Object}] :additional_headers + # @option request_options [Hash{String => Object}] :additional_query_parameters + # @option request_options [Hash{String => Object}] :additional_body_parameters + # @option request_options [Integer] :timeout_in_seconds + # + # @return [untyped] + def get_something(request_options: {}, **_params) + request = Seed::Internal::JSON::Request.new( + base_url: request_options[:base_url], + method: "GET", + path: "/get-something", + request_options: request_options + ) + begin + response = @client.send(request) + rescue Net::HTTPRequestTimeout + raise Seed::Errors::TimeoutError + end + code = response.code.to_i + return if code.between?(200, 299) + + error_class = Seed::Errors::ResponseError.subclass_for_code(code) + raise error_class.new(response.body, code: code) + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/version.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/version.rb new file mode 100644 index 000000000000..00dd45cdd958 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/lib/seed/version.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +module Seed + VERSION = "0.0.1" +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/reference.md b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/reference.md new file mode 100644 index 000000000000..a61f709181da --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/reference.md @@ -0,0 +1,172 @@ +# Reference +## Auth +
client.auth.get_token() -> Seed::Auth::Types::TokenResponse +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```ruby +client.auth.get_token(api_key: 'api_key'); +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**api_key:** `String` + +
+
+ +
+
+ +**request_options:** `Seed::Auth::RequestOptions` + +
+
+
+
+ + +
+
+
+ +## NestedNoAuth Api +
client.nested_no_auth.api.get_something() -> +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```ruby +client.nested_no_auth.api.get_something(); +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `Seed::NestedNoAuth::Api::RequestOptions` + +
+
+
+
+ + +
+
+
+ +## Nested Api +
client.nested.api.get_something() -> +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```ruby +client.nested.api.get_something(); +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `Seed::Nested::Api::RequestOptions` + +
+
+
+
+ + +
+
+
+ +## Simple +
client.simple.get_something() -> +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```ruby +client.simple.get_something(); +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `Seed::Simple::RequestOptions` + +
+
+
+
+ + +
+
+
diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/seed.gemspec b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/seed.gemspec new file mode 100644 index 000000000000..23b6a2279add --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/seed.gemspec @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require_relative "lib/seed/version" +require_relative "custom.gemspec" + +# NOTE: A handful of these fields are required as part of the Ruby specification. +# You can change them here or overwrite them in the custom gemspec file. +Gem::Specification.new do |spec| + spec.name = "seed" + spec.authors = ["Seed"] + spec.version = Seed::VERSION + spec.summary = "Ruby client library for the Seed API" + spec.description = "The Seed Ruby library provides convenient access to the Seed API from Ruby." + spec.required_ruby_version = ">= 3.3.0" + spec.metadata["rubygems_mfa_required"] = "true" + + # Specify which files should be added to the gem when it is released. + # The `git ls-files -z` loads the files in the RubyGem that have been added into git. + gemspec = File.basename(__FILE__) + spec.files = IO.popen(%w[git ls-files -z], chdir: __dir__, err: IO::NULL) do |ls| + ls.readlines("\x0", chomp: true).reject do |f| + (f == gemspec) || + f.start_with?(*%w[bin/ test/ spec/ features/ .git appveyor Gemfile]) + end + end + spec.bindir = "exe" + spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) } + spec.require_paths = ["lib"] + + # For more information and examples about making a new gem, check out our + # guide at: https://bundler.io/guides/creating_gem.html + + # Load custom gemspec configuration if it exists + custom_gemspec_file = File.join(__dir__, "custom.gemspec.rb") + add_custom_gemspec_data(spec) if File.exist?(custom_gemspec_file) +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/snippet.json b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/test/custom.test.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/test/custom.test.rb new file mode 100644 index 000000000000..4bd57989d43d --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/test/custom.test.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +# This is a custom test file, if you wish to add more tests +# to your SDK. +# Be sure to mark this file in `.fernignore`. +# +# If you include example requests/responses in your fern definition, +# you will have tests automatically generated for you. + +# This test is run via command line: rake customtest +describe "Custom Test" do + it "Default" do + refute false + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/test/test_helper.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/test/test_helper.rb new file mode 100644 index 000000000000..b086fe6d76ec --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/test/test_helper.rb @@ -0,0 +1,3 @@ +# frozen_string_literal: true + +require_relative "../lib/seed" diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/test/unit/internal/iterators/test_cursor_item_iterator.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/test/unit/internal/iterators/test_cursor_item_iterator.rb new file mode 100644 index 000000000000..5008f6abf69f --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/test/unit/internal/iterators/test_cursor_item_iterator.rb @@ -0,0 +1,189 @@ +# frozen_string_literal: true + +require "minitest/autorun" +require "stringio" +require "json" +require "test_helper" + +NUMBERS = (1..65).to_a +PageResponse = Struct.new(:cards, :next_cursor, keyword_init: true) + +class CursorItemIteratorTest < Minitest::Test + def make_iterator(initial_cursor:) + @times_called = 0 + + Seed::Internal::CursorItemIterator.new(initial_cursor:, cursor_field: :next_cursor, item_field: :cards) do |cursor| + @times_called += 1 + cursor ||= 0 + next_cursor = cursor + 10 + PageResponse.new( + cards: NUMBERS[cursor...next_cursor], + next_cursor: next_cursor < NUMBERS.length ? next_cursor : nil + ) + end + end + + def test_item_iterator_can_iterate_to_exhaustion + iterator = make_iterator(initial_cursor: 0) + + assert_equal NUMBERS, iterator.to_a + assert_equal 7, @times_called + + iterator = make_iterator(initial_cursor: 10) + + assert_equal (11..65).to_a, iterator.to_a + + iterator = make_iterator(initial_cursor: 5) + + assert_equal (6..65).to_a, iterator.to_a + end + + def test_item_iterator_can_work_without_an_initial_cursor + iterator = make_iterator(initial_cursor: nil) + + assert_equal NUMBERS, iterator.to_a + assert_equal 7, @times_called + end + + def test_items_iterator_iterates_lazily + iterator = make_iterator(initial_cursor: 0) + + assert_equal 0, @times_called + assert_equal 1, iterator.first + assert_equal 1, @times_called + + iterator = make_iterator(initial_cursor: 0) + + assert_equal 0, @times_called + assert_equal (1..15).to_a, iterator.first(15) + assert_equal 2, @times_called + + iterator = make_iterator(initial_cursor: 0) + + assert_equal 0, @times_called + iterator.each do |card| + break if card >= 15 + end + + assert_equal 2, @times_called + end + + def test_items_iterator_implements_enumerable + iterator = make_iterator(initial_cursor: 0) + + assert_equal 0, @times_called + doubled = iterator.map { |card| card * 2 } + + assert_equal 7, @times_called + assert_equal NUMBERS.length, doubled.length + end + + def test_items_iterator_can_be_advanced_manually + iterator = make_iterator(initial_cursor: 0) + + assert_equal 0, @times_called + + items = [] + expected_times_called = 0 + while (item = iterator.next_element) + expected_times_called += 1 if (item % 10) == 1 + + assert_equal expected_times_called, @times_called + assert_equal item != NUMBERS.last, iterator.next?, "#{item} #{iterator}" + items.push(item) + end + + assert_equal 7, @times_called + assert_equal NUMBERS, items + end + + def test_pages_iterator + iterator = make_iterator(initial_cursor: 0).pages + + assert_equal( + [ + (1..10).to_a, + (11..20).to_a, + (21..30).to_a, + (31..40).to_a, + (41..50).to_a, + (51..60).to_a, + (61..65).to_a + ], + iterator.to_a.map(&:cards) + ) + + iterator = make_iterator(initial_cursor: 10).pages + + assert_equal( + [ + (11..20).to_a, + (21..30).to_a, + (31..40).to_a, + (41..50).to_a, + (51..60).to_a, + (61..65).to_a + ], + iterator.to_a.map(&:cards) + ) + end + + def test_pages_iterator_can_work_without_an_initial_cursor + iterator = make_iterator(initial_cursor: nil).pages + + assert_equal 7, iterator.to_a.length + assert_equal 7, @times_called + end + + def test_pages_iterator_iterates_lazily + iterator = make_iterator(initial_cursor: 0).pages + + assert_equal 0, @times_called + iterator.first + + assert_equal 1, @times_called + + iterator = make_iterator(initial_cursor: 0).pages + + assert_equal 0, @times_called + assert_equal 2, iterator.first(2).length + assert_equal 2, @times_called + end + + def test_pages_iterator_knows_whether_another_page_is_upcoming + iterator = make_iterator(initial_cursor: 0).pages + + iterator.each_with_index do |_page, index| + assert_equal index + 1, @times_called + assert_equal index < 6, iterator.next? + end + end + + def test_pages_iterator_can_be_advanced_manually + iterator = make_iterator(initial_cursor: 0).pages + + assert_equal 0, @times_called + + lengths = [] + expected_times_called = 0 + while (page = iterator.next_page) + expected_times_called += 1 + + assert_equal expected_times_called, @times_called + lengths.push(page.cards.length) + end + + assert_equal 7, @times_called + assert_equal [10, 10, 10, 10, 10, 10, 5], lengths + end + + def test_pages_iterator_implements_enumerable + iterator = make_iterator(initial_cursor: 0).pages + + assert_equal 0, @times_called + lengths = iterator.map { |page| page.cards.length } + + assert_equal 7, @times_called + assert_equal [10, 10, 10, 10, 10, 10, 5], lengths + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/test/unit/internal/iterators/test_offset_item_iterator.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/test/unit/internal/iterators/test_offset_item_iterator.rb new file mode 100644 index 000000000000..92576b820128 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/test/unit/internal/iterators/test_offset_item_iterator.rb @@ -0,0 +1,151 @@ +# frozen_string_literal: true + +require "minitest/autorun" +require "stringio" +require "json" +require "test_helper" + +OffsetPageResponse = Struct.new(:items, :has_next, keyword_init: true) +TestIteratorConfig = Struct.new( + :step, + :has_next_field, + :total_item_count, + :per_page, + :initial_page +) do + def first_item_returned + if step + (initial_page || 0) + 1 + else + (((initial_page || 1) - 1) * per_page) + 1 + end + end +end + +LAZY_TEST_ITERATOR_CONFIG = TestIteratorConfig.new(initial_page: 1, step: false, has_next_field: :has_next, total_item_count: 65, per_page: 10) +ALL_TEST_ITERATOR_CONFIGS = [true, false].map do |step| + [:has_next, nil].map do |has_next_field| + [0, 5, 10, 60, 63].map do |total_item_count| + [5, 10].map do |per_page| + initial_pages = [nil, 3, 100] + initial_pages << (step ? 0 : 1) + + initial_pages.map do |initial_page| + TestIteratorConfig.new( + step: step, + has_next_field: has_next_field, + total_item_count: total_item_count, + per_page: per_page, + initial_page: initial_page + ) + end + end + end + end +end.flatten + +class OffsetItemIteratorTest < Minitest::Test + def make_iterator(config) + @times_called = 0 + + items = (1..config.total_item_count).to_a + + Seed::Internal::OffsetItemIterator.new( + initial_page: config.initial_page, + item_field: :items, + has_next_field: config.has_next_field, + step: config.step + ) do |page| + @times_called += 1 + + slice_start = config.step ? page : (page - 1) * config.per_page + slice_end = slice_start + config.per_page + + output = { + items: items[slice_start...slice_end] + } + output[config.has_next_field] = slice_end < items.length if config.has_next_field + + OffsetPageResponse.new(**output) + end + end + + def test_item_iterator_can_iterate_to_exhaustion + ALL_TEST_ITERATOR_CONFIGS.each do |config| + iterator = make_iterator(config) + + assert_equal (config.first_item_returned..config.total_item_count).to_a, iterator.to_a + end + end + + def test_items_iterator_can_be_advanced_manually_and_has_accurate_has_next + ALL_TEST_ITERATOR_CONFIGS.each do |config| + iterator = make_iterator(config) + items = [] + + while (item = iterator.next_element) + assert_equal(item != config.total_item_count, iterator.next?, "#{item} #{iterator}") + items.push(item) + end + + assert_equal (config.first_item_returned..config.total_item_count).to_a, items + end + end + + def test_pages_iterator_can_be_advanced_manually_and_has_accurate_has_next + ALL_TEST_ITERATOR_CONFIGS.each do |config| + iterator = make_iterator(config).pages + pages = [] + + loop do + has_next_output = iterator.next? + page = iterator.next_page + + assert_equal(has_next_output, !page.nil?, "next? was inaccurate: #{config} #{iterator.inspect}") + break if page.nil? + + pages.push(page) + end + + assert_equal pages, make_iterator(config).pages.to_a + end + end + + def test_items_iterator_iterates_lazily + iterator = make_iterator(LAZY_TEST_ITERATOR_CONFIG) + + assert_equal 0, @times_called + assert_equal 1, iterator.first + assert_equal 1, @times_called + + iterator = make_iterator(LAZY_TEST_ITERATOR_CONFIG) + + assert_equal 0, @times_called + assert_equal (1..15).to_a, iterator.first(15) + assert_equal 2, @times_called + + iterator = make_iterator(LAZY_TEST_ITERATOR_CONFIG) + + assert_equal 0, @times_called + iterator.each do |card| + break if card >= 15 + end + + assert_equal 2, @times_called + end + + def test_pages_iterator_iterates_lazily + iterator = make_iterator(LAZY_TEST_ITERATOR_CONFIG).pages + + assert_equal 0, @times_called + iterator.first + + assert_equal 1, @times_called + + iterator = make_iterator(LAZY_TEST_ITERATOR_CONFIG).pages + + assert_equal 0, @times_called + assert_equal 3, iterator.first(3).length + assert_equal 3, @times_called + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/test/unit/internal/types/test_array.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/test/unit/internal/types/test_array.rb new file mode 100644 index 000000000000..e7e6571f03ee --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/test/unit/internal/types/test_array.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +require "test_helper" + +describe Seed::Internal::Types::Array do + module TestArray + StringArray = Seed::Internal::Types::Array[String] + end + + describe "#initialize" do + it "sets the type" do + assert_equal String, TestArray::StringArray.type + end + end + + describe "#coerce" do + it "does not perform coercion if not an array" do + assert_equal 1, TestArray::StringArray.coerce(1) + end + + it "raises an error if not an array and strictness is on" do + assert_raises Seed::Internal::Errors::TypeError do + TestArray::StringArray.coerce(1, strict: true) + end + end + + it "coerces the elements" do + assert_equal %w[foobar 1 true], TestArray::StringArray.coerce(["foobar", 1, true]) + end + + it "raises an error if element of array is not coercable and strictness is on" do + assert_raises Seed::Internal::Errors::TypeError do + TestArray::StringArray.coerce([Object.new], strict: true) + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/test/unit/internal/types/test_boolean.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/test/unit/internal/types/test_boolean.rb new file mode 100644 index 000000000000..cba18e48765b --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/test/unit/internal/types/test_boolean.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +require "test_helper" + +describe Seed::Internal::Types::Boolean do + describe ".coerce" do + it "coerces true/false" do + assert Seed::Internal::Types::Boolean.coerce(true) + refute Seed::Internal::Types::Boolean.coerce(false) + end + + it "coerces an Integer" do + assert Seed::Internal::Types::Boolean.coerce(1) + refute Seed::Internal::Types::Boolean.coerce(0) + end + + it "coerces a String" do + assert Seed::Internal::Types::Boolean.coerce("1") + assert Seed::Internal::Types::Boolean.coerce("true") + refute Seed::Internal::Types::Boolean.coerce("0") + end + + it "passes through other values with strictness off" do + obj = Object.new + + assert_equal obj, Seed::Internal::Types::Boolean.coerce(obj) + end + + it "raises an error with other values with strictness on" do + assert_raises Seed::Internal::Errors::TypeError do + Seed::Internal::Types::Boolean.coerce(Object.new, strict: true) + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/test/unit/internal/types/test_enum.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/test/unit/internal/types/test_enum.rb new file mode 100644 index 000000000000..e8d89bce467f --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/test/unit/internal/types/test_enum.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require "test_helper" + +describe Seed::Internal::Types::Enum do + module EnumTest + module ExampleEnum + extend Seed::Internal::Types::Enum + + FOO = :foo + BAR = :bar + + finalize! + end + end + + describe "#values" do + it "defines values" do + assert_equal %i[foo bar].sort, EnumTest::ExampleEnum.values.sort + end + end + + describe "#coerce" do + it "coerces an existing member" do + assert_equal :foo, EnumTest::ExampleEnum.coerce(:foo) + end + + it "coerces a string version of a member" do + assert_equal :foo, EnumTest::ExampleEnum.coerce("foo") + end + + it "returns the value if not a member with strictness off" do + assert_equal 1, EnumTest::ExampleEnum.coerce(1) + end + + it "raises an error if value is not a member with strictness on" do + assert_raises Seed::Internal::Errors::TypeError do + EnumTest::ExampleEnum.coerce(1, strict: true) + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/test/unit/internal/types/test_hash.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/test/unit/internal/types/test_hash.rb new file mode 100644 index 000000000000..6c5e58a6a946 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/test/unit/internal/types/test_hash.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +require "test_helper" + +describe Seed::Internal::Types::Hash do + module TestHash + SymbolStringHash = Seed::Internal::Types::Hash[Symbol, String] + end + + describe ".[]" do + it "defines the key and value type" do + assert_equal Symbol, TestHash::SymbolStringHash.key_type + assert_equal String, TestHash::SymbolStringHash.value_type + end + end + + describe "#coerce" do + it "coerces the keys" do + assert_equal %i[foo bar], TestHash::SymbolStringHash.coerce({ "foo" => "1", :bar => "2" }).keys + end + + it "coerces the values" do + assert_equal %w[foo 1], TestHash::SymbolStringHash.coerce({ foo: :foo, bar: 1 }).values + end + + it "passes through other values with strictness off" do + obj = Object.new + + assert_equal obj, TestHash::SymbolStringHash.coerce(obj) + end + + it "raises an error with other values with strictness on" do + assert_raises Seed::Internal::Errors::TypeError do + TestHash::SymbolStringHash.coerce(Object.new, strict: true) + end + end + + it "raises an error with non-coercable key types with strictness on" do + assert_raises Seed::Internal::Errors::TypeError do + TestHash::SymbolStringHash.coerce({ Object.new => 1 }, strict: true) + end + end + + it "raises an error with non-coercable value types with strictness on" do + assert_raises Seed::Internal::Errors::TypeError do + TestHash::SymbolStringHash.coerce({ "foobar" => Object.new }, strict: true) + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/test/unit/internal/types/test_model.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/test/unit/internal/types/test_model.rb new file mode 100644 index 000000000000..3d87b9f5a8c7 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/test/unit/internal/types/test_model.rb @@ -0,0 +1,154 @@ +# frozen_string_literal: true + +require "test_helper" + +describe Seed::Internal::Types::Model do + module StringInteger + extend Seed::Internal::Types::Union + + member String + member Integer + end + + class ExampleModel < Seed::Internal::Types::Model + field :name, String + field :rating, StringInteger, optional: true + field :year, Integer, optional: true, nullable: true, api_name: "yearOfRelease" + end + + class ExampleModelInheritance < ExampleModel + field :director, String + end + + class ExampleWithDefaults < ExampleModel + field :type, String, default: "example" + end + + class ExampleChild < Seed::Internal::Types::Model + field :value, String + end + + class ExampleParent < Seed::Internal::Types::Model + field :child, ExampleChild + end + + describe ".field" do + before do + @example = ExampleModel.new(name: "Inception", rating: 4) + end + + it "defines fields on model" do + assert_equal %i[name rating year], ExampleModel.fields.keys + end + + it "defines fields from parent models" do + assert_equal %i[name rating year director], ExampleModelInheritance.fields.keys + end + + it "sets the field's type" do + assert_equal String, ExampleModel.fields[:name].type + assert_equal StringInteger, ExampleModel.fields[:rating].type + end + + it "sets the `default` option" do + assert_equal "example", ExampleWithDefaults.fields[:type].default + end + + it "defines getters" do + assert_respond_to @example, :name + assert_respond_to @example, :rating + + assert_equal "Inception", @example.name + assert_equal 4, @example.rating + end + + it "defines setters" do + assert_respond_to @example, :name= + assert_respond_to @example, :rating= + + @example.name = "Inception 2" + @example.rating = 5 + + assert_equal "Inception 2", @example.name + assert_equal 5, @example.rating + end + end + + describe "#initialize" do + it "sets the data" do + example = ExampleModel.new(name: "Inception", rating: 4) + + assert_equal "Inception", example.name + assert_equal 4, example.rating + end + + it "allows extra fields to be set" do + example = ExampleModel.new(name: "Inception", rating: 4, director: "Christopher Nolan") + + assert_equal "Christopher Nolan", example.director + end + + it "sets the defaults where applicable" do + example_using_defaults = ExampleWithDefaults.new + + assert_equal "example", example_using_defaults.type + + example_without_defaults = ExampleWithDefaults.new(type: "not example") + + assert_equal "not example", example_without_defaults.type + end + + it "coerces child models" do + parent = ExampleParent.new(child: { value: "foobar" }) + + assert_kind_of ExampleChild, parent.child + end + + it "uses the api_name to pull the value" do + example = ExampleModel.new({ name: "Inception", yearOfRelease: 2014 }) + + assert_equal 2014, example.year + refute_respond_to example, :yearOfRelease + end + end + + describe "#inspect" do + class SensitiveModel < Seed::Internal::Types::Model + field :username, String + field :password, String + field :client_secret, String + field :access_token, String + field :api_key, String + end + + it "redacts sensitive fields" do + model = SensitiveModel.new( + username: "user123", + password: "secret123", + client_secret: "cs_abc", + access_token: "token_xyz", + api_key: "key_123" + ) + + inspect_output = model.inspect + + assert_includes inspect_output, "username=\"user123\"" + assert_includes inspect_output, "password=[REDACTED]" + assert_includes inspect_output, "client_secret=[REDACTED]" + assert_includes inspect_output, "access_token=[REDACTED]" + assert_includes inspect_output, "api_key=[REDACTED]" + refute_includes inspect_output, "secret123" + refute_includes inspect_output, "cs_abc" + refute_includes inspect_output, "token_xyz" + refute_includes inspect_output, "key_123" + end + + it "does not redact non-sensitive fields" do + example = ExampleModel.new(name: "Inception", rating: 4) + inspect_output = example.inspect + + assert_includes inspect_output, "name=\"Inception\"" + assert_includes inspect_output, "rating=4" + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/test/unit/internal/types/test_union.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/test/unit/internal/types/test_union.rb new file mode 100644 index 000000000000..2b08e7f91d8f --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/test/unit/internal/types/test_union.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +require "test_helper" + +describe Seed::Internal::Types::Union do + class Rectangle < Seed::Internal::Types::Model + literal :type, "square" + + field :area, Float + end + + class Circle < Seed::Internal::Types::Model + literal :type, "circle" + + field :area, Float + end + + class Pineapple < Seed::Internal::Types::Model + literal :type, "pineapple" + + field :area, Float + end + + module Shape + extend Seed::Internal::Types::Union + + discriminant :type + + member -> { Rectangle }, key: "rect" + member -> { Circle }, key: "circle" + end + + module StringOrInteger + extend Seed::Internal::Types::Union + + member String + member Integer + end + + describe "#coerce" do + it "coerces hashes into member models with discriminated unions" do + circle = Shape.coerce({ type: "circle", area: 4.0 }) + + assert_instance_of Circle, circle + end + end + + describe "#member" do + it "defines Model members" do + assert Shape.member?(Rectangle) + assert Shape.member?(Circle) + refute Shape.member?(Pineapple) + end + + it "defines other members" do + assert StringOrInteger.member?(String) + assert StringOrInteger.member?(Integer) + refute StringOrInteger.member?(Float) + refute StringOrInteger.member?(Pineapple) + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/test/unit/internal/types/test_utils.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/test/unit/internal/types/test_utils.rb new file mode 100644 index 000000000000..29d14621a229 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-api-key/test/unit/internal/types/test_utils.rb @@ -0,0 +1,212 @@ +# frozen_string_literal: true + +require "test_helper" + +describe Seed::Internal::Types::Utils do + Utils = Seed::Internal::Types::Utils + + module TestUtils + class M < Seed::Internal::Types::Model + field :value, String + end + + class UnionMemberA < Seed::Internal::Types::Model + literal :type, "A" + field :only_on_a, String + end + + class UnionMemberB < Seed::Internal::Types::Model + literal :type, "B" + field :only_on_b, String + end + + module U + extend Seed::Internal::Types::Union + + discriminant :type + + member -> { UnionMemberA }, key: "A" + member -> { UnionMemberB }, key: "B" + end + + SymbolStringHash = Seed::Internal::Types::Hash[Symbol, String] + SymbolModelHash = -> { Seed::Internal::Types::Hash[Symbol, TestUtils::M] } + end + + describe ".coerce" do + describe "NilClass" do + it "always returns nil" do + assert_nil Utils.coerce(NilClass, "foobar") + assert_nil Utils.coerce(NilClass, 1) + assert_nil Utils.coerce(NilClass, Object.new) + end + end + + describe "String" do + it "coerces from String, Symbol, Numeric, or Boolean" do + assert_equal "foobar", Utils.coerce(String, "foobar") + assert_equal "foobar", Utils.coerce(String, :foobar) + assert_equal "1", Utils.coerce(String, 1) + assert_equal "1.0", Utils.coerce(String, 1.0) + assert_equal "true", Utils.coerce(String, true) + end + + it "passes through value if it cannot be coerced and not strict" do + obj = Object.new + + assert_equal obj, Utils.coerce(String, obj) + end + + it "raises an error if value cannot be coerced and strict" do + assert_raises Seed::Internal::Errors::TypeError do + Utils.coerce(String, Object.new, strict: true) + end + end + end + + describe "Symbol" do + it "coerces from Symbol, String" do + assert_equal :foobar, Utils.coerce(Symbol, :foobar) + assert_equal :foobar, Utils.coerce(Symbol, "foobar") + end + + it "passes through value if it cannot be coerced and not strict" do + obj = Object.new + + assert_equal obj, Utils.coerce(Symbol, obj) + end + + it "raises an error if value cannot be coerced and strict" do + assert_raises Seed::Internal::Errors::TypeError do + Utils.coerce(Symbol, Object.new, strict: true) + end + end + end + + describe "Integer" do + it "coerces from Numeric, String, Time" do + assert_equal 1, Utils.coerce(Integer, 1) + assert_equal 1, Utils.coerce(Integer, 1.0) + assert_equal 1, Utils.coerce(Integer, Complex.rect(1)) + assert_equal 1, Utils.coerce(Integer, Rational(1)) + assert_equal 1, Utils.coerce(Integer, "1") + assert_equal 1_713_916_800, Utils.coerce(Integer, Time.utc(2024, 4, 24)) + end + + it "passes through value if it cannot be coerced and not strict" do + obj = Object.new + + assert_equal obj, Utils.coerce(Integer, obj) + end + + it "raises an error if value cannot be coerced and strict" do + assert_raises Seed::Internal::Errors::TypeError do + Utils.coerce(Integer, Object.new, strict: true) + end + end + end + + describe "Float" do + it "coerces from Numeric, Time" do + assert_in_delta(1.0, Utils.coerce(Float, 1.0)) + assert_in_delta(1.0, Utils.coerce(Float, 1)) + assert_in_delta(1.0, Utils.coerce(Float, Complex.rect(1))) + assert_in_delta(1.0, Utils.coerce(Float, Rational(1))) + assert_in_delta(1_713_916_800.0, Utils.coerce(Integer, Time.utc(2024, 4, 24))) + end + + it "passes through value if it cannot be coerced and not strict" do + obj = Object.new + + assert_equal obj, Utils.coerce(Float, obj) + end + + it "raises an error if value cannot be coerced and strict" do + assert_raises Seed::Internal::Errors::TypeError do + Utils.coerce(Float, Object.new, strict: true) + end + end + end + + describe "Model" do + it "coerces a hash" do + result = Utils.coerce(TestUtils::M, { value: "foobar" }) + + assert_kind_of TestUtils::M, result + assert_equal "foobar", result.value + end + + it "coerces a hash when the target is a type function" do + result = Utils.coerce(-> { TestUtils::M }, { value: "foobar" }) + + assert_kind_of TestUtils::M, result + assert_equal "foobar", result.value + end + + it "will not coerce non-hashes" do + assert_equal "foobar", Utils.coerce(TestUtils::M, "foobar") + end + end + + describe "Enum" do + module ExampleEnum + extend Seed::Internal::Types::Enum + + FOO = :FOO + BAR = :BAR + + finalize! + end + + it "coerces into a Symbol version of the member value" do + assert_equal :FOO, Utils.coerce(ExampleEnum, "FOO") + end + + it "returns given value if not a member" do + assert_equal "NOPE", Utils.coerce(ExampleEnum, "NOPE") + end + end + + describe "Array" do + StringArray = Seed::Internal::Types::Array[String] + ModelArray = -> { Seed::Internal::Types::Array[TestUtils::M] } + UnionArray = -> { Seed::Internal::Types::Array[TestUtils::U] } + + it "coerces an array of literals" do + assert_equal %w[a b c], Utils.coerce(StringArray, %w[a b c]) + assert_equal ["1", "2.0", "true"], Utils.coerce(StringArray, [1, 2.0, true]) + assert_equal ["1", "2.0", "true"], Utils.coerce(StringArray, Set.new([1, 2.0, true])) + end + + it "coerces an array of Models" do + assert_equal [TestUtils::M.new(value: "foobar"), TestUtils::M.new(value: "bizbaz")], + Utils.coerce(ModelArray, [{ value: "foobar" }, { value: "bizbaz" }]) + + assert_equal [TestUtils::M.new(value: "foobar"), TestUtils::M.new(value: "bizbaz")], + Utils.coerce(ModelArray, [TestUtils::M.new(value: "foobar"), TestUtils::M.new(value: "bizbaz")]) + end + + it "coerces an array of model unions" do + assert_equal [TestUtils::UnionMemberA.new(type: "A", only_on_a: "A"), TestUtils::UnionMemberB.new(type: "B", only_on_b: "B")], + Utils.coerce(UnionArray, [{ type: "A", only_on_a: "A" }, { type: "B", only_on_b: "B" }]) + end + + it "returns given value if not an array" do + assert_equal 1, Utils.coerce(StringArray, 1) + end + end + + describe "Hash" do + it "coerces the keys and values" do + ssh_res = Utils.coerce(TestUtils::SymbolStringHash, { "foo" => "bar", "biz" => "2" }) + + assert_equal "bar", ssh_res[:foo] + assert_equal "2", ssh_res[:biz] + + smh_res = Utils.coerce(TestUtils::SymbolModelHash, { "foo" => { "value" => "foo" } }) + + assert_equal TestUtils::M.new(value: "foo"), smh_res[:foo] + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/.fern/metadata.json b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/.fern/metadata.json new file mode 100644 index 000000000000..ce04af614b97 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/.fern/metadata.json @@ -0,0 +1,5 @@ +{ + "cliVersion": "DUMMY", + "generatorName": "fernapi/fern-ruby-sdk-v2", + "generatorVersion": "latest" +} \ No newline at end of file diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/.github/workflows/ci.yml b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/.github/workflows/ci.yml new file mode 100644 index 000000000000..c52a42e0dd03 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/.github/workflows/ci.yml @@ -0,0 +1,37 @@ +name: ci + +on: [push, pull_request] + +jobs: + lint: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: "3.3" + bundler-cache: true + + - name: Run Rubocop + run: bundle exec rubocop + + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: "3.3" + bundler-cache: true + + - name: Run Tests + run: bundle exec rake test + diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/.gitignore b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/.gitignore new file mode 100644 index 000000000000..c111b331371a --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/.gitignore @@ -0,0 +1 @@ +*.gem diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/.rubocop.yml b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/.rubocop.yml new file mode 100644 index 000000000000..2c5567ea193e --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/.rubocop.yml @@ -0,0 +1,60 @@ +plugins: + - rubocop-minitest + +AllCops: + TargetRubyVersion: 3.3 + NewCops: enable + +Style/StringLiterals: + EnforcedStyle: double_quotes + +Style/StringLiteralsInInterpolation: + EnforcedStyle: double_quotes + +Style/AccessModifierDeclarations: + Enabled: false + +Lint/ConstantDefinitionInBlock: + Enabled: false + +Metrics/AbcSize: + Enabled: false + +Metrics/BlockLength: + Enabled: false + +Metrics/ClassLength: + Enabled: false + +Metrics/MethodLength: + Enabled: false + +Metrics/ParameterLists: + Enabled: false + +Metrics/PerceivedComplexity: + Enabled: false + +Metrics/CyclomaticComplexity: + Enabled: false + +Metrics/ModuleLength: + Enabled: false + +Layout/LineLength: + Enabled: false + +Naming/VariableNumber: + EnforcedStyle: snake_case + +Style/Documentation: + Enabled: false + +Style/Lambda: + EnforcedStyle: literal + +Minitest/MultipleAssertions: + Enabled: false + +Minitest/UselessAssertion: + Enabled: false diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/Gemfile b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/Gemfile new file mode 100644 index 000000000000..29b144d77f48 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/Gemfile @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + +gemspec + +group :test, :development do + gem "rake", "~> 13.0" + + gem "minitest", "~> 5.16" + gem "minitest-rg" + + gem "rubocop", "~> 1.21" + gem "rubocop-minitest" + + gem "pry" + + gem "webmock" +end + +# Load custom Gemfile configuration if it exists +custom_gemfile = File.join(__dir__, "Gemfile.custom") +eval_gemfile(custom_gemfile) if File.exist?(custom_gemfile) diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/Gemfile.custom b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/Gemfile.custom new file mode 100644 index 000000000000..11bdfaf13f2d --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/Gemfile.custom @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +# Custom Gemfile configuration file +# This file is automatically loaded by the main Gemfile. You can add custom gems, +# groups, or other Gemfile configurations here. If you do make changes to this file, +# you will need to add it to the .fernignore file to prevent your changes from being +# overwritten by the generator. + +# Example usage: +# group :test, :development do +# gem 'custom-gem', '~> 2.0' +# end + +# Add your custom gem dependencies here \ No newline at end of file diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/README.md b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/README.md new file mode 100644 index 000000000000..6eee82779c6d --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/README.md @@ -0,0 +1,101 @@ +# Seed Ruby Library + +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FRuby) + +The Seed Ruby library provides convenient access to the Seed APIs from Ruby. + +## Table of Contents + +- [Reference](#reference) +- [Usage](#usage) +- [Environments](#environments) +- [Errors](#errors) +- [Advanced](#advanced) + - [Timeouts](#timeouts) +- [Contributing](#contributing) + +## Reference + +A full reference for this library is available [here](./reference.md). + +## Usage + +Instantiate and use the client with the following: + +```ruby +require "seed" + +client = Seed::Client.new(); + +client.auth.get_token_with_client_credentials( + client_id: 'client_id', + client_secret: 'client_secret', + audience: 'https://api.example.com', + grant_type: 'client_credentials', + scope: 'scope' +); +``` + +## Environments + +This SDK allows you to configure different custom URLs for API requests. You can specify your own custom URL. + +### Custom URL +```ruby +require "seed" + +client = Seed::Client.new( + base_url: "https://example.com" +) +``` + +## Errors + +Failed API calls will raise errors that can be rescued from granularly. + +```ruby +require "seed" + +client = Seed::Client.new( + base_url: "https://example.com" +) + +begin + result = client.auth.get_token_with_client_credentials +rescue Seed::Errors::TimeoutError + puts "API didn't respond before our timeout elapsed" +rescue Seed::Errors::ServiceUnavailableError + puts "API returned status 503, is probably overloaded, try again later" +rescue Seed::Errors::ServerError + puts "API returned some other 5xx status, this is probably a bug" +rescue Seed::Errors::ResponseError => e + puts "API returned an unexpected status other than 5xx: #{e.code} #{e.message}" +rescue Seed::Errors::ApiError => e + puts "Some other error occurred when calling the API: #{e.message}" +end +``` + +## Advanced + +### Timeouts + +The SDK defaults to a 60 second timeout. Use the `timeout` option to configure this behavior. + +```ruby +require "seed" + +response = client.auth.get_token_with_client_credentials( + ..., + timeout: 30 # 30 second timeout +) +``` + +## Contributing + +While we value open-source contributions to this SDK, this library is generated programmatically. +Additions made directly to this library would have to be moved over to our generation code, +otherwise they would be overwritten upon the next generated release. Feel free to open a PR as +a proof of concept, but know that we will not be able to merge it as-is. We suggest opening +an issue first to discuss with us! + +On the other hand, contributions to the README are always very welcome! \ No newline at end of file diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/Rakefile b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/Rakefile new file mode 100644 index 000000000000..9bdd4a6ce80b --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/Rakefile @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +require "bundler/gem_tasks" +require "minitest/test_task" + +Minitest::TestTask.create + +require "rubocop/rake_task" + +RuboCop::RakeTask.new + +task default: %i[test] + +task lint: %i[rubocop] + +# Run only the custom test file +Minitest::TestTask.create(:customtest) do |t| + t.libs << "test" + t.test_globs = ["test/custom.test.rb"] +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/custom.gemspec.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/custom.gemspec.rb new file mode 100644 index 000000000000..86d8efd3cd3c --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/custom.gemspec.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +# Custom gemspec configuration file +# This file is automatically loaded by the main gemspec file. The 'spec' variable is available +# in this context from the main gemspec file. You can modify this file to add custom metadata, +# dependencies, or other gemspec configurations. If you do make changes to this file, you will +# need to add it to the .fernignore file to prevent your changes from being overwritten. + +def add_custom_gemspec_data(spec) + # Example custom configurations (uncomment and modify as needed) + + # spec.authors = ["Your name"] + # spec.email = ["your.email@example.com"] + # spec.homepage = "https://github.com/your-org/seed-ruby" + # spec.license = "Your license" +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/dynamic-snippets/example0/snippet.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/dynamic-snippets/example0/snippet.rb new file mode 100644 index 000000000000..98a71da6e6fa --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/dynamic-snippets/example0/snippet.rb @@ -0,0 +1,11 @@ +require "seed" + +client = Seed::Client.new(base_url: 'https://api.fern.com'); + +client.auth.get_token_with_client_credentials( + client_id: 'client_id', + client_secret: 'client_secret', + audience: 'https://api.example.com', + grant_type: 'client_credentials', + scope: 'scope' +); diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/dynamic-snippets/example1/snippet.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/dynamic-snippets/example1/snippet.rb new file mode 100644 index 000000000000..3847bdbe3ac6 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/dynamic-snippets/example1/snippet.rb @@ -0,0 +1,12 @@ +require "seed" + +client = Seed::Client.new(base_url: 'https://api.fern.com'); + +client.auth.refresh_token( + client_id: 'client_id', + client_secret: 'client_secret', + refresh_token: 'refresh_token', + audience: 'https://api.example.com', + grant_type: 'refresh_token', + scope: 'scope' +); diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/dynamic-snippets/example2/snippet.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/dynamic-snippets/example2/snippet.rb new file mode 100644 index 000000000000..ea8d55d2c117 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/dynamic-snippets/example2/snippet.rb @@ -0,0 +1,5 @@ +require "seed" + +client = Seed::Client.new(base_url: 'https://api.fern.com'); + +client.nested_no_auth.api.get_something(); diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/dynamic-snippets/example3/snippet.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/dynamic-snippets/example3/snippet.rb new file mode 100644 index 000000000000..29c334d11e52 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/dynamic-snippets/example3/snippet.rb @@ -0,0 +1,5 @@ +require "seed" + +client = Seed::Client.new(base_url: 'https://api.fern.com'); + +client.nested.api.get_something(); diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/dynamic-snippets/example4/snippet.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/dynamic-snippets/example4/snippet.rb new file mode 100644 index 000000000000..2040da7289cc --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/dynamic-snippets/example4/snippet.rb @@ -0,0 +1,5 @@ +require "seed" + +client = Seed::Client.new(base_url: 'https://api.fern.com'); + +client.simple.get_something(); diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed.rb new file mode 100644 index 000000000000..4282fd41587d --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +require "json" +require "net/http" +require "securerandom" + +require_relative "seed/internal/json/serializable" +require_relative "seed/internal/types/type" +require_relative "seed/internal/types/utils" +require_relative "seed/internal/types/union" +require_relative "seed/internal/errors/constraint_error" +require_relative "seed/internal/errors/type_error" +require_relative "seed/internal/http/base_request" +require_relative "seed/internal/json/request" +require_relative "seed/internal/http/raw_client" +require_relative "seed/internal/multipart/multipart_encoder" +require_relative "seed/internal/multipart/multipart_form_data_part" +require_relative "seed/internal/multipart/multipart_form_data" +require_relative "seed/internal/multipart/multipart_request" +require_relative "seed/internal/types/model/field" +require_relative "seed/internal/types/model" +require_relative "seed/internal/types/array" +require_relative "seed/internal/types/boolean" +require_relative "seed/internal/types/enum" +require_relative "seed/internal/types/hash" +require_relative "seed/internal/types/unknown" +require_relative "seed/errors/api_error" +require_relative "seed/errors/response_error" +require_relative "seed/errors/client_error" +require_relative "seed/errors/redirect_error" +require_relative "seed/errors/server_error" +require_relative "seed/errors/timeout_error" +require_relative "seed/internal/iterators/item_iterator" +require_relative "seed/internal/iterators/cursor_item_iterator" +require_relative "seed/internal/iterators/offset_item_iterator" +require_relative "seed/internal/iterators/cursor_page_iterator" +require_relative "seed/internal/iterators/offset_page_iterator" +require_relative "seed/auth/types/get_token_request" +require_relative "seed/auth/types/refresh_token_request" +require_relative "seed/auth/types/token_response" +require_relative "seed/client" +require_relative "seed/auth/client" +require_relative "seed/nested_no_auth/client" +require_relative "seed/nested_no_auth/api/client" +require_relative "seed/nested/client" +require_relative "seed/nested/api/client" +require_relative "seed/simple/client" +require_relative "seed/internal/inferred_auth_provider" diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/auth/client.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/auth/client.rb new file mode 100644 index 000000000000..3f6d0ac0f601 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/auth/client.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true + +module Seed + module Auth + class Client + # @param client [Seed::Internal::Http::RawClient] + # + # @return [void] + def initialize(client:) + @client = client + end + + # @param request_options [Hash] + # @param params [Seed::Auth::Types::GetTokenRequest] + # @option request_options [String] :base_url + # @option request_options [Hash{String => Object}] :additional_headers + # @option request_options [Hash{String => Object}] :additional_query_parameters + # @option request_options [Hash{String => Object}] :additional_body_parameters + # @option request_options [Integer] :timeout_in_seconds + # + # @return [Seed::Auth::Types::TokenResponse] + def get_token_with_client_credentials(request_options: {}, **params) + request = Seed::Internal::JSON::Request.new( + base_url: request_options[:base_url], + method: "POST", + path: "/token", + body: Seed::Auth::Types::GetTokenRequest.new(params).to_h, + request_options: request_options + ) + begin + response = @client.send(request) + rescue Net::HTTPRequestTimeout + raise Seed::Errors::TimeoutError + end + code = response.code.to_i + if code.between?(200, 299) + Seed::Auth::Types::TokenResponse.load(response.body) + else + error_class = Seed::Errors::ResponseError.subclass_for_code(code) + raise error_class.new(response.body, code: code) + end + end + + # @param request_options [Hash] + # @param params [Seed::Auth::Types::RefreshTokenRequest] + # @option request_options [String] :base_url + # @option request_options [Hash{String => Object}] :additional_headers + # @option request_options [Hash{String => Object}] :additional_query_parameters + # @option request_options [Hash{String => Object}] :additional_body_parameters + # @option request_options [Integer] :timeout_in_seconds + # + # @return [Seed::Auth::Types::TokenResponse] + def refresh_token(request_options: {}, **params) + request = Seed::Internal::JSON::Request.new( + base_url: request_options[:base_url], + method: "POST", + path: "/token/refresh", + body: Seed::Auth::Types::RefreshTokenRequest.new(params).to_h, + request_options: request_options + ) + begin + response = @client.send(request) + rescue Net::HTTPRequestTimeout + raise Seed::Errors::TimeoutError + end + code = response.code.to_i + if code.between?(200, 299) + Seed::Auth::Types::TokenResponse.load(response.body) + else + error_class = Seed::Errors::ResponseError.subclass_for_code(code) + raise error_class.new(response.body, code: code) + end + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/auth/types/get_token_request.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/auth/types/get_token_request.rb new file mode 100644 index 000000000000..a86ea9ab7829 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/auth/types/get_token_request.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Seed + module Auth + module Types + # A request to obtain an OAuth token. + class GetTokenRequest < Internal::Types::Model + field :client_id, -> { String }, optional: false, nullable: false + field :client_secret, -> { String }, optional: false, nullable: false + field :audience, -> { String }, optional: false, nullable: false + field :grant_type, -> { String }, optional: false, nullable: false + field :scope, -> { String }, optional: true, nullable: false + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/auth/types/refresh_token_request.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/auth/types/refresh_token_request.rb new file mode 100644 index 000000000000..8b5e363497d1 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/auth/types/refresh_token_request.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module Seed + module Auth + module Types + # A request to refresh an OAuth token. + class RefreshTokenRequest < Internal::Types::Model + field :client_id, -> { String }, optional: false, nullable: false + field :client_secret, -> { String }, optional: false, nullable: false + field :refresh_token, -> { String }, optional: false, nullable: false + field :audience, -> { String }, optional: false, nullable: false + field :grant_type, -> { String }, optional: false, nullable: false + field :scope, -> { String }, optional: true, nullable: false + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/auth/types/token_response.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/auth/types/token_response.rb new file mode 100644 index 000000000000..5eaf02cd5669 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/auth/types/token_response.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module Seed + module Auth + module Types + # An OAuth token response. + class TokenResponse < Internal::Types::Model + field :access_token, -> { String }, optional: false, nullable: false + field :expires_in, -> { Integer }, optional: false, nullable: false + field :refresh_token, -> { String }, optional: true, nullable: false + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/client.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/client.rb new file mode 100644 index 000000000000..2bfbbb629511 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/client.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +module Seed + class Client + # @param base_url [String, nil] + # + # @return [void] + def initialize(base_url:) + # Create an unauthenticated client for the auth endpoint + auth_raw_client = Seed::Internal::Http::RawClient.new( + base_url: base_url, + headers: { + "X-Fern-Language" => "Ruby" + } + ) + + # Create the auth client for token retrieval + auth_client = Seed::Auth::Client.new(client: auth_raw_client) + + # Create the auth provider with the auth client and credentials + @auth_provider = Seed::Internal::InferredAuthProvider.new( + auth_client: auth_client, + options: { base_url: base_url } + ) + + @raw_client = Seed::Internal::Http::RawClient.new( + base_url: base_url, + headers: { + "User-Agent" => "fern_inferred-auth-implicit-reference/0.0.1", + "X-Fern-Language" => "Ruby" + }.merge(@auth_provider.auth_headers) + ) + end + + # @return [Seed::Auth::Client] + def auth + @auth ||= Seed::Auth::Client.new(client: @raw_client) + end + + # @return [Seed::NestedNoAuth::Client] + def nested_no_auth + @nested_no_auth ||= Seed::NestedNoAuth::Client.new(client: @raw_client) + end + + # @return [Seed::Nested::Client] + def nested + @nested ||= Seed::Nested::Client.new(client: @raw_client) + end + + # @return [Seed::Simple::Client] + def simple + @simple ||= Seed::Simple::Client.new(client: @raw_client) + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/errors/api_error.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/errors/api_error.rb new file mode 100644 index 000000000000..b8ba53889b36 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/errors/api_error.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module Seed + module Errors + class ApiError < StandardError + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/errors/client_error.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/errors/client_error.rb new file mode 100644 index 000000000000..c3c6033641e2 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/errors/client_error.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module Seed + module Errors + class ClientError < ResponseError + end + + class UnauthorizedError < ClientError + end + + class ForbiddenError < ClientError + end + + class NotFoundError < ClientError + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/errors/redirect_error.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/errors/redirect_error.rb new file mode 100644 index 000000000000..f663c01e7615 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/errors/redirect_error.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module Seed + module Errors + class RedirectError < ResponseError + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/errors/response_error.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/errors/response_error.rb new file mode 100644 index 000000000000..beb4a1baf959 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/errors/response_error.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +module Seed + module Errors + class ResponseError < ApiError + attr_reader :code + + def initialize(msg, code:) + @code = code + super(msg) + end + + def inspect + "#<#{self.class.name} @code=#{code} @body=#{message}>" + end + + # Returns the most appropriate error class for the given code. + # + # @return [Class] + def self.subclass_for_code(code) + case code + when 300..399 + RedirectError + when 401 + UnauthorizedError + when 403 + ForbiddenError + when 404 + NotFoundError + when 400..499 + ClientError + when 503 + ServiceUnavailableError + when 500..599 + ServerError + else + ResponseError + end + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/errors/server_error.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/errors/server_error.rb new file mode 100644 index 000000000000..1838027cdeab --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/errors/server_error.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Seed + module Errors + class ServerError < ResponseError + end + + class ServiceUnavailableError < ApiError + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/errors/timeout_error.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/errors/timeout_error.rb new file mode 100644 index 000000000000..ec3a24bb7e96 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/errors/timeout_error.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module Seed + module Errors + class TimeoutError < ApiError + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/errors/constraint_error.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/errors/constraint_error.rb new file mode 100644 index 000000000000..e2f0bd66ac37 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/errors/constraint_error.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module Seed + module Internal + module Errors + class ConstraintError < StandardError + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/errors/type_error.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/errors/type_error.rb new file mode 100644 index 000000000000..6aec80f59f05 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/errors/type_error.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module Seed + module Internal + module Errors + class TypeError < StandardError + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/http/base_request.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/http/base_request.rb new file mode 100644 index 000000000000..5f65f1327023 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/http/base_request.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Seed + module Internal + module Http + # @api private + class BaseRequest + attr_reader :base_url, :path, :method, :headers, :query, :request_options + + # @param base_url [String] The base URL for the request + # @param path [String] The path for the request + # @param method [String] The HTTP method for the request (:get, :post, etc.) + # @param headers [Hash] Additional headers for the request (optional) + # @param query [Hash] Query parameters for the request (optional) + # @param request_options [Seed::RequestOptions, Hash{Symbol=>Object}, nil] + def initialize(base_url:, path:, method:, headers: {}, query: {}, request_options: {}) + @base_url = base_url + @path = path + @method = method + @headers = headers + @query = query + @request_options = request_options + end + + # Child classes should implement: + # - encode_headers: Returns the encoded HTTP request headers. + # - encode_body: Returns the encoded HTTP request body. + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/http/raw_client.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/http/raw_client.rb new file mode 100644 index 000000000000..3370a92b608b --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/http/raw_client.rb @@ -0,0 +1,106 @@ +# frozen_string_literal: true + +module Seed + module Internal + module Http + # @api private + class RawClient + # @param base_url [String] The base url for the request. + # @param max_retries [Integer] The number of times to retry a failed request, defaults to 2. + # @param timeout [Float] The timeout for the request, defaults to 60.0 seconds. + # @param headers [Hash] The headers for the request. + def initialize(base_url:, max_retries: 2, timeout: 60.0, headers: {}) + @base_url = base_url + @max_retries = max_retries + @timeout = timeout + @default_headers = { + "X-Fern-Language": "Ruby", + "X-Fern-SDK-Name": "seed", + "X-Fern-SDK-Version": "0.0.1" + }.merge(headers) + end + + # @param request [Seed::Internal::Http::BaseRequest] The HTTP request. + # @return [HTTP::Response] The HTTP response. + def send(request) + url = build_url(request) + + http_request = build_http_request( + url:, + method: request.method, + headers: request.encode_headers, + body: request.encode_body + ) + + conn = connect(url) + conn.open_timeout = @timeout + conn.read_timeout = @timeout + conn.write_timeout = @timeout + conn.continue_timeout = @timeout + + conn.request(http_request) + end + + # @param request [Seed::Internal::Http::BaseRequest] The HTTP request. + # @return [URI::Generic] The URL. + def build_url(request) + path = request.path.start_with?("/") ? request.path[1..] : request.path + base = request.base_url || @base_url + url = "#{base.chomp("/")}/#{path}" + url = "#{url}?#{encode_query(request.query)}" if request.query&.any? + URI.parse(url) + end + + # @param url [URI::Generic] The url to the resource. + # @param method [String] The HTTP method to use. + # @param headers [Hash] The headers for the request. + # @param body [String, nil] The body for the request. + # @return [HTTP::Request] The HTTP request. + def build_http_request(url:, method:, headers: {}, body: nil) + request = Net::HTTPGenericRequest.new( + method, + !body.nil?, + method != "HEAD", + url + ) + + request_headers = @default_headers.merge(headers) + request_headers.each { |name, value| request[name] = value } + request.body = body if body + + request + end + + # @param query [Hash] The query for the request. + # @return [String, nil] The encoded query. + def encode_query(query) + query.to_h.empty? ? nil : URI.encode_www_form(query) + end + + # @param url [URI::Generic] The url to connect to. + # @return [Net::HTTP] The HTTP connection. + def connect(url) + is_https = (url.scheme == "https") + + port = if url.port + url.port + elsif is_https + Net::HTTP.https_default_port + else + Net::HTTP.http_default_port + end + + http = Net::HTTP.new(url.host, port) + http.use_ssl = is_https + http.max_retries = @max_retries + http + end + + # @return [String] + def inspect + "#<#{self.class.name}:0x#{object_id.to_s(16)} @base_url=#{@base_url.inspect}>" + end + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/inferred_auth_provider.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/inferred_auth_provider.rb new file mode 100644 index 000000000000..c925f197eac0 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/inferred_auth_provider.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +module Seed + module Internal + class InferredAuthProvider + BUFFER_IN_SECONDS = 120 # 2 minutes + + # @param auth_client [untyped] + # @param options [Hash[String, untyped]] + # + # @return [void] + def initialize(auth_client:, options:) + @auth_client = auth_client + @options = options + @access_token = nil + @expires_at = nil + end + + # Returns a cached access token, refreshing if necessary. + # Refreshes the token if it's nil, or if we're within the buffer period before expiration. + # + # @return [String] + def token + return refresh if @access_token.nil? || token_needs_refresh? + + @access_token + end + + # Returns the authentication headers to be included in requests. + # + # @return [Hash[String, String]] + def auth_headers + access_token = token + { + "Authorization" => "Bearer #{access_token}" + } + end + # Checks if the token needs to be refreshed. + # Returns true if the token will expire within the buffer period. + # + # @return [Boolean] + private def token_needs_refresh? + return true if @expires_at.nil? + + Time.now >= (@expires_at - BUFFER_IN_SECONDS) + end + # Refreshes the access token by calling the token endpoint. + # + # @return [String] + private def refresh + request_params = {} + + token_response = @auth_client.get_token_with_client_credentials(**request_params, request_options: { base_url: @options[:base_url] }) + + @access_token = token_response.access_token + @expires_at = Time.now + token_response.expires_in + + @access_token + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/iterators/cursor_item_iterator.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/iterators/cursor_item_iterator.rb new file mode 100644 index 000000000000..ab627ffc7025 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/iterators/cursor_item_iterator.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module Seed + module Internal + class CursorItemIterator < ItemIterator + # Instantiates a CursorItemIterator, an Enumerable class which wraps calls to a cursor-based paginated API and yields individual items from it. + # + # @param initial_cursor [String] The initial cursor to use when iterating, if any. + # @param cursor_field [Symbol] The field in API responses to extract the next cursor from. + # @param item_field [Symbol] The field in API responses to extract the items to iterate over. + # @param block [Proc] A block which is responsible for receiving a cursor to use and returning the given page from the API. + # @return [Seed::Internal::CursorItemIterator] + def initialize(initial_cursor:, cursor_field:, item_field:, &) + super() + @item_field = item_field + @page_iterator = CursorPageIterator.new(initial_cursor:, cursor_field:, &) + @page = nil + end + + # Returns the CursorPageIterator mediating access to the underlying API. + # + # @return [Seed::Internal::CursorPageIterator] + def pages + @page_iterator + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/iterators/cursor_page_iterator.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/iterators/cursor_page_iterator.rb new file mode 100644 index 000000000000..f479a749fef9 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/iterators/cursor_page_iterator.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +module Seed + module Internal + class CursorPageIterator + include Enumerable + + # Instantiates a CursorPageIterator, an Enumerable class which wraps calls to a cursor-based paginated API and yields pages of items. + # + # @param initial_cursor [String] The initial cursor to use when iterating, if any. + # @param cursor_field [Symbol] The name of the field in API responses to extract the next cursor from. + # @param block [Proc] A block which is responsible for receiving a cursor to use and returning the given page from the API. + # @return [Seed::Internal::CursorPageIterator] + def initialize(initial_cursor:, cursor_field:, &block) + @need_initial_load = initial_cursor.nil? + @cursor = initial_cursor + @cursor_field = cursor_field + @get_next_page = block + end + + # Iterates over each page returned by the API. + # + # @param block [Proc] The block which each retrieved page is yielded to. + # @return [NilClass] + def each(&block) + while (page = next_page) + block.call(page) + end + end + + # Whether another page will be available from the API. + # + # @return [Boolean] + def next? + @need_initial_load || !@cursor.nil? + end + + # Retrieves the next page from the API. + # + # @return [Boolean] + def next_page + return if !@need_initial_load && @cursor.nil? + + @need_initial_load = false + fetched_page = @get_next_page.call(@cursor) + @cursor = fetched_page.send(@cursor_field) + fetched_page + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/iterators/item_iterator.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/iterators/item_iterator.rb new file mode 100644 index 000000000000..1284fb0fd367 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/iterators/item_iterator.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +module Seed + module Internal + class ItemIterator + include Enumerable + + # Iterates over each item returned by the API. + # + # @param block [Proc] The block which each retrieved item is yielded to. + # @return [NilClass] + def each(&block) + while (item = next_element) + block.call(item) + end + end + + # Whether another item will be available from the API. + # + # @return [Boolean] + def next? + load_next_page if @page.nil? + return false if @page.nil? + + return true if any_items_in_cached_page? + + load_next_page + any_items_in_cached_page? + end + + # Retrieves the next item from the API. + def next_element + item = next_item_from_cached_page + return item if item + + load_next_page + next_item_from_cached_page + end + + private + + def next_item_from_cached_page + return unless @page + + @page.send(@item_field).shift + end + + def any_items_in_cached_page? + return false unless @page + + !@page.send(@item_field).empty? + end + + def load_next_page + @page = @page_iterator.next_page + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/iterators/offset_item_iterator.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/iterators/offset_item_iterator.rb new file mode 100644 index 000000000000..f8840246686d --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/iterators/offset_item_iterator.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +module Seed + module Internal + class OffsetItemIterator < ItemIterator + # Instantiates an OffsetItemIterator, an Enumerable class which wraps calls to an offset-based paginated API and yields the individual items from it. + # + # @param initial_page [Integer] The initial page or offset to start from when iterating. + # @param item_field [Symbol] The name of the field in API responses to extract the items to iterate over. + # @param has_next_field [Symbol] The name of the field in API responses containing a boolean of whether another page exists. + # @param step [Boolean] If true, treats the page number as a true offset (i.e. increments the page number by the number of items returned from each call rather than just 1) + # @param block [Proc] A block which is responsible for receiving a page number to use and returning the given page from the API. + # + # @return [Seed::Internal::OffsetItemIterator] + def initialize(initial_page:, item_field:, has_next_field:, step:, &) + super() + @item_field = item_field + @page_iterator = OffsetPageIterator.new(initial_page:, item_field:, has_next_field:, step:, &) + @page = nil + end + + # Returns the OffsetPageIterator that is mediating access to the underlying API. + # + # @return [Seed::Internal::OffsetPageIterator] + def pages + @page_iterator + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/iterators/offset_page_iterator.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/iterators/offset_page_iterator.rb new file mode 100644 index 000000000000..051b65c5774c --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/iterators/offset_page_iterator.rb @@ -0,0 +1,83 @@ +# frozen_string_literal: true + +module Seed + module Internal + class OffsetPageIterator + include Enumerable + + # Instantiates an OffsetPageIterator, an Enumerable class which wraps calls to an offset-based paginated API and yields pages of items from it. + # + # @param initial_page [Integer] The initial page to use when iterating, if any. + # @param item_field [Symbol] The field to pull the list of items to iterate over. + # @param has_next_field [Symbol] The field to pull the boolean of whether a next page exists from, if any. + # @param step [Boolean] If true, treats the page number as a true offset (i.e. increments the page number by the number of items returned from each call rather than just 1) + # @param block [Proc] A block which is responsible for receiving a page number to use and returning the given page from the API. + # @return [Seed::Internal::OffsetPageIterator] + def initialize(initial_page:, item_field:, has_next_field:, step:, &block) + @page_number = initial_page || (step ? 0 : 1) + @item_field = item_field + @has_next_field = has_next_field + @step = step + @get_next_page = block + + # A cache of whether the API has another page, if it gives us that information... + @next_page = nil + # ...or the actual next page, preloaded, if it doesn't. + @has_next_page = nil + end + + # Iterates over each page returned by the API. + # + # @param block [Proc] The block which each retrieved page is yielded to. + # @return [NilClass] + def each(&block) + while (page = next_page) + block.call(page) + end + end + + # Whether another page will be available from the API. + # + # @return [Boolean] + def next? + return @has_next_page unless @has_next_page.nil? + return true if @next_page + + fetched_page = @get_next_page.call(@page_number) + fetched_page_items = fetched_page&.send(@item_field) + if fetched_page_items.nil? || fetched_page_items.empty? + @has_next_page = false + else + @next_page = fetched_page + true + end + end + + # Returns the next page from the API. + def next_page + return nil if @page_number.nil? + + if @next_page + this_page = @next_page + @next_page = nil + else + this_page = @get_next_page.call(@page_number) + end + + @has_next_page = this_page&.send(@has_next_field) if @has_next_field + + items = this_page.send(@item_field) + if items.nil? || items.empty? + @page_number = nil + return nil + elsif @step + @page_number += items.length + else + @page_number += 1 + end + + this_page + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/json/request.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/json/request.rb new file mode 100644 index 000000000000..15773d44c641 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/json/request.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +module Seed + module Internal + module JSON + # @api private + class Request < Seed::Internal::Http::BaseRequest + attr_reader :body + + # @param base_url [String] The base URL for the request + # @param path [String] The path for the request + # @param method [Symbol] The HTTP method for the request (:get, :post, etc.) + # @param headers [Hash] Additional headers for the request (optional) + # @param query [Hash] Query parameters for the request (optional) + # @param body [Object, nil] The JSON request body (optional) + # @param request_options [Seed::RequestOptions, Hash{Symbol=>Object}, nil] + def initialize(base_url:, path:, method:, headers: {}, query: {}, body: nil, request_options: {}) + super(base_url:, path:, method:, headers:, query:, request_options:) + + @body = body + end + + # @return [Hash] The encoded HTTP request headers. + def encode_headers + additional_headers = @request_options&.dig(:additional_headers) || @request_options&.dig("additional_headers") || {} + { + "Content-Type" => "application/json", + "Accept" => "application/json" + }.merge(@headers).merge(additional_headers) + end + + # @return [String, nil] The encoded HTTP request body. + def encode_body + @body.nil? ? nil : ::JSON.generate(@body) + end + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/json/serializable.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/json/serializable.rb new file mode 100644 index 000000000000..f80a15fb962c --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/json/serializable.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Seed + module Internal + module JSON + module Serializable + # Loads data from JSON into its deserialized form + # + # @param str [String] Raw JSON to load into an object + # @return [Object] + def load(str) + raise NotImplementedError + end + + # Dumps data from its deserialized form into JSON + # + # @param value [Object] The deserialized value + # @return [String] + def dump(value) + raise NotImplementedError + end + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/multipart/multipart_encoder.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/multipart/multipart_encoder.rb new file mode 100644 index 000000000000..307ad7436a57 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/multipart/multipart_encoder.rb @@ -0,0 +1,141 @@ +# frozen_string_literal: true + +module Seed + module Internal + module Multipart + # Encodes parameters into a `multipart/form-data` payload as described by RFC + # 2388: + # + # https://tools.ietf.org/html/rfc2388 + # + # This is most useful for transferring file-like objects. + # + # Parameters should be added with `#encode`. When ready, use `#body` to get + # the encoded result and `#content_type` to get the value that should be + # placed in the `Content-Type` header of a subsequent request (which includes + # a boundary value). + # + # This abstraction is heavily inspired by Stripe's multipart/form-data implementation, + # which can be found here: + # + # https://github.com/stripe/stripe-ruby/blob/ca00b676f04ac421cf5cb5ff0325f243651677b6/lib/stripe/multipart_encoder.rb#L18 + # + # @api private + class Encoder + CONTENT_TYPE = "multipart/form-data" + CRLF = "\r\n" + + attr_reader :boundary, :body + + def initialize + # Chose the same number of random bytes that Go uses in its standard + # library implementation. Easily enough entropy to ensure that it won't + # be present in a file we're sending. + @boundary = SecureRandom.hex(30) + + @body = String.new + @closed = false + @first_field = true + end + + # Gets the content type string including the boundary. + # + # @return [String] The content type with boundary + def content_type + "#{CONTENT_TYPE}; boundary=#{@boundary}" + end + + # Encode the given FormData object into a multipart/form-data payload. + # + # @param form_data [FormData] The form data to encode + # @return [String] The encoded body. + def encode(form_data) + return "" if form_data.parts.empty? + + form_data.parts.each do |part| + write_part(part) + end + close + + @body + end + + # Writes a FormDataPart to the encoder. + # + # @param part [FormDataPart] The part to write + # @return [nil] + def write_part(part) + raise "Cannot write to closed encoder" if @closed + + write_field( + name: part.name, + data: part.contents, + filename: part.filename, + headers: part.headers + ) + + nil + end + + # Writes a field to the encoder. + # + # @param name [String] The field name + # @param data [String] The field data + # @param filename [String, nil] Optional filename + # @param headers [Hash, nil] Optional additional headers + # @return [nil] + def write_field(name:, data:, filename: nil, headers: nil) + raise "Cannot write to closed encoder" if @closed + + if @first_field + @first_field = false + else + @body << CRLF + end + + @body << "--#{@boundary}#{CRLF}" + @body << %(Content-Disposition: form-data; name="#{escape(name.to_s)}") + @body << %(; filename="#{escape(filename)}") if filename + @body << CRLF + + if headers + headers.each do |key, value| + @body << "#{key}: #{value}#{CRLF}" + end + elsif filename + # Default content type for files. + @body << "Content-Type: application/octet-stream#{CRLF}" + end + + @body << CRLF + @body << data.to_s + + nil + end + + # Finalizes the encoder by writing the final boundary. + # + # @return [nil] + def close + raise "Encoder already closed" if @closed + + @body << CRLF + @body << "--#{@boundary}--" + @closed = true + + nil + end + + private + + # Escapes quotes for use in header values and replaces line breaks with spaces. + # + # @param str [String] The string to escape + # @return [String] The escaped string + def escape(str) + str.to_s.gsub('"', "%22").tr("\n", " ").tr("\r", " ") + end + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/multipart/multipart_form_data.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/multipart/multipart_form_data.rb new file mode 100644 index 000000000000..5be1bb25341f --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/multipart/multipart_form_data.rb @@ -0,0 +1,78 @@ +# frozen_string_literal: true + +module Seed + module Internal + module Multipart + # @api private + class FormData + # @return [Array] The parts in this multipart form data. + attr_reader :parts + + # @return [Encoder] The encoder for this multipart form data. + private attr_reader :encoder + + def initialize + @encoder = Encoder.new + @parts = [] + end + + # Adds a new part to the multipart form data. + # + # @param name [String] The name of the form field + # @param value [String, Integer, Float, Boolean, #read] The value of the field + # @param content_type [String, nil] Optional content type + # @return [self] Returns self for chaining + def add(name:, value:, content_type: nil) + headers = content_type ? { "Content-Type" => content_type } : nil + add_part(FormDataPart.new(name:, value:, headers:)) + end + + # Adds a file to the multipart form data. + # + # @param name [String] The name of the form field + # @param file [#read] The file or readable object + # @param filename [String, nil] Optional filename (defaults to basename of path for File objects) + # @param content_type [String, nil] Optional content type (e.g. "image/png") + # @return [self] Returns self for chaining + def add_file(name:, file:, filename: nil, content_type: nil) + headers = content_type ? { "Content-Type" => content_type } : nil + filename ||= filename_for(file) + add_part(FormDataPart.new(name:, value: file, filename:, headers:)) + end + + # Adds a pre-created part to the multipart form data. + # + # @param part [FormDataPart] The part to add + # @return [self] Returns self for chaining + def add_part(part) + @parts << part + self + end + + # Gets the content type string including the boundary. + # + # @return [String] The content type with boundary. + def content_type + @encoder.content_type + end + + # Encode the multipart form data into a multipart/form-data payload. + # + # @return [String] The encoded body. + def encode + @encoder.encode(self) + end + + private + + def filename_for(file) + if file.is_a?(::File) || file.respond_to?(:path) + ::File.basename(file.path) + elsif file.respond_to?(:name) + file.name + end + end + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/multipart/multipart_form_data_part.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/multipart/multipart_form_data_part.rb new file mode 100644 index 000000000000..de45416ee087 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/multipart/multipart_form_data_part.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require "securerandom" + +module Seed + module Internal + module Multipart + # @api private + class FormDataPart + attr_reader :name, :contents, :filename, :headers + + # @param name [String] The name of the form field + # @param value [String, Integer, Float, Boolean, File, #read] The value of the field + # @param filename [String, nil] Optional filename for file uploads + # @param headers [Hash, nil] Optional additional headers + def initialize(name:, value:, filename: nil, headers: nil) + @name = name + @contents = convert_to_content(value) + @filename = filename + @headers = headers + end + + # Converts the part to a hash suitable for serialization. + # + # @return [Hash] A hash representation of the part + def to_hash + result = { + name: @name, + contents: @contents + } + result[:filename] = @filename if @filename + result[:headers] = @headers if @headers + result + end + + private + + # Converts various types of values to a content representation + # @param value [String, Integer, Float, Boolean, #read] The value to convert + # @return [String] The string representation of the value + def convert_to_content(value) + if value.respond_to?(:read) + value.read + else + value.to_s + end + end + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/multipart/multipart_request.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/multipart/multipart_request.rb new file mode 100644 index 000000000000..915dada8c56e --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/multipart/multipart_request.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +module Seed + module Internal + module Multipart + # @api private + class Request < Seed::Internal::Http::BaseRequest + attr_reader :body + + # @param base_url [String] The base URL for the request + # @param path [String] The path for the request + # @param method [Symbol] The HTTP method for the request (:get, :post, etc.) + # @param headers [Hash] Additional headers for the request (optional) + # @param query [Hash] Query parameters for the request (optional) + # @param body [MultipartFormData, nil] The multipart form data for the request (optional) + # @param request_options [Seed::RequestOptions, Hash{Symbol=>Object}, nil] + def initialize(base_url:, path:, method:, headers: {}, query: {}, body: nil, request_options: {}) + super(base_url:, path:, method:, headers:, query:, request_options:) + + @body = body + end + + # @return [Hash] The encoded HTTP request headers. + def encode_headers + additional_headers = @request_options&.dig(:additional_headers) || @request_options&.dig("additional_headers") || {} + { + "Content-Type" => @body.content_type + }.merge(@headers).merge(additional_headers) + end + + # @return [String, nil] The encoded HTTP request body. + def encode_body + @body&.encode + end + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/types/array.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/types/array.rb new file mode 100644 index 000000000000..f3c7c1bd9549 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/types/array.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +module Seed + module Internal + module Types + # An array of a specific type + class Array + include Seed::Internal::Types::Type + + attr_reader :type + + class << self + # Instantiates a new `Array` of a given type + # + # @param type [Object] The member type of this array + # + # @return [Seed::Internal::Types::Array] + def [](type) + new(type) + end + end + + # @api private + def initialize(type) + @type = type + end + + # Coerces a value into this array + # + # @param value [Object] + # @option strict [Boolean] + # @return [::Array] + def coerce(value, strict: strict?) + unless value.is_a?(::Array) + raise Errors::TypeError, "cannot coerce `#{value.class}` to Array<#{type}>" if strict + + return value + end + + value.map do |element| + Utils.coerce(type, element, strict: strict) + end + end + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/types/boolean.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/types/boolean.rb new file mode 100644 index 000000000000..de0503d9e56b --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/types/boolean.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +module Seed + module Internal + module Types + module Boolean + extend Seed::Internal::Types::Union + + member TrueClass + member FalseClass + + # Overrides the base coercion method for enums to allow integer and string values to become booleans + # + # @param value [Object] + # @option strict [Boolean] + # @return [Object] + def self.coerce(value, strict: strict?) + case value + when TrueClass, FalseClass + value + when Integer + return value == 1 + when String + return %w[1 true].include?(value) + end + + raise Errors::TypeError, "cannot coerce `#{value.class}` to Boolean" if strict + + value + end + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/types/enum.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/types/enum.rb new file mode 100644 index 000000000000..72e45e4c1f27 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/types/enum.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +module Seed + module Internal + module Types + # Module for defining enums + module Enum + include Type + + # @api private + # + # @return [Array] + def values + @values ||= constants.map { |c| const_get(c) } + end + + # @api private + def finalize! + values + end + + # @api private + def strict? + @strict ||= false + end + + # @api private + def strict! + @strict = true + end + + def coerce(value, strict: strict?) + coerced_value = Utils.coerce(Symbol, value) + + return coerced_value if values.include?(coerced_value) + + raise Errors::TypeError, "`#{value}` not in enum #{self}" if strict + + value + end + + # Parse JSON string and coerce to the enum value + # + # @param str [String] JSON string to parse + # @return [String] The enum value + def load(str) + coerce(::JSON.parse(str)) + end + + def inspect + "#{name}[#{values.join(", ")}]" + end + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/types/hash.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/types/hash.rb new file mode 100644 index 000000000000..d8bffa63ac11 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/types/hash.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +module Seed + module Internal + module Types + class Hash + include Type + + attr_reader :key_type, :value_type + + class << self + def [](key_type, value_type) + new(key_type, value_type) + end + end + + def initialize(key_type, value_type) + @key_type = key_type + @value_type = value_type + end + + def coerce(value, strict: strict?) + unless value.is_a?(::Hash) + raise Errors::TypeError, "not hash" if strict + + return value + end + + value.to_h do |k, v| + [Utils.coerce(key_type, k, strict: strict), Utils.coerce(value_type, v, strict: strict)] + end + end + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/types/model.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/types/model.rb new file mode 100644 index 000000000000..9b1480c7333a --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/types/model.rb @@ -0,0 +1,200 @@ +# frozen_string_literal: true + +module Seed + module Internal + module Types + # @abstract + # + # An abstract model that all data objects will inherit from + class Model + include Type + + class << self + # The defined fields for this model + # + # @api private + # + # @return [Hash] + def fields + @fields ||= if self < Seed::Internal::Types::Model + superclass.fields.dup + else + {} + end + end + + # Any extra fields that have been created from instantiation + # + # @api private + # + # @return [Hash] + def extra_fields + @extra_fields ||= {} + end + + # Define a new field on this model + # + # @param name [Symbol] The name of the field + # @param type [Class] Type of the field + # @option optional [Boolean] If it is an optional field + # @option nullable [Boolean] If it is a nullable field + # @option api_name [Symbol, String] Name in the API of this field. When serializing/deserializing, will use + # this field name + # @return [void] + def field(name, type, optional: false, nullable: false, api_name: nil, default: nil) + add_field_definition(name: name, type: type, optional: optional, nullable: nullable, api_name: api_name, + default: default) + + define_accessor(name) + define_setter(name) + end + + # Define a new literal for this model + # + # @param name [Symbol] + # @param value [Object] + # @option api_name [Symbol, String] + # @return [void] + def literal(name, value, api_name: nil) + add_field_definition(name: name, type: value.class, optional: false, nullable: false, api_name: api_name, + value: value) + + define_accessor(name) + end + + # Adds a new field definition into the class's fields registry + # + # @api private + # + # @param name [Symbol] + # @param type [Class] + # @option optional [Boolean] + # @return [void] + private def add_field_definition(name:, type:, optional:, nullable:, api_name:, default: nil, value: nil) + fields[name.to_sym] = + Field.new(name: name, type: type, optional: optional, nullable: nullable, api_name: api_name, + value: value, default: default) + end + + # Adds a new field definition into the class's extra fields registry + # + # @api private + # + # @param name [Symbol] + # @param type [Class] + # @option required [Boolean] + # @option optional [Boolean] + # @return [void] + def add_extra_field_definition(name:, type:) + return if extra_fields.key?(name.to_sym) + + extra_fields[name.to_sym] = Field.new(name: name, type: type, optional: true, nullable: false) + + define_accessor(name) + define_setter(name) + end + + # @api private + private def define_accessor(name) + method_name = name.to_sym + + define_method(method_name) do + @data[name] + end + end + + # @api private + private def define_setter(name) + method_name = :"#{name}=" + + define_method(method_name) do |val| + @data[name] = val + end + end + + def coerce(value, strict: (respond_to?(:strict?) ? strict? : false)) # rubocop:disable Lint/UnusedMethodArgument + return value if value.is_a?(self) + + return value unless value.is_a?(::Hash) + + new(value) + end + + def load(str) + coerce(::JSON.parse(str, symbolize_names: true)) + end + + def ===(instance) + instance.class.ancestors.include?(self) + end + end + + # Creates a new instance of this model + # TODO: Should all this logic be in `#coerce` instead? + # + # @param values [Hash] + # @option strict [Boolean] + # @return [self] + def initialize(values = {}) + @data = {} + + values = Utils.symbolize_keys(values.dup) + + self.class.fields.each do |field_name, field| + value = values.delete(field.api_name.to_sym) || values.delete(field.api_name) || values.delete(field_name) + + field_value = value || (if field.literal? + field.value + elsif field.default + field.default + end) + + @data[field_name] = Utils.coerce(field.type, field_value) + end + + # Any remaining values in the input become extra fields + values.each do |name, value| + self.class.add_extra_field_definition(name: name, type: value.class) + + @data[name.to_sym] = value + end + end + + def to_h + self.class.fields.merge(self.class.extra_fields).each_with_object({}) do |(name, field), acc| + # If there is a value present in the data, use that value + # If there is a `nil` value present in the data, and it is optional but NOT nullable, exclude key altogether + # If there is a `nil` value present in the data, and it is optional and nullable, use the nil value + + value = @data[name] + + next if value.nil? && field.optional && !field.nullable + + if value.is_a?(::Array) + value = value.map { |item| item.respond_to?(:to_h) ? item.to_h : item } + elsif value.respond_to?(:to_h) + value = value.to_h + end + + acc[field.api_name] = value + end + end + + def ==(other) + self.class == other.class && to_h == other.to_h + end + + # @return [String] + def inspect + attrs = @data.map do |name, value| + field = self.class.fields[name] || self.class.extra_fields[name] + display_value = field&.sensitive? ? "[REDACTED]" : value.inspect + "#{name}=#{display_value}" + end + + "#<#{self.class.name}:0x#{object_id&.to_s(16)} #{attrs.join(" ")}>" + end + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/types/model/field.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/types/model/field.rb new file mode 100644 index 000000000000..6ce0186f6a5d --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/types/model/field.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +module Seed + module Internal + module Types + class Model + # Definition of a field on a model + class Field + SENSITIVE_FIELD_NAMES = %i[ + password secret token api_key apikey access_token refresh_token + client_secret client_id credential bearer authorization + ].freeze + + attr_reader :name, :type, :optional, :nullable, :api_name, :value, :default + + def initialize(name:, type:, optional: false, nullable: false, api_name: nil, value: nil, default: nil) + @name = name.to_sym + @type = type + @optional = optional + @nullable = nullable + @api_name = api_name || name.to_s + @value = value + @default = default + end + + def literal? + !value.nil? + end + + def sensitive? + SENSITIVE_FIELD_NAMES.include?(@name) || + SENSITIVE_FIELD_NAMES.any? { |sensitive| @name.to_s.include?(sensitive.to_s) } + end + end + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/types/type.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/types/type.rb new file mode 100644 index 000000000000..5866caf1dbda --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/types/type.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +module Seed + module Internal + module Types + # @abstract + module Type + include Seed::Internal::JSON::Serializable + + # Coerces a value to this type + # + # @param value [unknown] + # @option strict [Boolean] If we should strictly coerce this value + def coerce(value, strict: strict?) + raise NotImplementedError + end + + # Returns if strictness is on for this type, defaults to `false` + # + # @return [Boolean] + def strict? + @strict ||= false + end + + # Enable strictness by default for this type + # + # @return [void] + def strict! + @strict = true + self + end + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/types/union.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/types/union.rb new file mode 100644 index 000000000000..507565d2210d --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/types/union.rb @@ -0,0 +1,133 @@ +# frozen_string_literal: true + +module Seed + module Internal + module Types + # Define a union between two types + module Union + include Seed::Internal::Types::Type + + def members + @members ||= [] + end + + # Add a member to this union + # + # @param type [Object] + # @option key [Symbol, String] + # @return [void] + def member(type, key: nil) + members.push([key, Utils.wrap_type(type)]) + self + end + + def member?(type) + members.any? { |_key, type_fn| type == type_fn.call } + end + + # Set the discriminant for this union + # + # @param key [Symbol, String] + # @return [void] + def discriminant(key) + @discriminant = key + end + + # @api private + private def discriminated? + !@discriminant.nil? + end + + # Check if value matches a type, handling type wrapper instances + # (Internal::Types::Hash and Internal::Types::Array instances) + # + # @param value [Object] + # @param member_type [Object] + # @return [Boolean] + private def type_matches?(value, member_type) + case member_type + when Seed::Internal::Types::Hash + value.is_a?(::Hash) + when Seed::Internal::Types::Array + value.is_a?(::Array) + when Class, Module + value.is_a?(member_type) + else + false + end + end + + # Resolves the type of a value to be one of the members + # + # @param value [Object] + # @return [Class] + private def resolve_member(value) + if discriminated? && value.is_a?(::Hash) + discriminant_value = value.fetch(@discriminant, nil) + + return if discriminant_value.nil? + + members.to_h[discriminant_value]&.call + else + # First try exact type matching + result = members.find do |_key, mem| + member_type = Utils.unwrap_type(mem) + type_matches?(value, member_type) + end&.last&.call + + return result if result + + # For Hash values, try to coerce into Model member types + if value.is_a?(::Hash) + members.find do |_key, mem| + member_type = Utils.unwrap_type(mem) + # Check if member_type is a Model class + next unless member_type.is_a?(Class) && member_type <= Model + + # Try to coerce the hash into this model type with strict mode + begin + candidate = Utils.coerce(member_type, value, strict: true) + + # Validate that all required (non-optional) fields are present + # This ensures undiscriminated unions properly distinguish between member types + member_type.fields.each do |field_name, field| + raise Errors::TypeError, "Required field `#{field_name}` missing for union member #{member_type.name}" if candidate.instance_variable_get(:@data)[field_name].nil? && !field.optional + end + + true + rescue Errors::TypeError + false + end + end&.last&.call + end + end + end + + def coerce(value, strict: strict?) + type = resolve_member(value) + + unless type + return value unless strict + + if discriminated? + raise Errors::TypeError, + "value of type `#{value.class}` not member of union #{self}" + end + + raise Errors::TypeError, "could not resolve to member of union #{self}" + end + + Utils.coerce(type, value, strict: strict) + end + + # Parse JSON string and coerce to the correct union member type + # + # @param str [String] JSON string to parse + # @return [Object] Coerced value matching a union member + def load(str) + coerce(::JSON.parse(str, symbolize_names: true)) + end + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/types/unknown.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/types/unknown.rb new file mode 100644 index 000000000000..7b58de956da9 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/types/unknown.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Seed + module Internal + module Types + module Unknown + include Seed::Internal::Types::Type + + def coerce(value) + value + end + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/types/utils.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/types/utils.rb new file mode 100644 index 000000000000..4685ec234596 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/internal/types/utils.rb @@ -0,0 +1,101 @@ +# frozen_string_literal: true + +module Seed + module Internal + module Types + # Utilities for dealing with and checking types + module Utils + # Wraps a type into a type function + # + # @param type [Proc, Object] + # @return [Proc] + def self.wrap_type(type) + case type + when Proc + type + else + -> { type } + end + end + + # Resolves a type or type function into a type + # + # @param type [Proc, Object] + # @return [Object] + def self.unwrap_type(type) + type.is_a?(Proc) ? type.call : type + end + + def self.coerce(target, value, strict: false) + type = unwrap_type(target) + + case type + in Array + case value + when ::Array + return type.coerce(value, strict: strict) + when Set, ::Hash + return coerce(type, value.to_a) + end + in Hash + case value + when ::Hash + return type.coerce(value, strict: strict) + when ::Array + return coerce(type, value.to_h) + end + in ->(t) { t <= NilClass } + return nil + in ->(t) { t <= String } + case value + when String, Symbol, Numeric, TrueClass, FalseClass + return value.to_s + end + in ->(t) { t <= Symbol } + case value + when Symbol, String + return value.to_sym + end + in ->(t) { t <= Integer } + case value + when Numeric, String, Time + return value.to_i + end + in ->(t) { t <= Float } + case value + when Numeric, Time, String + return value.to_f + end + in ->(t) { t <= Model } + case value + when type + return value + when ::Hash + return type.coerce(value, strict: strict) + end + in Module + case type + in ->(t) { + t.singleton_class.included_modules.include?(Enum) || + t.singleton_class.included_modules.include?(Union) + } + return type.coerce(value, strict: strict) + else + value + end + else + value + end + + raise Errors::TypeError, "cannot coerce value of type `#{value.class}` to `#{target}`" if strict + + value + end + + def self.symbolize_keys(hash) + hash.transform_keys(&:to_sym) + end + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/nested/api/client.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/nested/api/client.rb new file mode 100644 index 000000000000..f29889a851f1 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/nested/api/client.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +module Seed + module Nested + module Api + class Client + # @param client [Seed::Internal::Http::RawClient] + # + # @return [void] + def initialize(client:) + @client = client + end + + # @param request_options [Hash] + # @param params [Hash] + # @option request_options [String] :base_url + # @option request_options [Hash{String => Object}] :additional_headers + # @option request_options [Hash{String => Object}] :additional_query_parameters + # @option request_options [Hash{String => Object}] :additional_body_parameters + # @option request_options [Integer] :timeout_in_seconds + # + # @return [untyped] + def get_something(request_options: {}, **_params) + request = Seed::Internal::JSON::Request.new( + base_url: request_options[:base_url], + method: "GET", + path: "/nested/get-something", + request_options: request_options + ) + begin + response = @client.send(request) + rescue Net::HTTPRequestTimeout + raise Seed::Errors::TimeoutError + end + code = response.code.to_i + return if code.between?(200, 299) + + error_class = Seed::Errors::ResponseError.subclass_for_code(code) + raise error_class.new(response.body, code: code) + end + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/nested/client.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/nested/client.rb new file mode 100644 index 000000000000..b52615d08246 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/nested/client.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Seed + module Nested + class Client + # @param client [Seed::Internal::Http::RawClient] + # + # @return [void] + def initialize(client:) + @client = client + end + + # @return [Seed::Api::Client] + def api + @api ||= Seed::Nested::Api::Client.new(client: @client) + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/nested_no_auth/api/client.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/nested_no_auth/api/client.rb new file mode 100644 index 000000000000..b668214f9c27 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/nested_no_auth/api/client.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +module Seed + module NestedNoAuth + module Api + class Client + # @param client [Seed::Internal::Http::RawClient] + # + # @return [void] + def initialize(client:) + @client = client + end + + # @param request_options [Hash] + # @param params [Hash] + # @option request_options [String] :base_url + # @option request_options [Hash{String => Object}] :additional_headers + # @option request_options [Hash{String => Object}] :additional_query_parameters + # @option request_options [Hash{String => Object}] :additional_body_parameters + # @option request_options [Integer] :timeout_in_seconds + # + # @return [untyped] + def get_something(request_options: {}, **_params) + request = Seed::Internal::JSON::Request.new( + base_url: request_options[:base_url], + method: "GET", + path: "/nested-no-auth/get-something", + request_options: request_options + ) + begin + response = @client.send(request) + rescue Net::HTTPRequestTimeout + raise Seed::Errors::TimeoutError + end + code = response.code.to_i + return if code.between?(200, 299) + + error_class = Seed::Errors::ResponseError.subclass_for_code(code) + raise error_class.new(response.body, code: code) + end + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/nested_no_auth/client.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/nested_no_auth/client.rb new file mode 100644 index 000000000000..6a790c64056a --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/nested_no_auth/client.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Seed + module NestedNoAuth + class Client + # @param client [Seed::Internal::Http::RawClient] + # + # @return [void] + def initialize(client:) + @client = client + end + + # @return [Seed::Api::Client] + def api + @api ||= Seed::NestedNoAuth::Api::Client.new(client: @client) + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/simple/client.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/simple/client.rb new file mode 100644 index 000000000000..840e4f04acb8 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/simple/client.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +module Seed + module Simple + class Client + # @param client [Seed::Internal::Http::RawClient] + # + # @return [void] + def initialize(client:) + @client = client + end + + # @param request_options [Hash] + # @param params [Hash] + # @option request_options [String] :base_url + # @option request_options [Hash{String => Object}] :additional_headers + # @option request_options [Hash{String => Object}] :additional_query_parameters + # @option request_options [Hash{String => Object}] :additional_body_parameters + # @option request_options [Integer] :timeout_in_seconds + # + # @return [untyped] + def get_something(request_options: {}, **_params) + request = Seed::Internal::JSON::Request.new( + base_url: request_options[:base_url], + method: "GET", + path: "/get-something", + request_options: request_options + ) + begin + response = @client.send(request) + rescue Net::HTTPRequestTimeout + raise Seed::Errors::TimeoutError + end + code = response.code.to_i + return if code.between?(200, 299) + + error_class = Seed::Errors::ResponseError.subclass_for_code(code) + raise error_class.new(response.body, code: code) + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/version.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/version.rb new file mode 100644 index 000000000000..00dd45cdd958 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/lib/seed/version.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +module Seed + VERSION = "0.0.1" +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/reference.md b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/reference.md new file mode 100644 index 000000000000..de8faeb332e5 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/reference.md @@ -0,0 +1,233 @@ +# Reference +## Auth +
client.auth.get_token_with_client_credentials(request) -> Seed::Auth::Types::TokenResponse +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```ruby +client.auth.get_token_with_client_credentials( + client_id: 'client_id', + client_secret: 'client_secret', + audience: 'https://api.example.com', + grant_type: 'client_credentials', + scope: 'scope' +); +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `Seed::Auth::Types::GetTokenRequest` + +
+
+ +
+
+ +**request_options:** `Seed::Auth::RequestOptions` + +
+
+
+
+ + +
+
+
+ +
client.auth.refresh_token(request) -> Seed::Auth::Types::TokenResponse +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```ruby +client.auth.refresh_token( + client_id: 'client_id', + client_secret: 'client_secret', + refresh_token: 'refresh_token', + audience: 'https://api.example.com', + grant_type: 'refresh_token', + scope: 'scope' +); +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `Seed::Auth::Types::RefreshTokenRequest` + +
+
+ +
+
+ +**request_options:** `Seed::Auth::RequestOptions` + +
+
+
+
+ + +
+
+
+ +## NestedNoAuth Api +
client.nested_no_auth.api.get_something() -> +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```ruby +client.nested_no_auth.api.get_something(); +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `Seed::NestedNoAuth::Api::RequestOptions` + +
+
+
+
+ + +
+
+
+ +## Nested Api +
client.nested.api.get_something() -> +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```ruby +client.nested.api.get_something(); +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `Seed::Nested::Api::RequestOptions` + +
+
+
+
+ + +
+
+
+ +## Simple +
client.simple.get_something() -> +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```ruby +client.simple.get_something(); +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `Seed::Simple::RequestOptions` + +
+
+
+
+ + +
+
+
diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/seed.gemspec b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/seed.gemspec new file mode 100644 index 000000000000..23b6a2279add --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/seed.gemspec @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require_relative "lib/seed/version" +require_relative "custom.gemspec" + +# NOTE: A handful of these fields are required as part of the Ruby specification. +# You can change them here or overwrite them in the custom gemspec file. +Gem::Specification.new do |spec| + spec.name = "seed" + spec.authors = ["Seed"] + spec.version = Seed::VERSION + spec.summary = "Ruby client library for the Seed API" + spec.description = "The Seed Ruby library provides convenient access to the Seed API from Ruby." + spec.required_ruby_version = ">= 3.3.0" + spec.metadata["rubygems_mfa_required"] = "true" + + # Specify which files should be added to the gem when it is released. + # The `git ls-files -z` loads the files in the RubyGem that have been added into git. + gemspec = File.basename(__FILE__) + spec.files = IO.popen(%w[git ls-files -z], chdir: __dir__, err: IO::NULL) do |ls| + ls.readlines("\x0", chomp: true).reject do |f| + (f == gemspec) || + f.start_with?(*%w[bin/ test/ spec/ features/ .git appveyor Gemfile]) + end + end + spec.bindir = "exe" + spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) } + spec.require_paths = ["lib"] + + # For more information and examples about making a new gem, check out our + # guide at: https://bundler.io/guides/creating_gem.html + + # Load custom gemspec configuration if it exists + custom_gemspec_file = File.join(__dir__, "custom.gemspec.rb") + add_custom_gemspec_data(spec) if File.exist?(custom_gemspec_file) +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/snippet.json b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/test/custom.test.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/test/custom.test.rb new file mode 100644 index 000000000000..4bd57989d43d --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/test/custom.test.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +# This is a custom test file, if you wish to add more tests +# to your SDK. +# Be sure to mark this file in `.fernignore`. +# +# If you include example requests/responses in your fern definition, +# you will have tests automatically generated for you. + +# This test is run via command line: rake customtest +describe "Custom Test" do + it "Default" do + refute false + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/test/test_helper.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/test/test_helper.rb new file mode 100644 index 000000000000..b086fe6d76ec --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/test/test_helper.rb @@ -0,0 +1,3 @@ +# frozen_string_literal: true + +require_relative "../lib/seed" diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/test/unit/internal/iterators/test_cursor_item_iterator.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/test/unit/internal/iterators/test_cursor_item_iterator.rb new file mode 100644 index 000000000000..5008f6abf69f --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/test/unit/internal/iterators/test_cursor_item_iterator.rb @@ -0,0 +1,189 @@ +# frozen_string_literal: true + +require "minitest/autorun" +require "stringio" +require "json" +require "test_helper" + +NUMBERS = (1..65).to_a +PageResponse = Struct.new(:cards, :next_cursor, keyword_init: true) + +class CursorItemIteratorTest < Minitest::Test + def make_iterator(initial_cursor:) + @times_called = 0 + + Seed::Internal::CursorItemIterator.new(initial_cursor:, cursor_field: :next_cursor, item_field: :cards) do |cursor| + @times_called += 1 + cursor ||= 0 + next_cursor = cursor + 10 + PageResponse.new( + cards: NUMBERS[cursor...next_cursor], + next_cursor: next_cursor < NUMBERS.length ? next_cursor : nil + ) + end + end + + def test_item_iterator_can_iterate_to_exhaustion + iterator = make_iterator(initial_cursor: 0) + + assert_equal NUMBERS, iterator.to_a + assert_equal 7, @times_called + + iterator = make_iterator(initial_cursor: 10) + + assert_equal (11..65).to_a, iterator.to_a + + iterator = make_iterator(initial_cursor: 5) + + assert_equal (6..65).to_a, iterator.to_a + end + + def test_item_iterator_can_work_without_an_initial_cursor + iterator = make_iterator(initial_cursor: nil) + + assert_equal NUMBERS, iterator.to_a + assert_equal 7, @times_called + end + + def test_items_iterator_iterates_lazily + iterator = make_iterator(initial_cursor: 0) + + assert_equal 0, @times_called + assert_equal 1, iterator.first + assert_equal 1, @times_called + + iterator = make_iterator(initial_cursor: 0) + + assert_equal 0, @times_called + assert_equal (1..15).to_a, iterator.first(15) + assert_equal 2, @times_called + + iterator = make_iterator(initial_cursor: 0) + + assert_equal 0, @times_called + iterator.each do |card| + break if card >= 15 + end + + assert_equal 2, @times_called + end + + def test_items_iterator_implements_enumerable + iterator = make_iterator(initial_cursor: 0) + + assert_equal 0, @times_called + doubled = iterator.map { |card| card * 2 } + + assert_equal 7, @times_called + assert_equal NUMBERS.length, doubled.length + end + + def test_items_iterator_can_be_advanced_manually + iterator = make_iterator(initial_cursor: 0) + + assert_equal 0, @times_called + + items = [] + expected_times_called = 0 + while (item = iterator.next_element) + expected_times_called += 1 if (item % 10) == 1 + + assert_equal expected_times_called, @times_called + assert_equal item != NUMBERS.last, iterator.next?, "#{item} #{iterator}" + items.push(item) + end + + assert_equal 7, @times_called + assert_equal NUMBERS, items + end + + def test_pages_iterator + iterator = make_iterator(initial_cursor: 0).pages + + assert_equal( + [ + (1..10).to_a, + (11..20).to_a, + (21..30).to_a, + (31..40).to_a, + (41..50).to_a, + (51..60).to_a, + (61..65).to_a + ], + iterator.to_a.map(&:cards) + ) + + iterator = make_iterator(initial_cursor: 10).pages + + assert_equal( + [ + (11..20).to_a, + (21..30).to_a, + (31..40).to_a, + (41..50).to_a, + (51..60).to_a, + (61..65).to_a + ], + iterator.to_a.map(&:cards) + ) + end + + def test_pages_iterator_can_work_without_an_initial_cursor + iterator = make_iterator(initial_cursor: nil).pages + + assert_equal 7, iterator.to_a.length + assert_equal 7, @times_called + end + + def test_pages_iterator_iterates_lazily + iterator = make_iterator(initial_cursor: 0).pages + + assert_equal 0, @times_called + iterator.first + + assert_equal 1, @times_called + + iterator = make_iterator(initial_cursor: 0).pages + + assert_equal 0, @times_called + assert_equal 2, iterator.first(2).length + assert_equal 2, @times_called + end + + def test_pages_iterator_knows_whether_another_page_is_upcoming + iterator = make_iterator(initial_cursor: 0).pages + + iterator.each_with_index do |_page, index| + assert_equal index + 1, @times_called + assert_equal index < 6, iterator.next? + end + end + + def test_pages_iterator_can_be_advanced_manually + iterator = make_iterator(initial_cursor: 0).pages + + assert_equal 0, @times_called + + lengths = [] + expected_times_called = 0 + while (page = iterator.next_page) + expected_times_called += 1 + + assert_equal expected_times_called, @times_called + lengths.push(page.cards.length) + end + + assert_equal 7, @times_called + assert_equal [10, 10, 10, 10, 10, 10, 5], lengths + end + + def test_pages_iterator_implements_enumerable + iterator = make_iterator(initial_cursor: 0).pages + + assert_equal 0, @times_called + lengths = iterator.map { |page| page.cards.length } + + assert_equal 7, @times_called + assert_equal [10, 10, 10, 10, 10, 10, 5], lengths + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/test/unit/internal/iterators/test_offset_item_iterator.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/test/unit/internal/iterators/test_offset_item_iterator.rb new file mode 100644 index 000000000000..92576b820128 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/test/unit/internal/iterators/test_offset_item_iterator.rb @@ -0,0 +1,151 @@ +# frozen_string_literal: true + +require "minitest/autorun" +require "stringio" +require "json" +require "test_helper" + +OffsetPageResponse = Struct.new(:items, :has_next, keyword_init: true) +TestIteratorConfig = Struct.new( + :step, + :has_next_field, + :total_item_count, + :per_page, + :initial_page +) do + def first_item_returned + if step + (initial_page || 0) + 1 + else + (((initial_page || 1) - 1) * per_page) + 1 + end + end +end + +LAZY_TEST_ITERATOR_CONFIG = TestIteratorConfig.new(initial_page: 1, step: false, has_next_field: :has_next, total_item_count: 65, per_page: 10) +ALL_TEST_ITERATOR_CONFIGS = [true, false].map do |step| + [:has_next, nil].map do |has_next_field| + [0, 5, 10, 60, 63].map do |total_item_count| + [5, 10].map do |per_page| + initial_pages = [nil, 3, 100] + initial_pages << (step ? 0 : 1) + + initial_pages.map do |initial_page| + TestIteratorConfig.new( + step: step, + has_next_field: has_next_field, + total_item_count: total_item_count, + per_page: per_page, + initial_page: initial_page + ) + end + end + end + end +end.flatten + +class OffsetItemIteratorTest < Minitest::Test + def make_iterator(config) + @times_called = 0 + + items = (1..config.total_item_count).to_a + + Seed::Internal::OffsetItemIterator.new( + initial_page: config.initial_page, + item_field: :items, + has_next_field: config.has_next_field, + step: config.step + ) do |page| + @times_called += 1 + + slice_start = config.step ? page : (page - 1) * config.per_page + slice_end = slice_start + config.per_page + + output = { + items: items[slice_start...slice_end] + } + output[config.has_next_field] = slice_end < items.length if config.has_next_field + + OffsetPageResponse.new(**output) + end + end + + def test_item_iterator_can_iterate_to_exhaustion + ALL_TEST_ITERATOR_CONFIGS.each do |config| + iterator = make_iterator(config) + + assert_equal (config.first_item_returned..config.total_item_count).to_a, iterator.to_a + end + end + + def test_items_iterator_can_be_advanced_manually_and_has_accurate_has_next + ALL_TEST_ITERATOR_CONFIGS.each do |config| + iterator = make_iterator(config) + items = [] + + while (item = iterator.next_element) + assert_equal(item != config.total_item_count, iterator.next?, "#{item} #{iterator}") + items.push(item) + end + + assert_equal (config.first_item_returned..config.total_item_count).to_a, items + end + end + + def test_pages_iterator_can_be_advanced_manually_and_has_accurate_has_next + ALL_TEST_ITERATOR_CONFIGS.each do |config| + iterator = make_iterator(config).pages + pages = [] + + loop do + has_next_output = iterator.next? + page = iterator.next_page + + assert_equal(has_next_output, !page.nil?, "next? was inaccurate: #{config} #{iterator.inspect}") + break if page.nil? + + pages.push(page) + end + + assert_equal pages, make_iterator(config).pages.to_a + end + end + + def test_items_iterator_iterates_lazily + iterator = make_iterator(LAZY_TEST_ITERATOR_CONFIG) + + assert_equal 0, @times_called + assert_equal 1, iterator.first + assert_equal 1, @times_called + + iterator = make_iterator(LAZY_TEST_ITERATOR_CONFIG) + + assert_equal 0, @times_called + assert_equal (1..15).to_a, iterator.first(15) + assert_equal 2, @times_called + + iterator = make_iterator(LAZY_TEST_ITERATOR_CONFIG) + + assert_equal 0, @times_called + iterator.each do |card| + break if card >= 15 + end + + assert_equal 2, @times_called + end + + def test_pages_iterator_iterates_lazily + iterator = make_iterator(LAZY_TEST_ITERATOR_CONFIG).pages + + assert_equal 0, @times_called + iterator.first + + assert_equal 1, @times_called + + iterator = make_iterator(LAZY_TEST_ITERATOR_CONFIG).pages + + assert_equal 0, @times_called + assert_equal 3, iterator.first(3).length + assert_equal 3, @times_called + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/test/unit/internal/types/test_array.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/test/unit/internal/types/test_array.rb new file mode 100644 index 000000000000..e7e6571f03ee --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/test/unit/internal/types/test_array.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +require "test_helper" + +describe Seed::Internal::Types::Array do + module TestArray + StringArray = Seed::Internal::Types::Array[String] + end + + describe "#initialize" do + it "sets the type" do + assert_equal String, TestArray::StringArray.type + end + end + + describe "#coerce" do + it "does not perform coercion if not an array" do + assert_equal 1, TestArray::StringArray.coerce(1) + end + + it "raises an error if not an array and strictness is on" do + assert_raises Seed::Internal::Errors::TypeError do + TestArray::StringArray.coerce(1, strict: true) + end + end + + it "coerces the elements" do + assert_equal %w[foobar 1 true], TestArray::StringArray.coerce(["foobar", 1, true]) + end + + it "raises an error if element of array is not coercable and strictness is on" do + assert_raises Seed::Internal::Errors::TypeError do + TestArray::StringArray.coerce([Object.new], strict: true) + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/test/unit/internal/types/test_boolean.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/test/unit/internal/types/test_boolean.rb new file mode 100644 index 000000000000..cba18e48765b --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/test/unit/internal/types/test_boolean.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +require "test_helper" + +describe Seed::Internal::Types::Boolean do + describe ".coerce" do + it "coerces true/false" do + assert Seed::Internal::Types::Boolean.coerce(true) + refute Seed::Internal::Types::Boolean.coerce(false) + end + + it "coerces an Integer" do + assert Seed::Internal::Types::Boolean.coerce(1) + refute Seed::Internal::Types::Boolean.coerce(0) + end + + it "coerces a String" do + assert Seed::Internal::Types::Boolean.coerce("1") + assert Seed::Internal::Types::Boolean.coerce("true") + refute Seed::Internal::Types::Boolean.coerce("0") + end + + it "passes through other values with strictness off" do + obj = Object.new + + assert_equal obj, Seed::Internal::Types::Boolean.coerce(obj) + end + + it "raises an error with other values with strictness on" do + assert_raises Seed::Internal::Errors::TypeError do + Seed::Internal::Types::Boolean.coerce(Object.new, strict: true) + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/test/unit/internal/types/test_enum.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/test/unit/internal/types/test_enum.rb new file mode 100644 index 000000000000..e8d89bce467f --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/test/unit/internal/types/test_enum.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require "test_helper" + +describe Seed::Internal::Types::Enum do + module EnumTest + module ExampleEnum + extend Seed::Internal::Types::Enum + + FOO = :foo + BAR = :bar + + finalize! + end + end + + describe "#values" do + it "defines values" do + assert_equal %i[foo bar].sort, EnumTest::ExampleEnum.values.sort + end + end + + describe "#coerce" do + it "coerces an existing member" do + assert_equal :foo, EnumTest::ExampleEnum.coerce(:foo) + end + + it "coerces a string version of a member" do + assert_equal :foo, EnumTest::ExampleEnum.coerce("foo") + end + + it "returns the value if not a member with strictness off" do + assert_equal 1, EnumTest::ExampleEnum.coerce(1) + end + + it "raises an error if value is not a member with strictness on" do + assert_raises Seed::Internal::Errors::TypeError do + EnumTest::ExampleEnum.coerce(1, strict: true) + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/test/unit/internal/types/test_hash.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/test/unit/internal/types/test_hash.rb new file mode 100644 index 000000000000..6c5e58a6a946 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/test/unit/internal/types/test_hash.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +require "test_helper" + +describe Seed::Internal::Types::Hash do + module TestHash + SymbolStringHash = Seed::Internal::Types::Hash[Symbol, String] + end + + describe ".[]" do + it "defines the key and value type" do + assert_equal Symbol, TestHash::SymbolStringHash.key_type + assert_equal String, TestHash::SymbolStringHash.value_type + end + end + + describe "#coerce" do + it "coerces the keys" do + assert_equal %i[foo bar], TestHash::SymbolStringHash.coerce({ "foo" => "1", :bar => "2" }).keys + end + + it "coerces the values" do + assert_equal %w[foo 1], TestHash::SymbolStringHash.coerce({ foo: :foo, bar: 1 }).values + end + + it "passes through other values with strictness off" do + obj = Object.new + + assert_equal obj, TestHash::SymbolStringHash.coerce(obj) + end + + it "raises an error with other values with strictness on" do + assert_raises Seed::Internal::Errors::TypeError do + TestHash::SymbolStringHash.coerce(Object.new, strict: true) + end + end + + it "raises an error with non-coercable key types with strictness on" do + assert_raises Seed::Internal::Errors::TypeError do + TestHash::SymbolStringHash.coerce({ Object.new => 1 }, strict: true) + end + end + + it "raises an error with non-coercable value types with strictness on" do + assert_raises Seed::Internal::Errors::TypeError do + TestHash::SymbolStringHash.coerce({ "foobar" => Object.new }, strict: true) + end + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/test/unit/internal/types/test_model.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/test/unit/internal/types/test_model.rb new file mode 100644 index 000000000000..3d87b9f5a8c7 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/test/unit/internal/types/test_model.rb @@ -0,0 +1,154 @@ +# frozen_string_literal: true + +require "test_helper" + +describe Seed::Internal::Types::Model do + module StringInteger + extend Seed::Internal::Types::Union + + member String + member Integer + end + + class ExampleModel < Seed::Internal::Types::Model + field :name, String + field :rating, StringInteger, optional: true + field :year, Integer, optional: true, nullable: true, api_name: "yearOfRelease" + end + + class ExampleModelInheritance < ExampleModel + field :director, String + end + + class ExampleWithDefaults < ExampleModel + field :type, String, default: "example" + end + + class ExampleChild < Seed::Internal::Types::Model + field :value, String + end + + class ExampleParent < Seed::Internal::Types::Model + field :child, ExampleChild + end + + describe ".field" do + before do + @example = ExampleModel.new(name: "Inception", rating: 4) + end + + it "defines fields on model" do + assert_equal %i[name rating year], ExampleModel.fields.keys + end + + it "defines fields from parent models" do + assert_equal %i[name rating year director], ExampleModelInheritance.fields.keys + end + + it "sets the field's type" do + assert_equal String, ExampleModel.fields[:name].type + assert_equal StringInteger, ExampleModel.fields[:rating].type + end + + it "sets the `default` option" do + assert_equal "example", ExampleWithDefaults.fields[:type].default + end + + it "defines getters" do + assert_respond_to @example, :name + assert_respond_to @example, :rating + + assert_equal "Inception", @example.name + assert_equal 4, @example.rating + end + + it "defines setters" do + assert_respond_to @example, :name= + assert_respond_to @example, :rating= + + @example.name = "Inception 2" + @example.rating = 5 + + assert_equal "Inception 2", @example.name + assert_equal 5, @example.rating + end + end + + describe "#initialize" do + it "sets the data" do + example = ExampleModel.new(name: "Inception", rating: 4) + + assert_equal "Inception", example.name + assert_equal 4, example.rating + end + + it "allows extra fields to be set" do + example = ExampleModel.new(name: "Inception", rating: 4, director: "Christopher Nolan") + + assert_equal "Christopher Nolan", example.director + end + + it "sets the defaults where applicable" do + example_using_defaults = ExampleWithDefaults.new + + assert_equal "example", example_using_defaults.type + + example_without_defaults = ExampleWithDefaults.new(type: "not example") + + assert_equal "not example", example_without_defaults.type + end + + it "coerces child models" do + parent = ExampleParent.new(child: { value: "foobar" }) + + assert_kind_of ExampleChild, parent.child + end + + it "uses the api_name to pull the value" do + example = ExampleModel.new({ name: "Inception", yearOfRelease: 2014 }) + + assert_equal 2014, example.year + refute_respond_to example, :yearOfRelease + end + end + + describe "#inspect" do + class SensitiveModel < Seed::Internal::Types::Model + field :username, String + field :password, String + field :client_secret, String + field :access_token, String + field :api_key, String + end + + it "redacts sensitive fields" do + model = SensitiveModel.new( + username: "user123", + password: "secret123", + client_secret: "cs_abc", + access_token: "token_xyz", + api_key: "key_123" + ) + + inspect_output = model.inspect + + assert_includes inspect_output, "username=\"user123\"" + assert_includes inspect_output, "password=[REDACTED]" + assert_includes inspect_output, "client_secret=[REDACTED]" + assert_includes inspect_output, "access_token=[REDACTED]" + assert_includes inspect_output, "api_key=[REDACTED]" + refute_includes inspect_output, "secret123" + refute_includes inspect_output, "cs_abc" + refute_includes inspect_output, "token_xyz" + refute_includes inspect_output, "key_123" + end + + it "does not redact non-sensitive fields" do + example = ExampleModel.new(name: "Inception", rating: 4) + inspect_output = example.inspect + + assert_includes inspect_output, "name=\"Inception\"" + assert_includes inspect_output, "rating=4" + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/test/unit/internal/types/test_union.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/test/unit/internal/types/test_union.rb new file mode 100644 index 000000000000..2b08e7f91d8f --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/test/unit/internal/types/test_union.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +require "test_helper" + +describe Seed::Internal::Types::Union do + class Rectangle < Seed::Internal::Types::Model + literal :type, "square" + + field :area, Float + end + + class Circle < Seed::Internal::Types::Model + literal :type, "circle" + + field :area, Float + end + + class Pineapple < Seed::Internal::Types::Model + literal :type, "pineapple" + + field :area, Float + end + + module Shape + extend Seed::Internal::Types::Union + + discriminant :type + + member -> { Rectangle }, key: "rect" + member -> { Circle }, key: "circle" + end + + module StringOrInteger + extend Seed::Internal::Types::Union + + member String + member Integer + end + + describe "#coerce" do + it "coerces hashes into member models with discriminated unions" do + circle = Shape.coerce({ type: "circle", area: 4.0 }) + + assert_instance_of Circle, circle + end + end + + describe "#member" do + it "defines Model members" do + assert Shape.member?(Rectangle) + assert Shape.member?(Circle) + refute Shape.member?(Pineapple) + end + + it "defines other members" do + assert StringOrInteger.member?(String) + assert StringOrInteger.member?(Integer) + refute StringOrInteger.member?(Float) + refute StringOrInteger.member?(Pineapple) + end + end +end diff --git a/seed/ruby-sdk-v2/inferred-auth-implicit-reference/test/unit/internal/types/test_utils.rb b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/test/unit/internal/types/test_utils.rb new file mode 100644 index 000000000000..29d14621a229 --- /dev/null +++ b/seed/ruby-sdk-v2/inferred-auth-implicit-reference/test/unit/internal/types/test_utils.rb @@ -0,0 +1,212 @@ +# frozen_string_literal: true + +require "test_helper" + +describe Seed::Internal::Types::Utils do + Utils = Seed::Internal::Types::Utils + + module TestUtils + class M < Seed::Internal::Types::Model + field :value, String + end + + class UnionMemberA < Seed::Internal::Types::Model + literal :type, "A" + field :only_on_a, String + end + + class UnionMemberB < Seed::Internal::Types::Model + literal :type, "B" + field :only_on_b, String + end + + module U + extend Seed::Internal::Types::Union + + discriminant :type + + member -> { UnionMemberA }, key: "A" + member -> { UnionMemberB }, key: "B" + end + + SymbolStringHash = Seed::Internal::Types::Hash[Symbol, String] + SymbolModelHash = -> { Seed::Internal::Types::Hash[Symbol, TestUtils::M] } + end + + describe ".coerce" do + describe "NilClass" do + it "always returns nil" do + assert_nil Utils.coerce(NilClass, "foobar") + assert_nil Utils.coerce(NilClass, 1) + assert_nil Utils.coerce(NilClass, Object.new) + end + end + + describe "String" do + it "coerces from String, Symbol, Numeric, or Boolean" do + assert_equal "foobar", Utils.coerce(String, "foobar") + assert_equal "foobar", Utils.coerce(String, :foobar) + assert_equal "1", Utils.coerce(String, 1) + assert_equal "1.0", Utils.coerce(String, 1.0) + assert_equal "true", Utils.coerce(String, true) + end + + it "passes through value if it cannot be coerced and not strict" do + obj = Object.new + + assert_equal obj, Utils.coerce(String, obj) + end + + it "raises an error if value cannot be coerced and strict" do + assert_raises Seed::Internal::Errors::TypeError do + Utils.coerce(String, Object.new, strict: true) + end + end + end + + describe "Symbol" do + it "coerces from Symbol, String" do + assert_equal :foobar, Utils.coerce(Symbol, :foobar) + assert_equal :foobar, Utils.coerce(Symbol, "foobar") + end + + it "passes through value if it cannot be coerced and not strict" do + obj = Object.new + + assert_equal obj, Utils.coerce(Symbol, obj) + end + + it "raises an error if value cannot be coerced and strict" do + assert_raises Seed::Internal::Errors::TypeError do + Utils.coerce(Symbol, Object.new, strict: true) + end + end + end + + describe "Integer" do + it "coerces from Numeric, String, Time" do + assert_equal 1, Utils.coerce(Integer, 1) + assert_equal 1, Utils.coerce(Integer, 1.0) + assert_equal 1, Utils.coerce(Integer, Complex.rect(1)) + assert_equal 1, Utils.coerce(Integer, Rational(1)) + assert_equal 1, Utils.coerce(Integer, "1") + assert_equal 1_713_916_800, Utils.coerce(Integer, Time.utc(2024, 4, 24)) + end + + it "passes through value if it cannot be coerced and not strict" do + obj = Object.new + + assert_equal obj, Utils.coerce(Integer, obj) + end + + it "raises an error if value cannot be coerced and strict" do + assert_raises Seed::Internal::Errors::TypeError do + Utils.coerce(Integer, Object.new, strict: true) + end + end + end + + describe "Float" do + it "coerces from Numeric, Time" do + assert_in_delta(1.0, Utils.coerce(Float, 1.0)) + assert_in_delta(1.0, Utils.coerce(Float, 1)) + assert_in_delta(1.0, Utils.coerce(Float, Complex.rect(1))) + assert_in_delta(1.0, Utils.coerce(Float, Rational(1))) + assert_in_delta(1_713_916_800.0, Utils.coerce(Integer, Time.utc(2024, 4, 24))) + end + + it "passes through value if it cannot be coerced and not strict" do + obj = Object.new + + assert_equal obj, Utils.coerce(Float, obj) + end + + it "raises an error if value cannot be coerced and strict" do + assert_raises Seed::Internal::Errors::TypeError do + Utils.coerce(Float, Object.new, strict: true) + end + end + end + + describe "Model" do + it "coerces a hash" do + result = Utils.coerce(TestUtils::M, { value: "foobar" }) + + assert_kind_of TestUtils::M, result + assert_equal "foobar", result.value + end + + it "coerces a hash when the target is a type function" do + result = Utils.coerce(-> { TestUtils::M }, { value: "foobar" }) + + assert_kind_of TestUtils::M, result + assert_equal "foobar", result.value + end + + it "will not coerce non-hashes" do + assert_equal "foobar", Utils.coerce(TestUtils::M, "foobar") + end + end + + describe "Enum" do + module ExampleEnum + extend Seed::Internal::Types::Enum + + FOO = :FOO + BAR = :BAR + + finalize! + end + + it "coerces into a Symbol version of the member value" do + assert_equal :FOO, Utils.coerce(ExampleEnum, "FOO") + end + + it "returns given value if not a member" do + assert_equal "NOPE", Utils.coerce(ExampleEnum, "NOPE") + end + end + + describe "Array" do + StringArray = Seed::Internal::Types::Array[String] + ModelArray = -> { Seed::Internal::Types::Array[TestUtils::M] } + UnionArray = -> { Seed::Internal::Types::Array[TestUtils::U] } + + it "coerces an array of literals" do + assert_equal %w[a b c], Utils.coerce(StringArray, %w[a b c]) + assert_equal ["1", "2.0", "true"], Utils.coerce(StringArray, [1, 2.0, true]) + assert_equal ["1", "2.0", "true"], Utils.coerce(StringArray, Set.new([1, 2.0, true])) + end + + it "coerces an array of Models" do + assert_equal [TestUtils::M.new(value: "foobar"), TestUtils::M.new(value: "bizbaz")], + Utils.coerce(ModelArray, [{ value: "foobar" }, { value: "bizbaz" }]) + + assert_equal [TestUtils::M.new(value: "foobar"), TestUtils::M.new(value: "bizbaz")], + Utils.coerce(ModelArray, [TestUtils::M.new(value: "foobar"), TestUtils::M.new(value: "bizbaz")]) + end + + it "coerces an array of model unions" do + assert_equal [TestUtils::UnionMemberA.new(type: "A", only_on_a: "A"), TestUtils::UnionMemberB.new(type: "B", only_on_b: "B")], + Utils.coerce(UnionArray, [{ type: "A", only_on_a: "A" }, { type: "B", only_on_b: "B" }]) + end + + it "returns given value if not an array" do + assert_equal 1, Utils.coerce(StringArray, 1) + end + end + + describe "Hash" do + it "coerces the keys and values" do + ssh_res = Utils.coerce(TestUtils::SymbolStringHash, { "foo" => "bar", "biz" => "2" }) + + assert_equal "bar", ssh_res[:foo] + assert_equal "2", ssh_res[:biz] + + smh_res = Utils.coerce(TestUtils::SymbolModelHash, { "foo" => { "value" => "foo" } }) + + assert_equal TestUtils::M.new(value: "foo"), smh_res[:foo] + end + end + end +end diff --git a/seed/rust-model/inferred-auth-implicit-api-key/.fern/metadata.json b/seed/rust-model/inferred-auth-implicit-api-key/.fern/metadata.json new file mode 100644 index 000000000000..30ce560b849b --- /dev/null +++ b/seed/rust-model/inferred-auth-implicit-api-key/.fern/metadata.json @@ -0,0 +1,5 @@ +{ + "cliVersion": "DUMMY", + "generatorName": "fernapi/fern-rust-model", + "generatorVersion": "latest" +} \ No newline at end of file diff --git a/seed/rust-model/inferred-auth-implicit-api-key/snippet.json b/seed/rust-model/inferred-auth-implicit-api-key/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/rust-model/inferred-auth-implicit-api-key/src/auth_token_response.rs b/seed/rust-model/inferred-auth-implicit-api-key/src/auth_token_response.rs new file mode 100644 index 000000000000..10b978735eb6 --- /dev/null +++ b/seed/rust-model/inferred-auth-implicit-api-key/src/auth_token_response.rs @@ -0,0 +1,11 @@ +pub use crate::prelude::*; + +/// An auth token response. +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub struct TokenResponse { + pub access_token: String, + pub token_type: String, + pub expires_in: i64, + #[serde(skip_serializing_if = "Option::is_none")] + pub scope: Option, +} \ No newline at end of file diff --git a/seed/rust-model/inferred-auth-implicit-api-key/src/lib.rs b/seed/rust-model/inferred-auth-implicit-api-key/src/lib.rs new file mode 100644 index 000000000000..466c825739cc --- /dev/null +++ b/seed/rust-model/inferred-auth-implicit-api-key/src/lib.rs @@ -0,0 +1,5 @@ +//! Generated models by Fern + +pub mod types; + +pub use types::*; diff --git a/seed/rust-model/inferred-auth-implicit-api-key/src/mod.rs b/seed/rust-model/inferred-auth-implicit-api-key/src/mod.rs new file mode 100644 index 000000000000..0fab45d78b7a --- /dev/null +++ b/seed/rust-model/inferred-auth-implicit-api-key/src/mod.rs @@ -0,0 +1,13 @@ +//! Request and response types for the InferredAuthImplicitApiKey +//! +//! This module contains all data structures used for API communication, +//! including request bodies, response types, and shared models. +//! +//! ## Type Categories +//! +//! - **Request/Response Types**: 1 types for API operations + +pub mod auth_token_response; + +pub use auth_token_response::TokenResponse; + diff --git a/seed/rust-model/inferred-auth-implicit-reference/.fern/metadata.json b/seed/rust-model/inferred-auth-implicit-reference/.fern/metadata.json new file mode 100644 index 000000000000..30ce560b849b --- /dev/null +++ b/seed/rust-model/inferred-auth-implicit-reference/.fern/metadata.json @@ -0,0 +1,5 @@ +{ + "cliVersion": "DUMMY", + "generatorName": "fernapi/fern-rust-model", + "generatorVersion": "latest" +} \ No newline at end of file diff --git a/seed/rust-model/inferred-auth-implicit-reference/snippet.json b/seed/rust-model/inferred-auth-implicit-reference/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/rust-model/inferred-auth-implicit-reference/src/auth_get_token_request.rs b/seed/rust-model/inferred-auth-implicit-reference/src/auth_get_token_request.rs new file mode 100644 index 000000000000..02b951cdd69a --- /dev/null +++ b/seed/rust-model/inferred-auth-implicit-reference/src/auth_get_token_request.rs @@ -0,0 +1,12 @@ +pub use crate::prelude::*; + +/// A request to obtain an OAuth token. +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub struct GetTokenRequest { + pub client_id: String, + pub client_secret: String, + pub audience: String, + pub grant_type: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub scope: Option, +} \ No newline at end of file diff --git a/seed/rust-model/inferred-auth-implicit-reference/src/auth_refresh_token_request.rs b/seed/rust-model/inferred-auth-implicit-reference/src/auth_refresh_token_request.rs new file mode 100644 index 000000000000..31de2f6a35e8 --- /dev/null +++ b/seed/rust-model/inferred-auth-implicit-reference/src/auth_refresh_token_request.rs @@ -0,0 +1,13 @@ +pub use crate::prelude::*; + +/// A request to refresh an OAuth token. +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub struct RefreshTokenRequest { + pub client_id: String, + pub client_secret: String, + pub refresh_token: String, + pub audience: String, + pub grant_type: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub scope: Option, +} \ No newline at end of file diff --git a/seed/rust-model/inferred-auth-implicit-reference/src/auth_token_response.rs b/seed/rust-model/inferred-auth-implicit-reference/src/auth_token_response.rs new file mode 100644 index 000000000000..82edfb75873d --- /dev/null +++ b/seed/rust-model/inferred-auth-implicit-reference/src/auth_token_response.rs @@ -0,0 +1,10 @@ +pub use crate::prelude::*; + +/// An OAuth token response. +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub struct TokenResponse { + pub access_token: String, + pub expires_in: i64, + #[serde(skip_serializing_if = "Option::is_none")] + pub refresh_token: Option, +} \ No newline at end of file diff --git a/seed/rust-model/inferred-auth-implicit-reference/src/lib.rs b/seed/rust-model/inferred-auth-implicit-reference/src/lib.rs new file mode 100644 index 000000000000..466c825739cc --- /dev/null +++ b/seed/rust-model/inferred-auth-implicit-reference/src/lib.rs @@ -0,0 +1,5 @@ +//! Generated models by Fern + +pub mod types; + +pub use types::*; diff --git a/seed/rust-model/inferred-auth-implicit-reference/src/mod.rs b/seed/rust-model/inferred-auth-implicit-reference/src/mod.rs new file mode 100644 index 000000000000..5c7aa398fdaf --- /dev/null +++ b/seed/rust-model/inferred-auth-implicit-reference/src/mod.rs @@ -0,0 +1,17 @@ +//! Request and response types for the InferredAuthImplicit +//! +//! This module contains all data structures used for API communication, +//! including request bodies, response types, and shared models. +//! +//! ## Type Categories +//! +//! - **Request/Response Types**: 3 types for API operations + +pub mod auth_get_token_request; +pub mod auth_refresh_token_request; +pub mod auth_token_response; + +pub use auth_get_token_request::GetTokenRequest; +pub use auth_refresh_token_request::RefreshTokenRequest; +pub use auth_token_response::TokenResponse; + diff --git a/seed/rust-model/nullable-allof-extends/.fern/metadata.json b/seed/rust-model/nullable-allof-extends/.fern/metadata.json new file mode 100644 index 000000000000..30ce560b849b --- /dev/null +++ b/seed/rust-model/nullable-allof-extends/.fern/metadata.json @@ -0,0 +1,5 @@ +{ + "cliVersion": "DUMMY", + "generatorName": "fernapi/fern-rust-model", + "generatorVersion": "latest" +} \ No newline at end of file diff --git a/seed/rust-model/nullable-allof-extends/snippet.json b/seed/rust-model/nullable-allof-extends/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/rust-model/nullable-allof-extends/src/lib.rs b/seed/rust-model/nullable-allof-extends/src/lib.rs new file mode 100644 index 000000000000..466c825739cc --- /dev/null +++ b/seed/rust-model/nullable-allof-extends/src/lib.rs @@ -0,0 +1,5 @@ +//! Generated models by Fern + +pub mod types; + +pub use types::*; diff --git a/seed/rust-model/nullable-allof-extends/src/mod.rs b/seed/rust-model/nullable-allof-extends/src/mod.rs new file mode 100644 index 000000000000..8abcbc1ad69f --- /dev/null +++ b/seed/rust-model/nullable-allof-extends/src/mod.rs @@ -0,0 +1,17 @@ +//! Request and response types for the Nullable AllOf Extends Test +//! +//! This module contains all data structures used for API communication, +//! including request bodies, response types, and shared models. +//! +//! ## Type Categories +//! +//! - **Model Types**: 3 types for data representation + +pub mod root_object; +pub mod normal_object; +pub mod nullable_object; + +pub use root_object::RootObject; +pub use normal_object::NormalObject; +pub use nullable_object::NullableObject; + diff --git a/seed/rust-model/nullable-allof-extends/src/normal_object.rs b/seed/rust-model/nullable-allof-extends/src/normal_object.rs new file mode 100644 index 000000000000..585209905bf7 --- /dev/null +++ b/seed/rust-model/nullable-allof-extends/src/normal_object.rs @@ -0,0 +1,9 @@ +pub use crate::prelude::*; + +/// A standard object with no nullable issues. +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub struct NormalObject { + #[serde(rename = "normalField")] + #[serde(skip_serializing_if = "Option::is_none")] + pub normal_field: Option, +} \ No newline at end of file diff --git a/seed/rust-model/nullable-allof-extends/src/nullable_object.rs b/seed/rust-model/nullable-allof-extends/src/nullable_object.rs new file mode 100644 index 000000000000..ad089b18864d --- /dev/null +++ b/seed/rust-model/nullable-allof-extends/src/nullable_object.rs @@ -0,0 +1,9 @@ +pub use crate::prelude::*; + +/// This schema has nullable:true at the top level. +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub struct NullableObject { + #[serde(rename = "nullableField")] + #[serde(skip_serializing_if = "Option::is_none")] + pub nullable_field: Option, +} \ No newline at end of file diff --git a/seed/rust-model/nullable-allof-extends/src/root_object.rs b/seed/rust-model/nullable-allof-extends/src/root_object.rs new file mode 100644 index 000000000000..40398bc808f5 --- /dev/null +++ b/seed/rust-model/nullable-allof-extends/src/root_object.rs @@ -0,0 +1,10 @@ +pub use crate::prelude::*; + +/// Object inheriting from a nullable schema via allOf. +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub struct RootObject { + #[serde(flatten)] + pub normal_object_fields: NormalObject, + #[serde(flatten)] + pub nullable_object_fields: NullableObject, +} \ No newline at end of file diff --git a/seed/rust-model/oauth-client-credentials-mandatory-auth/.fern/metadata.json b/seed/rust-model/oauth-client-credentials-mandatory-auth/.fern/metadata.json new file mode 100644 index 000000000000..30ce560b849b --- /dev/null +++ b/seed/rust-model/oauth-client-credentials-mandatory-auth/.fern/metadata.json @@ -0,0 +1,5 @@ +{ + "cliVersion": "DUMMY", + "generatorName": "fernapi/fern-rust-model", + "generatorVersion": "latest" +} \ No newline at end of file diff --git a/seed/rust-model/oauth-client-credentials-mandatory-auth/snippet.json b/seed/rust-model/oauth-client-credentials-mandatory-auth/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/rust-model/oauth-client-credentials-mandatory-auth/src/auth_token_response.rs b/seed/rust-model/oauth-client-credentials-mandatory-auth/src/auth_token_response.rs new file mode 100644 index 000000000000..82edfb75873d --- /dev/null +++ b/seed/rust-model/oauth-client-credentials-mandatory-auth/src/auth_token_response.rs @@ -0,0 +1,10 @@ +pub use crate::prelude::*; + +/// An OAuth token response. +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub struct TokenResponse { + pub access_token: String, + pub expires_in: i64, + #[serde(skip_serializing_if = "Option::is_none")] + pub refresh_token: Option, +} \ No newline at end of file diff --git a/seed/rust-model/oauth-client-credentials-mandatory-auth/src/get_token_request.rs b/seed/rust-model/oauth-client-credentials-mandatory-auth/src/get_token_request.rs new file mode 100644 index 000000000000..e314e4edbf5f --- /dev/null +++ b/seed/rust-model/oauth-client-credentials-mandatory-auth/src/get_token_request.rs @@ -0,0 +1,12 @@ +pub use crate::prelude::*; + +/// Request type for API operation +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub struct GetTokenRequest { + pub client_id: String, + pub client_secret: String, + pub audience: String, + pub grant_type: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub scope: Option, +} diff --git a/seed/rust-model/oauth-client-credentials-mandatory-auth/src/lib.rs b/seed/rust-model/oauth-client-credentials-mandatory-auth/src/lib.rs new file mode 100644 index 000000000000..466c825739cc --- /dev/null +++ b/seed/rust-model/oauth-client-credentials-mandatory-auth/src/lib.rs @@ -0,0 +1,5 @@ +//! Generated models by Fern + +pub mod types; + +pub use types::*; diff --git a/seed/rust-model/oauth-client-credentials-mandatory-auth/src/mod.rs b/seed/rust-model/oauth-client-credentials-mandatory-auth/src/mod.rs new file mode 100644 index 000000000000..e8fe84ac3d47 --- /dev/null +++ b/seed/rust-model/oauth-client-credentials-mandatory-auth/src/mod.rs @@ -0,0 +1,17 @@ +//! Request and response types for the OauthClientCredentialsMandatoryAuth +//! +//! This module contains all data structures used for API communication, +//! including request bodies, response types, and shared models. +//! +//! ## Type Categories +//! +//! - **Request/Response Types**: 3 types for API operations + +pub mod auth_token_response; +pub mod get_token_request; +pub mod refresh_token_request; + +pub use auth_token_response::TokenResponse; +pub use get_token_request::GetTokenRequest; +pub use refresh_token_request::RefreshTokenRequest; + diff --git a/seed/rust-model/oauth-client-credentials-mandatory-auth/src/refresh_token_request.rs b/seed/rust-model/oauth-client-credentials-mandatory-auth/src/refresh_token_request.rs new file mode 100644 index 000000000000..98fa73284fb7 --- /dev/null +++ b/seed/rust-model/oauth-client-credentials-mandatory-auth/src/refresh_token_request.rs @@ -0,0 +1,13 @@ +pub use crate::prelude::*; + +/// Request type for API operation +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub struct RefreshTokenRequest { + pub client_id: String, + pub client_secret: String, + pub refresh_token: String, + pub audience: String, + pub grant_type: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub scope: Option, +} diff --git a/seed/rust-model/pagination/src/list_with_optional_data_query_request.rs b/seed/rust-model/pagination/src/list_with_optional_data_query_request.rs new file mode 100644 index 000000000000..5752cf7119b9 --- /dev/null +++ b/seed/rust-model/pagination/src/list_with_optional_data_query_request.rs @@ -0,0 +1,11 @@ +pub use crate::prelude::*; + +/// Query parameters for listWithOptionalData +/// +/// Request type for the ListWithOptionalDataQueryRequest operation. +#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq, Hash)] +pub struct ListWithOptionalDataQueryRequest { + /// Defaults to first page + #[serde(skip_serializing_if = "Option::is_none")] + pub page: Option, +} diff --git a/seed/rust-model/pagination/src/mod.rs b/seed/rust-model/pagination/src/mod.rs index 7902423f9dfa..1a2f6a45d3f8 100644 --- a/seed/rust-model/pagination/src/mod.rs +++ b/seed/rust-model/pagination/src/mod.rs @@ -5,7 +5,7 @@ //! //! ## Type Categories //! -//! - **Request/Response Types**: 41 types for API operations +//! - **Request/Response Types**: 43 types for API operations //! - **Model Types**: 28 types for data representation pub mod username_cursor; @@ -48,6 +48,7 @@ pub mod users_username_container; pub mod users_list_users_extended_response; pub mod users_list_users_extended_optional_list_response; pub mod users_list_users_pagination_response; +pub mod users_list_users_optional_data_pagination_response; pub mod users_list_users_mixed_type_pagination_response; pub mod users_page; pub mod users_next_page; @@ -77,6 +78,7 @@ pub mod users_list_with_extended_results_and_optional_data_query_request; pub mod users_list_usernames_query_request; pub mod list_usernames_with_optional_response_query_request; pub mod users_list_with_global_config_query_request; +pub mod list_with_optional_data_query_request; pub use username_cursor::UsernameCursor; pub use username_page::UsernamePage; @@ -118,6 +120,7 @@ pub use users_username_container::UsernameContainer2; pub use users_list_users_extended_response::ListUsersExtendedResponse2; pub use users_list_users_extended_optional_list_response::ListUsersExtendedOptionalListResponse2; pub use users_list_users_pagination_response::ListUsersPaginationResponse2; +pub use users_list_users_optional_data_pagination_response::ListUsersOptionalDataPaginationResponse; pub use users_list_users_mixed_type_pagination_response::ListUsersMixedTypePaginationResponse2; pub use users_page::Page2; pub use users_next_page::NextPage2; @@ -147,4 +150,5 @@ pub use users_list_with_extended_results_and_optional_data_query_request::UsersL pub use users_list_usernames_query_request::UsersListUsernamesQueryRequest; pub use list_usernames_with_optional_response_query_request::ListUsernamesWithOptionalResponseQueryRequest; pub use users_list_with_global_config_query_request::UsersListWithGlobalConfigQueryRequest; +pub use list_with_optional_data_query_request::ListWithOptionalDataQueryRequest; diff --git a/seed/rust-model/pagination/src/users_list_users_optional_data_pagination_response.rs b/seed/rust-model/pagination/src/users_list_users_optional_data_pagination_response.rs new file mode 100644 index 000000000000..0c095e77059e --- /dev/null +++ b/seed/rust-model/pagination/src/users_list_users_optional_data_pagination_response.rs @@ -0,0 +1,14 @@ +pub use crate::prelude::*; + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub struct ListUsersOptionalDataPaginationResponse { + #[serde(rename = "hasNextPage")] + #[serde(skip_serializing_if = "Option::is_none")] + pub has_next_page: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub page: Option, + /// The totall number of /users + pub total_count: i64, + #[serde(skip_serializing_if = "Option::is_none")] + pub data: Option>, +} \ No newline at end of file diff --git a/seed/rust-sdk/inferred-auth-implicit-api-key/.fern/metadata.json b/seed/rust-sdk/inferred-auth-implicit-api-key/.fern/metadata.json new file mode 100644 index 000000000000..ce1c0cd76077 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-api-key/.fern/metadata.json @@ -0,0 +1,5 @@ +{ + "cliVersion": "DUMMY", + "generatorName": "fernapi/fern-rust-sdk", + "generatorVersion": "latest" +} \ No newline at end of file diff --git a/seed/rust-sdk/inferred-auth-implicit-api-key/.github/workflows/ci.yml b/seed/rust-sdk/inferred-auth-implicit-api-key/.github/workflows/ci.yml new file mode 100644 index 000000000000..1a311af94152 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-api-key/.github/workflows/ci.yml @@ -0,0 +1,59 @@ +name: ci + +on: [push] + +env: + RUSTFLAGS: "-A warnings" + +jobs: + check: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Rust + uses: actions-rust-lang/setup-rust-toolchain@v1 + + - name: Check + run: cargo check + + compile: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Rust + uses: actions-rust-lang/setup-rust-toolchain@v1 + + - name: Compile + run: cargo build + + test: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Rust + uses: actions-rust-lang/setup-rust-toolchain@v1 + + - name: Test + run: cargo test + + publish: + needs: [check, compile, test] + if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Rust + uses: actions-rust-lang/setup-rust-toolchain@v1 + + - name: Publish + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + run: cargo publish \ No newline at end of file diff --git a/seed/rust-sdk/inferred-auth-implicit-api-key/.gitignore b/seed/rust-sdk/inferred-auth-implicit-api-key/.gitignore new file mode 100644 index 000000000000..f75d27799da1 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-api-key/.gitignore @@ -0,0 +1,5 @@ +/target +**/*.rs.bk +Cargo.lock +.DS_Store +*.swp \ No newline at end of file diff --git a/seed/rust-sdk/inferred-auth-implicit-api-key/Cargo.toml b/seed/rust-sdk/inferred-auth-implicit-api-key/Cargo.toml new file mode 100644 index 000000000000..1e28fc0ab115 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-api-key/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "seed_inferred_auth_implicit_api_key" +version = "0.0.1" +edition = "2021" +description = "Rust SDK for seed_inferred_auth_implicit_api_key generated by Fern" +license = "MIT" +repository = "https://github.com/fern-api/fern" +documentation = "https://docs.rs/seed_inferred_auth_implicit_api_key" + +[lib] +doctest = false + +[dependencies] +bytes = "1.0" +chrono = { version = "0.4", features = ["serde"] } +futures = "0.3" +num-bigint = { version = "0.4", features = ["serde"] } +ordered-float = { version = "4.5", features = ["serde"] } +percent-encoding = "2.3" +reqwest = { version = "0.12", features = ["json", "stream"], default-features = false } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +thiserror = "1.0" +tokio = { version = "1.0", features = ["full"] } +uuid = { version = "1.0", features = ["serde"] } + +[dev-dependencies] +tokio-test = "0.4" + +[features] +multipart = [] +sse = [] diff --git a/seed/rust-sdk/inferred-auth-implicit-api-key/README.md b/seed/rust-sdk/inferred-auth-implicit-api-key/README.md new file mode 100644 index 000000000000..06249b7d6107 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-api-key/README.md @@ -0,0 +1,150 @@ +# Seed Rust Library + +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FRust) +[![crates.io shield](https://img.shields.io/crates/v/seed_inferred_auth_implicit_api_key)](https://crates.io/crates/seed_inferred_auth_implicit_api_key) + +The Seed Rust library provides convenient access to the Seed APIs from Rust. + +## Table of Contents + +- [Installation](#installation) +- [Reference](#reference) +- [Usage](#usage) +- [Errors](#errors) +- [Advanced](#advanced) + - [Retries](#retries) + - [Timeouts](#timeouts) + - [Additional Headers](#additional-headers) + - [Additional Query String Parameters](#additional-query-string-parameters) +- [Contributing](#contributing) + +## Installation + +Add this to your `Cargo.toml`: + +```toml +[dependencies] +seed_inferred_auth_implicit_api_key = "0.0.1" +``` + +Or install via cargo: + +```sh +cargo add seed_inferred_auth_implicit_api_key +``` + +## Reference + +A full reference for this library is available [here](./reference.md). + +## Usage + +Instantiate and use the client with the following: + +```rust +use seed_inferred_auth_implicit_api_key::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + ..Default::default() + }; + let client = InferredAuthImplicitApiKeyClient::new(config).expect("Failed to build client"); + client + .auth + .get_token(Some( + RequestOptions::new().additional_header("X-Api-Key", "api_key".to_string()), + )) + .await; +} +``` + +## Errors + +When the API returns a non-success status code (4xx or 5xx response), an error will be returned. + +```rust +match client.auth.get_token(None)?.await { + Ok(response) => { + println!("Success: {:?}", response); + }, + Err(ApiError::HTTP { status, message }) => { + println!("API Error {}: {:?}", status, message); + }, + Err(e) => { + println!("Other error: {:?}", e); + } +} +``` + +## Advanced + +### Retries + +The SDK is instrumented with automatic retries with exponential backoff. A request will be retried as long +as the request is deemed retryable and the number of retry attempts has not grown larger than the configured +retry limit (default: 2). + +A request is deemed retryable when any of the following HTTP status codes is returned: + +- [408](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/408) (Timeout) +- [429](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429) (Too Many Requests) +- [5XX](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500) (Internal Server Errors) + +Use the `max_retries` method to configure this behavior. + +```rust +let response = client.auth.get_token( + Some(RequestOptions::new().max_retries(3)) +)?.await; +``` + +### Timeouts + +The SDK defaults to a 30 second timeout. Use the `timeout` method to configure this behavior. + +```rust +let response = client.auth.get_token( + Some(RequestOptions::new().timeout_seconds(30)) +)?.await; +``` + +### Additional Headers + +You can add custom headers to requests using `RequestOptions`. + +```rust +let response = client.auth.get_token( + Some( + RequestOptions::new() + .additional_header("X-Custom-Header", "custom-value") + .additional_header("X-Another-Header", "another-value") + ) +)? +.await; +``` + +### Additional Query String Parameters + +You can add custom query parameters to requests using `RequestOptions`. + +```rust +let response = client.auth.get_token( + Some( + RequestOptions::new() + .additional_query_param("filter", "active") + .additional_query_param("sort", "desc") + ) +)? +.await; +``` + +## Contributing + +While we value open-source contributions to this SDK, this library is generated programmatically. +Additions made directly to this library would have to be moved over to our generation code, +otherwise they would be overwritten upon the next generated release. Feel free to open a PR as +a proof of concept, but know that we will not be able to merge it as-is. We suggest opening +an issue first to discuss with us! + +On the other hand, contributions to the README are always very welcome! \ No newline at end of file diff --git a/seed/rust-sdk/inferred-auth-implicit-api-key/dynamic-snippets/example0.rs b/seed/rust-sdk/inferred-auth-implicit-api-key/dynamic-snippets/example0.rs new file mode 100644 index 000000000000..e6e48a2e4f8e --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-api-key/dynamic-snippets/example0.rs @@ -0,0 +1,16 @@ +use seed_inferred_auth_implicit_api_key::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + base_url: "https://api.fern.com".to_string(), + ..Default::default() + }; + let client = InferredAuthImplicitApiKeyClient::new(config).expect("Failed to build client"); + client + .auth + .get_token(Some( + RequestOptions::new().additional_header("X-Api-Key", "api_key".to_string()), + )) + .await; +} diff --git a/seed/rust-sdk/inferred-auth-implicit-api-key/dynamic-snippets/example1.rs b/seed/rust-sdk/inferred-auth-implicit-api-key/dynamic-snippets/example1.rs new file mode 100644 index 000000000000..6187038540e3 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-api-key/dynamic-snippets/example1.rs @@ -0,0 +1,11 @@ +use seed_inferred_auth_implicit_api_key::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + base_url: "https://api.fern.com".to_string(), + ..Default::default() + }; + let client = InferredAuthImplicitApiKeyClient::new(config).expect("Failed to build client"); + client.nested_no_auth.api.get_something(None).await; +} diff --git a/seed/rust-sdk/inferred-auth-implicit-api-key/dynamic-snippets/example2.rs b/seed/rust-sdk/inferred-auth-implicit-api-key/dynamic-snippets/example2.rs new file mode 100644 index 000000000000..421e77f98ae4 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-api-key/dynamic-snippets/example2.rs @@ -0,0 +1,11 @@ +use seed_inferred_auth_implicit_api_key::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + base_url: "https://api.fern.com".to_string(), + ..Default::default() + }; + let client = InferredAuthImplicitApiKeyClient::new(config).expect("Failed to build client"); + client.nested.api.get_something(None).await; +} diff --git a/seed/rust-sdk/inferred-auth-implicit-api-key/dynamic-snippets/example3.rs b/seed/rust-sdk/inferred-auth-implicit-api-key/dynamic-snippets/example3.rs new file mode 100644 index 000000000000..55764757cd59 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-api-key/dynamic-snippets/example3.rs @@ -0,0 +1,11 @@ +use seed_inferred_auth_implicit_api_key::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + base_url: "https://api.fern.com".to_string(), + ..Default::default() + }; + let client = InferredAuthImplicitApiKeyClient::new(config).expect("Failed to build client"); + client.simple.get_something(None).await; +} diff --git a/seed/rust-sdk/inferred-auth-implicit-api-key/reference.md b/seed/rust-sdk/inferred-auth-implicit-api-key/reference.md new file mode 100644 index 000000000000..62896e4ccf6d --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-api-key/reference.md @@ -0,0 +1,145 @@ +# Reference +## Auth +
client.auth.get_token() -> Result +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```rust +use seed_inferred_auth_implicit_api_key::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + ..Default::default() + }; + let client = InferredAuthImplicitApiKeyClient::new(config).expect("Failed to build client"); + client + .auth + .get_token(Some( + RequestOptions::new().additional_header("X-Api-Key", "api_key".to_string()), + )) + .await; +} +``` +
+
+
+
+ + +
+
+
+ +## NestedNoAuth Api +
client.nested_no_auth().api.get_something() -> Result<(), ApiError> +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```rust +use seed_inferred_auth_implicit_api_key::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + ..Default::default() + }; + let client = InferredAuthImplicitApiKeyClient::new(config).expect("Failed to build client"); + client.nested_no_auth.api.get_something(None).await; +} +``` +
+
+
+
+ + +
+
+
+ +## Nested Api +
client.nested().api.get_something() -> Result<(), ApiError> +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```rust +use seed_inferred_auth_implicit_api_key::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + ..Default::default() + }; + let client = InferredAuthImplicitApiKeyClient::new(config).expect("Failed to build client"); + client.nested.api.get_something(None).await; +} +``` +
+
+
+
+ + +
+
+
+ +## Simple +
client.simple.get_something() -> Result<(), ApiError> +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```rust +use seed_inferred_auth_implicit_api_key::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + ..Default::default() + }; + let client = InferredAuthImplicitApiKeyClient::new(config).expect("Failed to build client"); + client.simple.get_something(None).await; +} +``` +
+
+
+
+ + +
+
+
diff --git a/seed/rust-sdk/inferred-auth-implicit-api-key/rustfmt.toml b/seed/rust-sdk/inferred-auth-implicit-api-key/rustfmt.toml new file mode 100644 index 000000000000..872221fb31fe --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-api-key/rustfmt.toml @@ -0,0 +1,4 @@ +# Generated by Fern +edition = "2021" +max_width = 100 +use_small_heuristics = "Default" \ No newline at end of file diff --git a/seed/rust-sdk/inferred-auth-implicit-api-key/snippet.json b/seed/rust-sdk/inferred-auth-implicit-api-key/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/rust-sdk/inferred-auth-implicit-api-key/src/api/mod.rs b/seed/rust-sdk/inferred-auth-implicit-api-key/src/api/mod.rs new file mode 100644 index 000000000000..58f681f69244 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-api-key/src/api/mod.rs @@ -0,0 +1,17 @@ +//! API client and types for the InferredAuthImplicitApiKey +//! +//! This module contains all the API definitions including request/response types +//! and client implementations for interacting with the API. +//! +//! ## Modules +//! +//! - [`resources`] - Service clients and endpoints +//! - [`types`] - Request, response, and model types + +pub mod resources; +pub mod types; + +pub use resources::{ + AuthClient, InferredAuthImplicitApiKeyClient, NestedClient, NestedNoAuthClient, SimpleClient, +}; +pub use types::*; diff --git a/seed/rust-sdk/inferred-auth-implicit-api-key/src/api/resources/auth/auth.rs b/seed/rust-sdk/inferred-auth-implicit-api-key/src/api/resources/auth/auth.rs new file mode 100644 index 000000000000..3b359f87eb95 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-api-key/src/api/resources/auth/auth.rs @@ -0,0 +1,24 @@ +use crate::api::*; +use crate::{ApiError, ClientConfig, HttpClient, RequestOptions}; +use reqwest::Method; + +pub struct AuthClient { + pub http_client: HttpClient, +} + +impl AuthClient { + pub fn new(config: ClientConfig) -> Result { + Ok(Self { + http_client: HttpClient::new(config.clone())?, + }) + } + + pub async fn get_token( + &self, + options: Option, + ) -> Result { + self.http_client + .execute_request(Method::POST, "/token", None, None, options) + .await + } +} diff --git a/seed/rust-sdk/inferred-auth-implicit-api-key/src/api/resources/auth/mod.rs b/seed/rust-sdk/inferred-auth-implicit-api-key/src/api/resources/auth/mod.rs new file mode 100644 index 000000000000..c5d7cba0e123 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-api-key/src/api/resources/auth/mod.rs @@ -0,0 +1,2 @@ +pub mod auth; +pub use auth::AuthClient; diff --git a/seed/rust-sdk/inferred-auth-implicit-api-key/src/api/resources/mod.rs b/seed/rust-sdk/inferred-auth-implicit-api-key/src/api/resources/mod.rs new file mode 100644 index 000000000000..23c6ab4beab5 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-api-key/src/api/resources/mod.rs @@ -0,0 +1,39 @@ +//! Service clients and API endpoints +//! +//! This module contains client implementations for: +//! +//! - **Auth** +//! - **NestedNoAuth** +//! - **Nested** +//! - **Simple** + +use crate::{ApiError, ClientConfig}; + +pub mod auth; +pub mod nested; +pub mod nested_no_auth; +pub mod simple; +pub struct InferredAuthImplicitApiKeyClient { + pub config: ClientConfig, + pub auth: AuthClient, + pub nested_no_auth: NestedNoAuthClient, + pub nested: NestedClient, + pub simple: SimpleClient, +} + +impl InferredAuthImplicitApiKeyClient { + pub fn new(config: ClientConfig) -> Result { + Ok(Self { + config: config.clone(), + auth: AuthClient::new(config.clone())?, + nested_no_auth: NestedNoAuthClient::new(config.clone())?, + nested: NestedClient::new(config.clone())?, + simple: SimpleClient::new(config.clone())?, + }) + } +} + +pub use auth::AuthClient; +pub use nested::NestedClient; +pub use nested_no_auth::NestedNoAuthClient; +pub use simple::SimpleClient; diff --git a/seed/rust-sdk/inferred-auth-implicit-api-key/src/api/resources/nested/api/mod.rs b/seed/rust-sdk/inferred-auth-implicit-api-key/src/api/resources/nested/api/mod.rs new file mode 100644 index 000000000000..60b6b867df7b --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-api-key/src/api/resources/nested/api/mod.rs @@ -0,0 +1,2 @@ +pub mod nested_api; +pub use nested_api::ApiClient2; diff --git a/seed/rust-sdk/inferred-auth-implicit-api-key/src/api/resources/nested/api/nested_api.rs b/seed/rust-sdk/inferred-auth-implicit-api-key/src/api/resources/nested/api/nested_api.rs new file mode 100644 index 000000000000..5a59312331ac --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-api-key/src/api/resources/nested/api/nested_api.rs @@ -0,0 +1,20 @@ +use crate::{ApiError, ClientConfig, HttpClient, RequestOptions}; +use reqwest::Method; + +pub struct ApiClient2 { + pub http_client: HttpClient, +} + +impl ApiClient2 { + pub fn new(config: ClientConfig) -> Result { + Ok(Self { + http_client: HttpClient::new(config.clone())?, + }) + } + + pub async fn get_something(&self, options: Option) -> Result<(), ApiError> { + self.http_client + .execute_request(Method::GET, "/nested/get-something", None, None, options) + .await + } +} diff --git a/seed/rust-sdk/inferred-auth-implicit-api-key/src/api/resources/nested/mod.rs b/seed/rust-sdk/inferred-auth-implicit-api-key/src/api/resources/nested/mod.rs new file mode 100644 index 000000000000..0ae5397c7f44 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-api-key/src/api/resources/nested/mod.rs @@ -0,0 +1,16 @@ +use crate::{ApiError, ClientConfig, HttpClient}; + +pub mod api; +pub use api::ApiClient2; +pub struct NestedClient { + pub http_client: HttpClient, + pub api: ApiClient2, +} +impl NestedClient { + pub fn new(config: ClientConfig) -> Result { + Ok(Self { + http_client: HttpClient::new(config.clone())?, + api: ApiClient2::new(config.clone())?, + }) + } +} diff --git a/seed/rust-sdk/inferred-auth-implicit-api-key/src/api/resources/nested_no_auth/api/mod.rs b/seed/rust-sdk/inferred-auth-implicit-api-key/src/api/resources/nested_no_auth/api/mod.rs new file mode 100644 index 000000000000..ac9a34097bf9 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-api-key/src/api/resources/nested_no_auth/api/mod.rs @@ -0,0 +1,2 @@ +pub mod nested_no_auth_api; +pub use nested_no_auth_api::ApiClient; diff --git a/seed/rust-sdk/inferred-auth-implicit-api-key/src/api/resources/nested_no_auth/api/nested_no_auth_api.rs b/seed/rust-sdk/inferred-auth-implicit-api-key/src/api/resources/nested_no_auth/api/nested_no_auth_api.rs new file mode 100644 index 000000000000..f9ac674562d7 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-api-key/src/api/resources/nested_no_auth/api/nested_no_auth_api.rs @@ -0,0 +1,26 @@ +use crate::{ApiError, ClientConfig, HttpClient, RequestOptions}; +use reqwest::Method; + +pub struct ApiClient { + pub http_client: HttpClient, +} + +impl ApiClient { + pub fn new(config: ClientConfig) -> Result { + Ok(Self { + http_client: HttpClient::new(config.clone())?, + }) + } + + pub async fn get_something(&self, options: Option) -> Result<(), ApiError> { + self.http_client + .execute_request( + Method::GET, + "/nested-no-auth/get-something", + None, + None, + options, + ) + .await + } +} diff --git a/seed/rust-sdk/inferred-auth-implicit-api-key/src/api/resources/nested_no_auth/mod.rs b/seed/rust-sdk/inferred-auth-implicit-api-key/src/api/resources/nested_no_auth/mod.rs new file mode 100644 index 000000000000..73f91ddc946a --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-api-key/src/api/resources/nested_no_auth/mod.rs @@ -0,0 +1,16 @@ +use crate::{ApiError, ClientConfig, HttpClient}; + +pub mod api; +pub use api::ApiClient; +pub struct NestedNoAuthClient { + pub http_client: HttpClient, + pub api: ApiClient, +} +impl NestedNoAuthClient { + pub fn new(config: ClientConfig) -> Result { + Ok(Self { + http_client: HttpClient::new(config.clone())?, + api: ApiClient::new(config.clone())?, + }) + } +} diff --git a/seed/rust-sdk/inferred-auth-implicit-api-key/src/api/resources/simple/mod.rs b/seed/rust-sdk/inferred-auth-implicit-api-key/src/api/resources/simple/mod.rs new file mode 100644 index 000000000000..26f8c47f517d --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-api-key/src/api/resources/simple/mod.rs @@ -0,0 +1,2 @@ +pub mod simple; +pub use simple::SimpleClient; diff --git a/seed/rust-sdk/inferred-auth-implicit-api-key/src/api/resources/simple/simple.rs b/seed/rust-sdk/inferred-auth-implicit-api-key/src/api/resources/simple/simple.rs new file mode 100644 index 000000000000..5ec45ae2b26c --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-api-key/src/api/resources/simple/simple.rs @@ -0,0 +1,20 @@ +use crate::{ApiError, ClientConfig, HttpClient, RequestOptions}; +use reqwest::Method; + +pub struct SimpleClient { + pub http_client: HttpClient, +} + +impl SimpleClient { + pub fn new(config: ClientConfig) -> Result { + Ok(Self { + http_client: HttpClient::new(config.clone())?, + }) + } + + pub async fn get_something(&self, options: Option) -> Result<(), ApiError> { + self.http_client + .execute_request(Method::GET, "/get-something", None, None, options) + .await + } +} diff --git a/seed/rust-sdk/inferred-auth-implicit-api-key/src/api/types/auth_token_response.rs b/seed/rust-sdk/inferred-auth-implicit-api-key/src/api/types/auth_token_response.rs new file mode 100644 index 000000000000..3b2acb30c3dc --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-api-key/src/api/types/auth_token_response.rs @@ -0,0 +1,11 @@ +pub use crate::prelude::*; + +/// An auth token response. +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub struct TokenResponse { + pub access_token: String, + pub token_type: String, + pub expires_in: i64, + #[serde(skip_serializing_if = "Option::is_none")] + pub scope: Option, +} diff --git a/seed/rust-sdk/inferred-auth-implicit-api-key/src/api/types/mod.rs b/seed/rust-sdk/inferred-auth-implicit-api-key/src/api/types/mod.rs new file mode 100644 index 000000000000..b3d49c841bad --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-api-key/src/api/types/mod.rs @@ -0,0 +1,3 @@ +pub mod auth_token_response; + +pub use auth_token_response::TokenResponse; diff --git a/seed/rust-sdk/inferred-auth-implicit-api-key/src/client.rs b/seed/rust-sdk/inferred-auth-implicit-api-key/src/client.rs new file mode 100644 index 000000000000..c41cd6d07194 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-api-key/src/client.rs @@ -0,0 +1,92 @@ +use crate::api::resources::InferredAuthImplicitApiKeyClient; +use crate::{ApiError, ClientConfig}; +use std::collections::HashMap; +use std::time::Duration; + +/* +Things to know: + +`impl Into` is a generic parameter constraint in Rust that means "accept any type that can be converted into a String." +It's essentially Rust's way of saying "I'll take a string in any form you give it to me." + +Types that implement Into: + +// All of these work: +builder.api_key("hello") // &str +builder.api_key("hello".to_string()) // String +builder.api_key(format!("key_{}", id)) // String from format! +builder.api_key(my_string_variable) // String variable +*/ + +/// Builder for creating API clients with custom configuration +pub struct ApiClientBuilder { + config: ClientConfig, +} + +impl ApiClientBuilder { + /// Create a new builder with the specified base URL + pub fn new(base_url: impl Into) -> Self { + let mut config = ClientConfig::default(); + config.base_url = base_url.into(); + Self { config } + } + + /// Set the API key for authentication + pub fn api_key(mut self, key: impl Into) -> Self { + self.config.api_key = Some(key.into()); + self + } + + /// Set the bearer token for authentication + pub fn token(mut self, token: impl Into) -> Self { + self.config.token = Some(token.into()); + self + } + + /// Set the username for basic authentication + pub fn username(mut self, username: impl Into) -> Self { + self.config.username = Some(username.into()); + self + } + + /// Set the password for basic authentication + pub fn password(mut self, password: impl Into) -> Self { + self.config.password = Some(password.into()); + self + } + + /// Set the request timeout + pub fn timeout(mut self, timeout: Duration) -> Self { + self.config.timeout = timeout; + self + } + + /// Set the maximum number of retries + pub fn max_retries(mut self, retries: u32) -> Self { + self.config.max_retries = retries; + self + } + + /// Add a custom header + pub fn custom_header(mut self, key: impl Into, value: impl Into) -> Self { + self.config.custom_headers.insert(key.into(), value.into()); + self + } + + /// Add multiple custom headers + pub fn custom_headers(mut self, headers: HashMap) -> Self { + self.config.custom_headers.extend(headers); + self + } + + /// Set the user agent + pub fn user_agent(mut self, user_agent: impl Into) -> Self { + self.config.user_agent = user_agent.into(); + self + } + + /// Build the client with validation + pub fn build(self) -> Result { + InferredAuthImplicitApiKeyClient::new(self.config) + } +} diff --git a/seed/rust-sdk/inferred-auth-implicit-api-key/src/config.rs b/seed/rust-sdk/inferred-auth-implicit-api-key/src/config.rs new file mode 100644 index 000000000000..f9766cb594a9 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-api-key/src/config.rs @@ -0,0 +1,30 @@ +use std::collections::HashMap; +use std::time::Duration; + +#[derive(Debug, Clone)] +pub struct ClientConfig { + pub base_url: String, + pub api_key: Option, + pub token: Option, + pub username: Option, + pub password: Option, + pub timeout: Duration, + pub max_retries: u32, + pub custom_headers: HashMap, + pub user_agent: String, +} +impl Default for ClientConfig { + fn default() -> Self { + Self { + base_url: String::new(), + api_key: None, + token: None, + username: None, + password: None, + timeout: Duration::from_secs(60), + max_retries: 3, + custom_headers: HashMap::new(), + user_agent: "InferredAuthImplicitApiKey Rust SDK".to_string(), + } + } +} diff --git a/seed/rust-sdk/inferred-auth-implicit-api-key/src/core/http_client.rs b/seed/rust-sdk/inferred-auth-implicit-api-key/src/core/http_client.rs new file mode 100644 index 000000000000..e1171a4537f3 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-api-key/src/core/http_client.rs @@ -0,0 +1,512 @@ +use crate::{join_url, ApiError, ClientConfig, RequestOptions}; +use futures::{Stream, StreamExt}; +use reqwest::{ + header::{HeaderName, HeaderValue}, + Client, Method, Request, Response, +}; +use serde::de::DeserializeOwned; +use std::{ + pin::Pin, + str::FromStr, + task::{Context, Poll}, +}; + +/// A streaming byte stream for downloading files efficiently +pub struct ByteStream { + content_length: Option, + inner: Pin> + Send>>, +} + +impl ByteStream { + /// Create a new ByteStream from a Response + pub(crate) fn new(response: Response) -> Self { + let content_length = response.content_length(); + let stream = response.bytes_stream(); + + Self { + content_length, + inner: Box::pin(stream), + } + } + + /// Collect the entire stream into a `Vec` + /// + /// This consumes the stream and buffers all data into memory. + /// For large files, prefer using `try_next()` to process chunks incrementally. + /// + /// # Example + /// ```no_run + /// let stream = client.download_file().await?; + /// let bytes = stream.collect().await?; + /// ``` + pub async fn collect(mut self) -> Result, ApiError> { + let mut result = Vec::new(); + while let Some(chunk) = self.inner.next().await { + result.extend_from_slice(&chunk.map_err(ApiError::Network)?); + } + Ok(result) + } + + /// Get the next chunk from the stream + /// + /// Returns `Ok(Some(bytes))` if a chunk is available, + /// `Ok(None)` if the stream is finished, or an error. + /// + /// # Example + /// ```no_run + /// let mut stream = client.download_file().await?; + /// while let Some(chunk) = stream.try_next().await? { + /// process_chunk(&chunk); + /// } + /// ``` + pub async fn try_next(&mut self) -> Result, ApiError> { + match self.inner.next().await { + Some(Ok(bytes)) => Ok(Some(bytes)), + Some(Err(e)) => Err(ApiError::Network(e)), + None => Ok(None), + } + } + + /// Get the content length from response headers if available + pub fn content_length(&self) -> Option { + self.content_length + } +} + +impl Stream for ByteStream { + type Item = Result; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + match self.inner.as_mut().poll_next(cx) { + Poll::Ready(Some(Ok(bytes))) => Poll::Ready(Some(Ok(bytes))), + Poll::Ready(Some(Err(e))) => Poll::Ready(Some(Err(ApiError::Network(e)))), + Poll::Ready(None) => Poll::Ready(None), + Poll::Pending => Poll::Pending, + } + } +} + +/// Internal HTTP client that handles requests with authentication and retries +#[derive(Clone)] +pub struct HttpClient { + client: Client, + config: ClientConfig, +} + +impl HttpClient { + pub fn new(config: ClientConfig) -> Result { + let client = Client::builder() + .timeout(config.timeout) + .user_agent(&config.user_agent) + .build() + .map_err(ApiError::Network)?; + + Ok(Self { client, config }) + } + + /// Execute a request with the given method, path, and options + pub async fn execute_request( + &self, + method: Method, + path: &str, + body: Option, + query_params: Option>, + options: Option, + ) -> Result + where + T: DeserializeOwned, // Generic T: DeserializeOwned means the response will be automatically deserialized into whatever type you specify: + { + let url = join_url(&self.config.base_url, path); + let mut request = self.client.request(method, &url); + + // Apply query parameters if provided + if let Some(params) = query_params { + request = request.query(¶ms); + } + + // Apply additional query parameters from options + if let Some(opts) = &options { + if !opts.additional_query_params.is_empty() { + request = request.query(&opts.additional_query_params); + } + } + + // Apply body if provided + if let Some(body) = body { + request = request.json(&body); + } + + // Build the request + let mut req = request.build().map_err(|e| ApiError::Network(e))?; + + // Apply authentication and headers + self.apply_auth_headers(&mut req, &options)?; + self.apply_custom_headers(&mut req, &options)?; + + // Execute with retries + let response = self.execute_with_retries(req, &options).await?; + self.parse_response(response).await + } + + /// Execute a multipart/form-data request with the given method, path, and options + /// + /// This method is used for file uploads using reqwest's built-in multipart support. + /// Note: Multipart requests are not retried because they cannot be cloned. + /// + /// # Example + /// ```no_run + /// let form = reqwest::multipart::Form::new() + /// .part("file", reqwest::multipart::Part::bytes(vec![1, 2, 3])); + /// + /// let response: MyResponse = client.execute_multipart_request( + /// Method::POST, + /// "/upload", + /// form, + /// None, + /// None, + /// ).await?; + /// ``` + #[cfg(feature = "multipart")] + pub async fn execute_multipart_request( + &self, + method: Method, + path: &str, + form: reqwest::multipart::Form, + query_params: Option>, + options: Option, + ) -> Result + where + T: DeserializeOwned, + { + let url = join_url(&self.config.base_url, path); + let mut request = self.client.request(method, &url); + + // Apply query parameters if provided + if let Some(params) = query_params { + request = request.query(¶ms); + } + + // Apply additional query parameters from options + if let Some(opts) = &options { + if !opts.additional_query_params.is_empty() { + request = request.query(&opts.additional_query_params); + } + } + + // Use reqwest's built-in multipart support + request = request.multipart(form); + + // Build the request + let mut req = request.build().map_err(|e| ApiError::Network(e))?; + + // Apply authentication and headers + self.apply_auth_headers(&mut req, &options)?; + self.apply_custom_headers(&mut req, &options)?; + + // Execute directly without retries (multipart requests cannot be cloned) + let response = self.client.execute(req).await.map_err(ApiError::Network)?; + + // Check response status + if !response.status().is_success() { + let status_code = response.status().as_u16(); + let body = response.text().await.ok(); + return Err(ApiError::from_response(status_code, body.as_deref())); + } + + self.parse_response(response).await + } + + fn apply_auth_headers( + &self, + request: &mut Request, + options: &Option, + ) -> Result<(), ApiError> { + let headers = request.headers_mut(); + + // Apply API key (request options override config) + let api_key = options + .as_ref() + .and_then(|opts| opts.api_key.as_ref()) + .or(self.config.api_key.as_ref()); + + if let Some(key) = api_key { + headers.insert("api_key", key.parse().map_err(|_| ApiError::InvalidHeader)?); + } + + // Apply bearer token (request options override config) + let token = options + .as_ref() + .and_then(|opts| opts.token.as_ref()) + .or(self.config.token.as_ref()); + + if let Some(token) = token { + let auth_value = format!("Bearer {}", token); + headers.insert( + "Authorization", + auth_value.parse().map_err(|_| ApiError::InvalidHeader)?, + ); + } + + Ok(()) + } + + fn apply_custom_headers( + &self, + request: &mut Request, + options: &Option, + ) -> Result<(), ApiError> { + let headers = request.headers_mut(); + + // Apply config-level custom headers + for (key, value) in &self.config.custom_headers { + headers.insert( + HeaderName::from_str(key).map_err(|_| ApiError::InvalidHeader)?, + HeaderValue::from_str(value).map_err(|_| ApiError::InvalidHeader)?, + ); + } + + // Apply request-level custom headers (override config) + if let Some(options) = options { + for (key, value) in &options.additional_headers { + headers.insert( + HeaderName::from_str(key).map_err(|_| ApiError::InvalidHeader)?, + HeaderValue::from_str(value).map_err(|_| ApiError::InvalidHeader)?, + ); + } + } + + Ok(()) + } + + async fn execute_with_retries( + &self, + request: Request, + options: &Option, + ) -> Result { + let max_retries = options + .as_ref() + .and_then(|opts| opts.max_retries) + .unwrap_or(self.config.max_retries); + + let mut last_error = None; + + for attempt in 0..=max_retries { + let cloned_request = request.try_clone().ok_or(ApiError::RequestClone)?; + + match self.client.execute(cloned_request).await { + Ok(response) if response.status().is_success() => return Ok(response), + Ok(response) => { + let status_code = response.status().as_u16(); + let body = response.text().await.ok(); + return Err(ApiError::from_response(status_code, body.as_deref())); + } + Err(e) if attempt < max_retries => { + last_error = Some(e); + // Exponential backoff + let delay = std::time::Duration::from_millis(100 * 2_u64.pow(attempt)); + tokio::time::sleep(delay).await; + } + Err(e) => return Err(ApiError::Network(e)), + } + } + + Err(ApiError::Network(last_error.unwrap())) + } + + async fn parse_response(&self, response: Response) -> Result + where + T: DeserializeOwned, + { + let text = response.text().await.map_err(ApiError::Network)?; + serde_json::from_str(&text).map_err(ApiError::Serialization) + } + + /// Execute a request and return a streaming response (for large file downloads) + /// + /// This method returns a `ByteStream` that can be used to download large files + /// efficiently without loading the entire content into memory. The stream can be + /// consumed chunk by chunk, written directly to disk, or collected into bytes. + /// + /// # Examples + /// + /// **Option 1: Collect all bytes into memory** + /// ```no_run + /// let stream = client.execute_stream_request( + /// Method::GET, + /// "/file", + /// None, + /// None, + /// None, + /// ).await?; + /// + /// let bytes = stream.collect().await?; + /// ``` + /// + /// **Option 2: Process chunks with try_next()** + /// ```no_run + /// let mut stream = client.execute_stream_request( + /// Method::GET, + /// "/large-file", + /// None, + /// None, + /// None, + /// ).await?; + /// + /// while let Some(chunk) = stream.try_next().await? { + /// process_chunk(&chunk); + /// } + /// ``` + /// + /// **Option 3: Stream with futures::Stream trait** + /// ```no_run + /// use futures::StreamExt; + /// + /// let stream = client.execute_stream_request( + /// Method::GET, + /// "/large-file", + /// None, + /// None, + /// None, + /// ).await?; + /// + /// let mut file = tokio::fs::File::create("output.mp4").await?; + /// let mut stream = std::pin::pin!(stream); + /// while let Some(chunk) = stream.next().await { + /// let chunk = chunk?; + /// tokio::io::AsyncWriteExt::write_all(&mut file, &chunk).await?; + /// } + /// ``` + pub async fn execute_stream_request( + &self, + method: Method, + path: &str, + body: Option, + query_params: Option>, + options: Option, + ) -> Result { + let url = join_url(&self.config.base_url, path); + let mut request = self.client.request(method, &url); + + // Apply query parameters if provided + if let Some(params) = query_params { + request = request.query(¶ms); + } + + // Apply additional query parameters from options + if let Some(opts) = &options { + if !opts.additional_query_params.is_empty() { + request = request.query(&opts.additional_query_params); + } + } + + // Apply body if provided + if let Some(body) = body { + request = request.json(&body); + } + + // Build the request + let mut req = request.build().map_err(|e| ApiError::Network(e))?; + + // Apply authentication and headers + self.apply_auth_headers(&mut req, &options)?; + self.apply_custom_headers(&mut req, &options)?; + + // Execute with retries + let response = self.execute_with_retries(req, &options).await?; + + // Return streaming response + Ok(ByteStream::new(response)) + } + + /// Execute a request and return an SSE stream + /// + /// This method returns an `SseStream` that automatically parses + /// Server-Sent Events and deserializes the JSON data in each event. + /// + /// # SSE-Specific Headers + /// + /// This method automatically sets the following headers **after** applying custom headers, + /// which means these headers will override any user-supplied values: + /// - `Accept: text/event-stream` - Required for SSE protocol + /// - `Cache-Control: no-store` - Prevents caching of streaming responses + /// + /// This ensures proper SSE behavior even if custom headers are provided. + /// + /// # Example + /// ```no_run + /// use futures::StreamExt; + /// + /// let stream = client.execute_sse_request::( + /// Method::POST, + /// "/stream", + /// Some(serde_json::json!({"query": "Hello"})), + /// None, + /// None, + /// Some("[[DONE]]".to_string()), + /// ).await?; + /// + /// let mut stream = std::pin::pin!(stream); + /// while let Some(chunk) = stream.next().await { + /// let chunk = chunk?; + /// println!("Received: {:?}", chunk); + /// } + /// ``` + #[cfg(feature = "sse")] + pub async fn execute_sse_request( + &self, + method: Method, + path: &str, + body: Option, + query_params: Option>, + options: Option, + terminator: Option, + ) -> Result, ApiError> + where + T: DeserializeOwned + Send + 'static, + { + let url = join_url(&self.config.base_url, path); + let mut request = self.client.request(method, &url); + + // Apply query parameters if provided + if let Some(params) = query_params { + request = request.query(¶ms); + } + + // Apply additional query parameters from options + if let Some(opts) = &options { + if !opts.additional_query_params.is_empty() { + request = request.query(&opts.additional_query_params); + } + } + + // Apply body if provided + if let Some(body) = body { + request = request.json(&body); + } + + // Build the request + let mut req = request.build().map_err(|e| ApiError::Network(e))?; + + // Apply authentication and headers + self.apply_auth_headers(&mut req, &options)?; + self.apply_custom_headers(&mut req, &options)?; + + // SSE-specific headers + req.headers_mut().insert( + "Accept", + "text/event-stream" + .parse() + .map_err(|_| ApiError::InvalidHeader)?, + ); + req.headers_mut().insert( + "Cache-Control", + "no-store".parse().map_err(|_| ApiError::InvalidHeader)?, + ); + + // Execute with retries + let response = self.execute_with_retries(req, &options).await?; + + // Return SSE stream + crate::SseStream::new(response, terminator).await + } +} diff --git a/seed/rust-sdk/inferred-auth-implicit-api-key/src/core/mod.rs b/seed/rust-sdk/inferred-auth-implicit-api-key/src/core/mod.rs new file mode 100644 index 000000000000..ba23f97e4acd --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-api-key/src/core/mod.rs @@ -0,0 +1,15 @@ +//! Core client infrastructure + +mod http_client; +mod query_parameter_builder; +mod request_options; +#[cfg(feature = "sse")] +mod sse_stream; +mod utils; + +pub use http_client::{ByteStream, HttpClient}; +pub use query_parameter_builder::{parse_structured_query, QueryBuilder, QueryBuilderError}; +pub use request_options::RequestOptions; +#[cfg(feature = "sse")] +pub use sse_stream::SseStream; +pub use utils::join_url; diff --git a/seed/rust-sdk/inferred-auth-implicit-api-key/src/core/pagination.rs b/seed/rust-sdk/inferred-auth-implicit-api-key/src/core/pagination.rs new file mode 100644 index 000000000000..2cd716dc82a3 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-api-key/src/core/pagination.rs @@ -0,0 +1,282 @@ +use std::collections::VecDeque; +use std::future::Future; +use std::pin::Pin; +use std::sync::Arc; +use std::task::{Context, Poll}; + +use futures::Stream; +use serde_json::Value; + +use crate::{ApiError, HttpClient}; + +/// Result of a pagination request +#[derive(Debug)] +pub struct PaginationResult { + pub items: Vec, + pub next_cursor: Option, + pub has_next_page: bool, +} + +/// Async paginator that implements Stream for iterating over paginated results +pub struct AsyncPaginator { + http_client: Arc, + page_loader: Box< + dyn Fn( + Arc, + Option, + ) + -> Pin, ApiError>> + Send>> + + Send + + Sync, + >, + current_page: VecDeque, + current_cursor: Option, + has_next_page: bool, + loading_next: + Option, ApiError>> + Send>>>, +} + +impl AsyncPaginator { + pub fn new( + http_client: Arc, + page_loader: F, + initial_cursor: Option, + ) -> Result + where + F: Fn(Arc, Option) -> Fut + Send + Sync + 'static, + Fut: Future, ApiError>> + Send + 'static, + { + Ok(Self { + http_client, + page_loader: Box::new(move |client, cursor| Box::pin(page_loader(client, cursor))), + current_page: VecDeque::new(), + current_cursor: initial_cursor, + has_next_page: true, // Assume true initially, will be updated after first request + loading_next: None, + }) + } + + /// Check if there are more pages available + pub fn has_next_page(&self) -> bool { + !self.current_page.is_empty() || self.has_next_page + } + + /// Load the next page explicitly + pub async fn next_page(&mut self) -> Result, ApiError> { + if !self.has_next_page { + return Ok(Vec::new()); + } + + let result = + (self.page_loader)(self.http_client.clone(), self.current_cursor.clone()).await?; + + self.current_cursor = result.next_cursor; + self.has_next_page = result.has_next_page; + + Ok(result.items) + } +} + +impl Stream for AsyncPaginator +where + T: Unpin, +{ + type Item = Result; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + // If we have items in the current page, return the next one + if let Some(item) = self.current_page.pop_front() { + return Poll::Ready(Some(Ok(item))); + } + + // If we're already loading the next page, poll that future + if let Some(ref mut loading_future) = self.loading_next { + match loading_future.as_mut().poll(cx) { + Poll::Ready(Ok(result)) => { + self.current_page.extend(result.items); + self.current_cursor = result.next_cursor; + self.has_next_page = result.has_next_page; + self.loading_next = None; + + // Try to get the next item from the newly loaded page + if let Some(item) = self.current_page.pop_front() { + return Poll::Ready(Some(Ok(item))); + } else if !self.has_next_page { + return Poll::Ready(None); + } + // Fall through to start loading next page + } + Poll::Ready(Err(e)) => { + self.loading_next = None; + return Poll::Ready(Some(Err(e))); + } + Poll::Pending => return Poll::Pending, + } + } + + // If we have no more pages to load, we're done + if !self.has_next_page { + return Poll::Ready(None); + } + + // Start loading the next page + let future = (self.page_loader)(self.http_client.clone(), self.current_cursor.clone()); + self.loading_next = Some(future); + + // Poll the future immediately + if let Some(ref mut loading_future) = self.loading_next { + match loading_future.as_mut().poll(cx) { + Poll::Ready(Ok(result)) => { + self.current_page.extend(result.items); + self.current_cursor = result.next_cursor; + self.has_next_page = result.has_next_page; + self.loading_next = None; + + if let Some(item) = self.current_page.pop_front() { + Poll::Ready(Some(Ok(item))) + } else if !self.has_next_page { + Poll::Ready(None) + } else { + // This shouldn't happen, but just in case + cx.waker().wake_by_ref(); + Poll::Pending + } + } + Poll::Ready(Err(e)) => { + self.loading_next = None; + Poll::Ready(Some(Err(e))) + } + Poll::Pending => Poll::Pending, + } + } else { + Poll::Pending + } + } +} + +/// Synchronous paginator for blocking iteration +pub struct SyncPaginator { + http_client: Arc, + page_loader: Box< + dyn Fn(Arc, Option) -> Result, ApiError> + + Send + + Sync, + >, + current_page: VecDeque, + current_cursor: Option, + has_next_page: bool, +} + +impl SyncPaginator { + pub fn new( + http_client: Arc, + page_loader: F, + initial_cursor: Option, + ) -> Result + where + F: Fn(Arc, Option) -> Result, ApiError> + + Send + + Sync + + 'static, + { + Ok(Self { + http_client, + page_loader: Box::new(page_loader), + current_page: VecDeque::new(), + current_cursor: initial_cursor, + has_next_page: true, // Assume true initially + }) + } + + /// Check if there are more pages available + pub fn has_next_page(&self) -> bool { + !self.current_page.is_empty() || self.has_next_page + } + + /// Load the next page explicitly + pub fn next_page(&mut self) -> Result, ApiError> { + if !self.has_next_page { + return Ok(Vec::new()); + } + + let result = (self.page_loader)(self.http_client.clone(), self.current_cursor.clone())?; + + self.current_cursor = result.next_cursor; + self.has_next_page = result.has_next_page; + + Ok(result.items) + } + + /// Get all remaining items by loading all pages + pub fn collect_all(&mut self) -> Result, ApiError> { + let mut all_items = Vec::new(); + + // Add items from current page + while let Some(item) = self.current_page.pop_front() { + all_items.push(item); + } + + // Load all remaining pages + while self.has_next_page { + let page_items = self.next_page()?; + all_items.extend(page_items); + } + + Ok(all_items) + } +} + +impl Iterator for SyncPaginator { + type Item = Result; + + fn next(&mut self) -> Option { + // If we have items in the current page, return the next one + if let Some(item) = self.current_page.pop_front() { + return Some(Ok(item)); + } + + // If we have no more pages to load, we're done + if !self.has_next_page { + return None; + } + + // Load the next page + match (self.page_loader)(self.http_client.clone(), self.current_cursor.clone()) { + Ok(result) => { + self.current_page.extend(result.items); + self.current_cursor = result.next_cursor; + self.has_next_page = result.has_next_page; + + // Return the first item from the newly loaded page + self.current_page.pop_front().map(Ok) + } + Err(e) => Some(Err(e)), + } + } +} + +/// Trait for types that can provide pagination metadata +pub trait Paginated { + /// Extract the items from this page + fn items(&self) -> &[T]; + + /// Get the cursor for the next page, if any + fn next_cursor(&self) -> Option<&str>; + + /// Check if there's a next page available + fn has_next_page(&self) -> bool; +} + +/// Trait for types that can provide offset-based pagination metadata +pub trait OffsetPaginated { + /// Extract the items from this page + fn items(&self) -> &[T]; + + /// Check if there's a next page available + fn has_next_page(&self) -> bool; + + /// Get the current page size (for calculating next offset) + fn page_size(&self) -> usize { + self.items().len() + } +} diff --git a/seed/rust-sdk/inferred-auth-implicit-api-key/src/core/query_parameter_builder.rs b/seed/rust-sdk/inferred-auth-implicit-api-key/src/core/query_parameter_builder.rs new file mode 100644 index 000000000000..24e78f885611 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-api-key/src/core/query_parameter_builder.rs @@ -0,0 +1,297 @@ +use chrono::{DateTime, Utc}; +use serde::Serialize; + +/// Modern query builder with type-safe method chaining +/// Provides a clean, Swift-like API for building HTTP query parameters +#[derive(Debug, Default)] +pub struct QueryBuilder { + params: Vec<(String, String)>, +} + +impl QueryBuilder { + /// Create a new query parameter builder + pub fn new() -> Self { + Self::default() + } + + /// Add a string parameter (accept both required/optional) + pub fn string(mut self, key: &str, value: impl Into>) -> Self { + if let Some(v) = value.into() { + self.params.push((key.to_string(), v)); + } + self + } + + /// Add multiple string parameters with the same key (for allow-multiple query params) + /// Accepts both Vec and Vec>, adding each non-None value as a separate query parameter + pub fn string_array(mut self, key: &str, values: I) -> Self + where + I: IntoIterator, + T: Into>, + { + for value in values { + if let Some(v) = value.into() { + self.params.push((key.to_string(), v)); + } + } + self + } + + /// Add an integer parameter (accept both required/optional) + pub fn int(mut self, key: &str, value: impl Into>) -> Self { + if let Some(v) = value.into() { + self.params.push((key.to_string(), v.to_string())); + } + self + } + + /// Add a big integer parameter (accept both required/optional) + pub fn big_int(mut self, key: &str, value: impl Into>) -> Self { + if let Some(v) = value.into() { + self.params.push((key.to_string(), v.to_string())); + } + self + } + + /// Add multiple integer parameters with the same key (for allow-multiple query params) + /// Accepts both Vec and Vec>, adding each non-None value as a separate query parameter + pub fn int_array(mut self, key: &str, values: I) -> Self + where + I: IntoIterator, + T: Into>, + { + for value in values { + if let Some(v) = value.into() { + self.params.push((key.to_string(), v.to_string())); + } + } + self + } + + /// Add a float parameter + pub fn float(mut self, key: &str, value: impl Into>) -> Self { + if let Some(v) = value.into() { + self.params.push((key.to_string(), v.to_string())); + } + self + } + + /// Add multiple float parameters with the same key (for allow-multiple query params) + /// Accepts both Vec and Vec>, adding each non-None value as a separate query parameter + pub fn float_array(mut self, key: &str, values: I) -> Self + where + I: IntoIterator, + T: Into>, + { + for value in values { + if let Some(v) = value.into() { + self.params.push((key.to_string(), v.to_string())); + } + } + self + } + + /// Add a boolean parameter + pub fn bool(mut self, key: &str, value: impl Into>) -> Self { + if let Some(v) = value.into() { + self.params.push((key.to_string(), v.to_string())); + } + self + } + + /// Add multiple boolean parameters with the same key (for allow-multiple query params) + /// Accepts both Vec and Vec>, adding each non-None value as a separate query parameter + pub fn bool_array(mut self, key: &str, values: I) -> Self + where + I: IntoIterator, + T: Into>, + { + for value in values { + if let Some(v) = value.into() { + self.params.push((key.to_string(), v.to_string())); + } + } + self + } + + /// Add a datetime parameter + pub fn datetime(mut self, key: &str, value: impl Into>>) -> Self { + if let Some(v) = value.into() { + self.params.push((key.to_string(), v.to_rfc3339())); + } + self + } + + /// Add a UUID parameter (converts to string) + pub fn uuid(mut self, key: &str, value: impl Into>) -> Self { + if let Some(v) = value.into() { + self.params.push((key.to_string(), v.to_string())); + } + self + } + + /// Add a date parameter (converts NaiveDate to DateTime) + pub fn date(mut self, key: &str, value: impl Into>) -> Self { + if let Some(v) = value.into() { + // Convert NaiveDate to DateTime at start of day + let datetime = v.and_hms_opt(0, 0, 0).unwrap().and_utc(); + self.params.push((key.to_string(), datetime.to_rfc3339())); + } + self + } + + /// Add any serializable parameter (for enums and complex types) + pub fn serialize(mut self, key: &str, value: Option) -> Self { + if let Some(v) = value { + // For enums that implement Display, use the Display implementation + // to avoid JSON quotes in query parameters + if let Ok(serialized) = serde_json::to_string(&v) { + // Remove JSON quotes if the value is a simple string + let cleaned = if serialized.starts_with('"') && serialized.ends_with('"') { + serialized.trim_matches('"').to_string() + } else { + serialized + }; + self.params.push((key.to_string(), cleaned)); + } + } + self + } + + /// Add multiple serializable parameters with the same key (for allow-multiple query params with enums) + /// Accepts both Vec and Vec>, adding each non-None value as a separate query parameter + pub fn serialize_array( + mut self, + key: &str, + values: impl IntoIterator, + ) -> Self { + for value in values { + if let Ok(serialized) = serde_json::to_string(&value) { + // Skip null values (from Option::None) + if serialized == "null" { + continue; + } + // Remove JSON quotes if the value is a simple string + let cleaned = if serialized.starts_with('"') && serialized.ends_with('"') { + serialized.trim_matches('"').to_string() + } else { + serialized + }; + self.params.push((key.to_string(), cleaned)); + } + } + self + } + + /// Parse and add a structured query string + /// Handles complex query patterns like: + /// - "key:value" patterns + /// - "key:value1,value2" (comma-separated values) + /// - Quoted values: "key:\"value with spaces\"" + /// - Space-separated terms (treated as AND logic) + pub fn structured_query(mut self, key: &str, value: impl Into>) -> Self { + if let Some(query_str) = value.into() { + if let Ok(parsed_params) = parse_structured_query(&query_str) { + self.params.extend(parsed_params); + } else { + // Fall back to simple query parameter if parsing fails + self.params.push((key.to_string(), query_str)); + } + } + self + } + + /// Build the final query parameters + pub fn build(self) -> Option> { + if self.params.is_empty() { + None + } else { + Some(self.params) + } + } +} + +/// Errors that can occur during structured query parsing +#[derive(Debug)] +pub enum QueryBuilderError { + InvalidQuerySyntax(String), +} + +impl std::fmt::Display for QueryBuilderError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + QueryBuilderError::InvalidQuerySyntax(msg) => { + write!(f, "Invalid query syntax: {}", msg) + } + } + } +} + +impl std::error::Error for QueryBuilderError {} + +/// Parse structured query strings like "key:value key2:value1,value2" +/// Used for complex filtering patterns in APIs like Foxglove +/// +/// Supported patterns: +/// - Simple: "status:active" +/// - Multiple values: "type:sensor,camera" +/// - Quoted values: "location:\"New York\"" +/// - Complex: "status:active type:sensor location:\"San Francisco\"" +pub fn parse_structured_query(query: &str) -> Result, QueryBuilderError> { + let mut params = Vec::new(); + let terms = tokenize_query(query); + + for term in terms { + if let Some((key, values)) = term.split_once(':') { + // Handle comma-separated values + for value in values.split(',') { + let clean_value = value.trim_matches('"'); // Remove quotes + params.push((key.to_string(), clean_value.to_string())); + } + } else { + // For terms without colons, return error to be explicit about expected format + return Err(QueryBuilderError::InvalidQuerySyntax(format!( + "Cannot parse term '{}' - expected 'key:value' format for structured queries", + term + ))); + } + } + + Ok(params) +} + +/// Tokenize a query string, properly handling quoted strings +fn tokenize_query(input: &str) -> Vec { + let mut tokens = Vec::new(); + let mut current_token = String::new(); + let mut in_quotes = false; + let mut chars = input.chars().peekable(); + + while let Some(c) = chars.next() { + match c { + '"' => { + // Toggle quote state and include the quote in the token + in_quotes = !in_quotes; + current_token.push(c); + } + ' ' if !in_quotes => { + // Space outside quotes - end current token + if !current_token.is_empty() { + tokens.push(current_token.trim().to_string()); + current_token.clear(); + } + } + _ => { + // Any other character (including spaces inside quotes) + current_token.push(c); + } + } + } + + // Add the last token if there is one + if !current_token.is_empty() { + tokens.push(current_token.trim().to_string()); + } + + tokens +} diff --git a/seed/rust-sdk/inferred-auth-implicit-api-key/src/core/request_options.rs b/seed/rust-sdk/inferred-auth-implicit-api-key/src/core/request_options.rs new file mode 100644 index 000000000000..dbd4d60d4940 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-api-key/src/core/request_options.rs @@ -0,0 +1,58 @@ +use std::collections::HashMap; +/// Options for customizing individual requests +#[derive(Debug, Clone, Default)] +pub struct RequestOptions { + /// API key for authentication (overrides client-level API key) + pub api_key: Option, + /// Bearer token for authentication (overrides client-level token) + pub token: Option, + /// Maximum number of retry attempts for failed requests + pub max_retries: Option, + /// Request timeout in seconds (overrides client-level timeout) + pub timeout_seconds: Option, + /// Additional headers to include in the request + pub additional_headers: HashMap, + /// Additional query parameters to include in the request + pub additional_query_params: HashMap, +} + +impl RequestOptions { + pub fn new() -> Self { + Self::default() + } + + pub fn api_key(mut self, key: impl Into) -> Self { + self.api_key = Some(key.into()); + self + } + + pub fn token(mut self, token: impl Into) -> Self { + self.token = Some(token.into()); + self + } + + pub fn max_retries(mut self, retries: u32) -> Self { + self.max_retries = Some(retries); + self + } + + pub fn timeout_seconds(mut self, timeout: u64) -> Self { + self.timeout_seconds = Some(timeout); + self + } + + pub fn additional_header(mut self, key: impl Into, value: impl Into) -> Self { + self.additional_headers.insert(key.into(), value.into()); + self + } + + pub fn additional_query_param( + mut self, + key: impl Into, + value: impl Into, + ) -> Self { + self.additional_query_params + .insert(key.into(), value.into()); + self + } +} diff --git a/seed/rust-sdk/inferred-auth-implicit-api-key/src/core/sse_stream.rs b/seed/rust-sdk/inferred-auth-implicit-api-key/src/core/sse_stream.rs new file mode 100644 index 000000000000..50a375e0a7ff --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-api-key/src/core/sse_stream.rs @@ -0,0 +1,281 @@ +use crate::ApiError; +use futures::Stream; +use pin_project::pin_project; +use reqwest::{header::CONTENT_TYPE, Response}; +use reqwest_sse::{error::EventError, Event, EventSource}; +use serde::de::DeserializeOwned; +use std::{ + marker::PhantomData, + pin::Pin, + task::{Context, Poll}, +}; + +/// Metadata from a Server-Sent Event +/// +/// Contains the SSE protocol fields (event, id, retry) that accompany the data payload. +/// This struct provides access to SSE metadata for advanced use cases. +/// +/// Per the SSE specification: +/// - `event` defaults to "message" when not specified by the server +/// - `id` is optional and used for reconnection support (Last-Event-ID header) +/// - `retry` is optional and specifies reconnection timeout in milliseconds +#[derive(Debug, Clone)] +pub struct SseMetadata { + /// The event type (defaults to "message" per SSE spec if not specified by server) + pub event: String, + /// The event ID for reconnection support (None if not specified) + pub id: Option, + /// Retry timeout in milliseconds (None if not specified) + pub retry: Option, +} + +/// A Server-Sent Event with both data and metadata +/// +/// Contains the deserialized data payload along with SSE protocol metadata. +/// Use this when you need access to event IDs, event types, or retry information. +#[derive(Debug)] +pub struct SseEvent { + /// The deserialized data payload + pub data: T, + /// SSE protocol metadata + pub metadata: SseMetadata, +} + +/// A type-safe wrapper around Server-Sent Events (SSE) streams +/// +/// Leverages `reqwest-sse` for SSE protocol parsing and adds: +/// - Automatic JSON deserialization to typed structs +/// - Stream terminator support (e.g., `[DONE]` for OpenAI-style APIs) +/// - Integrated error handling with `ApiError` +/// - Content-Type validation (`text/event-stream` required) +/// +/// # Charset Handling +/// +/// The `reqwest-sse` library automatically handles charset detection and decoding +/// based on the Content-Type header. If no charset is specified, UTF-8 is assumed. +/// This matches the SSE specification default behavior. +/// +/// # Example +/// +/// Basic usage with async iteration: +/// +/// ```no_run +/// use futures::StreamExt; +/// +/// let stream: SseStream = client.stream_completions(request).await?; +/// let mut stream = std::pin::pin!(stream); +/// +/// while let Some(result) = stream.next().await { +/// match result { +/// Ok(chunk) => println!("Received: {:?}", chunk), +/// Err(e) => eprintln!("Error: {}", e), +/// } +/// } +/// ``` +/// +/// # Error Handling +/// +/// The stream yields `Result` items. Errors can occur from: +/// - Invalid JSON in SSE data field (`ApiError::Serialization`) +/// - SSE protocol errors (`ApiError::SseParseError`) +/// - Network errors during streaming +/// +/// **Important:** When an error occurs for a single event (e.g., malformed JSON), +/// the stream yields `Err` for that item but **continues streaming** subsequent events. +/// The stream only ends when: +/// - A terminator is received (if configured) +/// - The server closes the connection +/// - A fatal network error occurs +/// +/// This allows the client to handle per-event errors gracefully without losing +/// the entire stream. Compare this to other error handling strategies where a single +/// bad event might terminate the stream. +/// +/// # Terminator Support +/// +/// When a terminator string is specified (e.g., `[DONE]`), the stream automatically +/// ends when an SSE event with that exact data is received. The terminator event +/// itself is not yielded to the consumer. +#[pin_project] +pub struct SseStream { + #[pin] + inner: Pin> + Send>>, + terminator: Option, + _phantom: PhantomData, +} + +impl SseStream +where + T: DeserializeOwned, +{ + /// Create a new SSE stream from a Response + /// + /// # Arguments + /// * `response` - The HTTP response to parse as SSE + /// * `terminator` - Optional terminator string (e.g., `"[DONE]"`) that signals end of stream + /// + /// # Errors + /// Returns `ApiError::SseParseError` if: + /// - Response Content-Type is not `text/event-stream` + /// - SSE stream cannot be created from response + pub(crate) async fn new( + response: Response, + terminator: Option, + ) -> Result { + // Validate Content-Type header (case-insensitive, handles parameters) + let content_type = response + .headers() + .get(CONTENT_TYPE) + .and_then(|v| v.to_str().ok()) + .unwrap_or(""); + + // Extract main content type (before ';' parameter separator) and compare case-insensitively + let content_type_main = content_type.split(';').next().unwrap_or("").trim(); + + if !content_type_main.eq_ignore_ascii_case("text/event-stream") { + return Err(ApiError::SseParseError(format!( + "Expected Content-Type to be 'text/event-stream', got '{}'", + content_type + ))); + } + + // Use reqwest-sse's EventSource trait to get SSE stream + let events = response + .events() + .await + .map_err(|e| ApiError::SseParseError(e.to_string()))?; + + Ok(Self { + inner: Box::pin(events), + terminator, + _phantom: PhantomData, + }) + } +} + +impl SseStream +where + T: DeserializeOwned, +{ + /// Convert this stream into one that yields events with metadata + /// + /// This consumes the stream and returns a new stream that yields `SseEvent` + /// instead of just `T`, providing access to SSE metadata (event type, id, retry). + /// + /// # Example + /// + /// ```no_run + /// use futures::StreamExt; + /// + /// let stream = client.stream_completions(request).await?; + /// let mut stream_with_metadata = stream.with_metadata(); + /// let mut stream_with_metadata = std::pin::pin!(stream_with_metadata); + /// + /// while let Some(result) = stream_with_metadata.next().await { + /// match result { + /// Ok(event) => { + /// println!("Data: {:?}", event.data); + /// println!("Event type: {}", event.metadata.event); + /// if let Some(id) = &event.metadata.id { + /// println!("Event ID: {}", id); + /// } + /// } + /// Err(e) => eprintln!("Error: {}", e), + /// } + /// } + /// ``` + pub fn with_metadata(self) -> SseStreamWithMetadata { + SseStreamWithMetadata { inner: self } + } +} + +impl Stream for SseStream +where + T: DeserializeOwned, +{ + type Item = Result; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + match this.inner.poll_next(cx) { + Poll::Ready(Some(Ok(event))) => { + // Check for terminator before parsing + if let Some(ref terminator) = this.terminator { + if event.data == *terminator { + // Terminator found - end stream cleanly + return Poll::Ready(None); + } + } + + // Deserialize JSON data to typed struct + match serde_json::from_str(&event.data) { + Ok(value) => Poll::Ready(Some(Ok(value))), + Err(e) => Poll::Ready(Some(Err(ApiError::Serialization(e)))), + } + } + Poll::Ready(Some(Err(e))) => { + Poll::Ready(Some(Err(ApiError::SseParseError(e.to_string())))) + } + Poll::Ready(None) => Poll::Ready(None), + Poll::Pending => Poll::Pending, + } + } +} + +/// Stream wrapper that yields events with metadata +/// +/// Created by calling [`SseStream::with_metadata()`]. This stream yields `SseEvent` +/// which includes both the deserialized data and SSE protocol metadata. +#[pin_project] +pub struct SseStreamWithMetadata { + #[pin] + inner: SseStream, +} + +impl Stream for SseStreamWithMetadata +where + T: DeserializeOwned, +{ + type Item = Result, ApiError>; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + + // Access the inner stream's fields through pin projection + let inner_pin = this.inner.project(); + + match inner_pin.inner.poll_next(cx) { + Poll::Ready(Some(Ok(event))) => { + // Check for terminator + if let Some(ref terminator) = inner_pin.terminator { + if event.data == *terminator { + return Poll::Ready(None); + } + } + + // Extract metadata + let metadata = SseMetadata { + // Default to "message" if event type is empty (per SSE spec) + event: if event.event_type.is_empty() { + "message".to_string() + } else { + event.event_type.clone() + }, + id: event.last_event_id.clone(), + retry: event.retry.map(|d| d.as_millis() as u64), + }; + + // Deserialize JSON data + match serde_json::from_str(&event.data) { + Ok(data) => Poll::Ready(Some(Ok(SseEvent { data, metadata }))), + Err(e) => Poll::Ready(Some(Err(ApiError::Serialization(e)))), + } + } + Poll::Ready(Some(Err(e))) => { + Poll::Ready(Some(Err(ApiError::SseParseError(e.to_string())))) + } + Poll::Ready(None) => Poll::Ready(None), + Poll::Pending => Poll::Pending, + } + } +} diff --git a/seed/rust-sdk/inferred-auth-implicit-api-key/src/core/utils.rs b/seed/rust-sdk/inferred-auth-implicit-api-key/src/core/utils.rs new file mode 100644 index 000000000000..808056dc34a2 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-api-key/src/core/utils.rs @@ -0,0 +1,19 @@ +/// URL building utilities +/// Safely join a base URL with a path, handling slashes properly +/// +/// # Examples +/// ``` +/// use example_api::utils::url::join_url; +/// +/// assert_eq!(join_url("https://api.example.com", "users"), "https://api.example.com/users"); +/// assert_eq!(join_url("https://api.example.com/", "users"), "https://api.example.com/users"); +/// assert_eq!(join_url("https://api.example.com", "/users"), "https://api.example.com/users"); +/// assert_eq!(join_url("https://api.example.com/", "/users"), "https://api.example.com/users"); +/// ``` +pub fn join_url(base_url: &str, path: &str) -> String { + format!( + "{}/{}", + base_url.trim_end_matches('/'), + path.trim_start_matches('/') + ) +} diff --git a/seed/rust-sdk/inferred-auth-implicit-api-key/src/error.rs b/seed/rust-sdk/inferred-auth-implicit-api-key/src/error.rs new file mode 100644 index 000000000000..7feca158a561 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-api-key/src/error.rs @@ -0,0 +1,32 @@ +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum ApiError { + #[error("HTTP error {status}: {message}")] + Http { status: u16, message: String }, + #[error("Network error: {0}")] + Network(reqwest::Error), + #[error("Serialization error: {0}")] + Serialization(serde_json::Error), + #[error("Configuration error: {0}")] + Configuration(String), + #[error("Invalid header value")] + InvalidHeader, + #[error("Could not clone request for retry")] + RequestClone, + #[error("SSE stream terminated")] + StreamTerminated, + #[error("SSE parse error: {0}")] + SseParseError(String), +} + +impl ApiError { + pub fn from_response(status_code: u16, body: Option<&str>) -> Self { + match status_code { + _ => Self::Http { + status: status_code, + message: body.unwrap_or("Unknown error").to_string(), + }, + } + } +} diff --git a/seed/rust-sdk/inferred-auth-implicit-api-key/src/lib.rs b/seed/rust-sdk/inferred-auth-implicit-api-key/src/lib.rs new file mode 100644 index 000000000000..0a9c197a0a34 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-api-key/src/lib.rs @@ -0,0 +1,45 @@ +//! # InferredAuthImplicitApiKey SDK +//! +//! The official Rust SDK for the InferredAuthImplicitApiKey. +//! +//! ## Getting Started +//! +//! ```rust +//! use seed_inferred_auth_implicit_api_key::prelude::*; +//! +//! #[tokio::main] +//! async fn main() { +//! let config = ClientConfig { +//! ..Default::default() +//! }; +//! let client = InferredAuthImplicitApiKeyClient::new(config).expect("Failed to build client"); +//! client +//! .auth +//! .get_token(Some( +//! RequestOptions::new().additional_header("X-Api-Key", "api_key".to_string()), +//! )) +//! .await; +//! } +//! ``` +//! +//! ## Modules +//! +//! - [`api`] - Core API types and models +//! - [`client`] - Client implementations +//! - [`config`] - Configuration options +//! - [`core`] - Core utilities and infrastructure +//! - [`error`] - Error types and handling +//! - [`prelude`] - Common imports for convenience + +pub mod api; +pub mod client; +pub mod config; +pub mod core; +pub mod error; +pub mod prelude; + +pub use api::*; +pub use client::*; +pub use config::*; +pub use core::*; +pub use error::ApiError; diff --git a/seed/rust-sdk/inferred-auth-implicit-api-key/src/prelude.rs b/seed/rust-sdk/inferred-auth-implicit-api-key/src/prelude.rs new file mode 100644 index 000000000000..73a3315404c2 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-api-key/src/prelude.rs @@ -0,0 +1,19 @@ +//! Prelude module for convenient imports +//! +//! This module re-exports the most commonly used types and traits. +//! Import it with: `use seed_inferred_auth_implicit_api_key::prelude::*;` + +// Client and configuration +pub use crate::config::ClientConfig; +pub use crate::core::{HttpClient, RequestOptions}; +pub use crate::error::ApiError; + +// Main client and resource clients +pub use crate::api::*; + +// Re-export commonly used external types +pub use ordered_float::OrderedFloat; +pub use serde::{Deserialize, Serialize}; +pub use serde_json::{json, Value}; +pub use std::collections::{HashMap, HashSet}; +pub use std::fmt; diff --git a/seed/rust-sdk/inferred-auth-implicit-reference/.fern/metadata.json b/seed/rust-sdk/inferred-auth-implicit-reference/.fern/metadata.json new file mode 100644 index 000000000000..ce1c0cd76077 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-reference/.fern/metadata.json @@ -0,0 +1,5 @@ +{ + "cliVersion": "DUMMY", + "generatorName": "fernapi/fern-rust-sdk", + "generatorVersion": "latest" +} \ No newline at end of file diff --git a/seed/rust-sdk/inferred-auth-implicit-reference/.github/workflows/ci.yml b/seed/rust-sdk/inferred-auth-implicit-reference/.github/workflows/ci.yml new file mode 100644 index 000000000000..1a311af94152 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-reference/.github/workflows/ci.yml @@ -0,0 +1,59 @@ +name: ci + +on: [push] + +env: + RUSTFLAGS: "-A warnings" + +jobs: + check: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Rust + uses: actions-rust-lang/setup-rust-toolchain@v1 + + - name: Check + run: cargo check + + compile: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Rust + uses: actions-rust-lang/setup-rust-toolchain@v1 + + - name: Compile + run: cargo build + + test: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Rust + uses: actions-rust-lang/setup-rust-toolchain@v1 + + - name: Test + run: cargo test + + publish: + needs: [check, compile, test] + if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Rust + uses: actions-rust-lang/setup-rust-toolchain@v1 + + - name: Publish + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + run: cargo publish \ No newline at end of file diff --git a/seed/rust-sdk/inferred-auth-implicit-reference/.gitignore b/seed/rust-sdk/inferred-auth-implicit-reference/.gitignore new file mode 100644 index 000000000000..f75d27799da1 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-reference/.gitignore @@ -0,0 +1,5 @@ +/target +**/*.rs.bk +Cargo.lock +.DS_Store +*.swp \ No newline at end of file diff --git a/seed/rust-sdk/inferred-auth-implicit-reference/Cargo.toml b/seed/rust-sdk/inferred-auth-implicit-reference/Cargo.toml new file mode 100644 index 000000000000..bc8ec351f59e --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-reference/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "seed_inferred_auth_implicit" +version = "0.0.1" +edition = "2021" +description = "Rust SDK for seed_inferred_auth_implicit generated by Fern" +license = "MIT" +repository = "https://github.com/fern-api/fern" +documentation = "https://docs.rs/seed_inferred_auth_implicit" + +[lib] +doctest = false + +[dependencies] +bytes = "1.0" +chrono = { version = "0.4", features = ["serde"] } +futures = "0.3" +num-bigint = { version = "0.4", features = ["serde"] } +ordered-float = { version = "4.5", features = ["serde"] } +percent-encoding = "2.3" +reqwest = { version = "0.12", features = ["json", "stream"], default-features = false } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +thiserror = "1.0" +tokio = { version = "1.0", features = ["full"] } +uuid = { version = "1.0", features = ["serde"] } + +[dev-dependencies] +tokio-test = "0.4" + +[features] +multipart = [] +sse = [] diff --git a/seed/rust-sdk/inferred-auth-implicit-reference/README.md b/seed/rust-sdk/inferred-auth-implicit-reference/README.md new file mode 100644 index 000000000000..6028d4697a36 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-reference/README.md @@ -0,0 +1,157 @@ +# Seed Rust Library + +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FRust) +[![crates.io shield](https://img.shields.io/crates/v/seed_inferred_auth_implicit)](https://crates.io/crates/seed_inferred_auth_implicit) + +The Seed Rust library provides convenient access to the Seed APIs from Rust. + +## Table of Contents + +- [Installation](#installation) +- [Reference](#reference) +- [Usage](#usage) +- [Errors](#errors) +- [Advanced](#advanced) + - [Retries](#retries) + - [Timeouts](#timeouts) + - [Additional Headers](#additional-headers) + - [Additional Query String Parameters](#additional-query-string-parameters) +- [Contributing](#contributing) + +## Installation + +Add this to your `Cargo.toml`: + +```toml +[dependencies] +seed_inferred_auth_implicit = "0.0.1" +``` + +Or install via cargo: + +```sh +cargo add seed_inferred_auth_implicit +``` + +## Reference + +A full reference for this library is available [here](./reference.md). + +## Usage + +Instantiate and use the client with the following: + +```rust +use seed_inferred_auth_implicit::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + ..Default::default() + }; + let client = InferredAuthImplicitClient::new(config).expect("Failed to build client"); + client + .auth + .get_token_with_client_credentials( + &GetTokenRequest { + client_id: "client_id".to_string(), + client_secret: "client_secret".to_string(), + audience: "https://api.example.com".to_string(), + grant_type: "client_credentials".to_string(), + scope: Some("scope".to_string()), + }, + None, + ) + .await; +} +``` + +## Errors + +When the API returns a non-success status code (4xx or 5xx response), an error will be returned. + +```rust +match client.auth.get_token_with_client_credentials(None)?.await { + Ok(response) => { + println!("Success: {:?}", response); + }, + Err(ApiError::HTTP { status, message }) => { + println!("API Error {}: {:?}", status, message); + }, + Err(e) => { + println!("Other error: {:?}", e); + } +} +``` + +## Advanced + +### Retries + +The SDK is instrumented with automatic retries with exponential backoff. A request will be retried as long +as the request is deemed retryable and the number of retry attempts has not grown larger than the configured +retry limit (default: 2). + +A request is deemed retryable when any of the following HTTP status codes is returned: + +- [408](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/408) (Timeout) +- [429](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429) (Too Many Requests) +- [5XX](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500) (Internal Server Errors) + +Use the `max_retries` method to configure this behavior. + +```rust +let response = client.auth.get_token_with_client_credentials( + Some(RequestOptions::new().max_retries(3)) +)?.await; +``` + +### Timeouts + +The SDK defaults to a 30 second timeout. Use the `timeout` method to configure this behavior. + +```rust +let response = client.auth.get_token_with_client_credentials( + Some(RequestOptions::new().timeout_seconds(30)) +)?.await; +``` + +### Additional Headers + +You can add custom headers to requests using `RequestOptions`. + +```rust +let response = client.auth.get_token_with_client_credentials( + Some( + RequestOptions::new() + .additional_header("X-Custom-Header", "custom-value") + .additional_header("X-Another-Header", "another-value") + ) +)? +.await; +``` + +### Additional Query String Parameters + +You can add custom query parameters to requests using `RequestOptions`. + +```rust +let response = client.auth.get_token_with_client_credentials( + Some( + RequestOptions::new() + .additional_query_param("filter", "active") + .additional_query_param("sort", "desc") + ) +)? +.await; +``` + +## Contributing + +While we value open-source contributions to this SDK, this library is generated programmatically. +Additions made directly to this library would have to be moved over to our generation code, +otherwise they would be overwritten upon the next generated release. Feel free to open a PR as +a proof of concept, but know that we will not be able to merge it as-is. We suggest opening +an issue first to discuss with us! + +On the other hand, contributions to the README are always very welcome! \ No newline at end of file diff --git a/seed/rust-sdk/inferred-auth-implicit-reference/dynamic-snippets/example0.rs b/seed/rust-sdk/inferred-auth-implicit-reference/dynamic-snippets/example0.rs new file mode 100644 index 000000000000..f5e4d9b0672b --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-reference/dynamic-snippets/example0.rs @@ -0,0 +1,23 @@ +use seed_inferred_auth_implicit::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + base_url: "https://api.fern.com".to_string(), + ..Default::default() + }; + let client = InferredAuthImplicitClient::new(config).expect("Failed to build client"); + client + .auth + .get_token_with_client_credentials( + &GetTokenRequest { + client_id: "client_id".to_string(), + client_secret: "client_secret".to_string(), + audience: "https://api.example.com".to_string(), + grant_type: "client_credentials".to_string(), + scope: Some("scope".to_string()), + }, + None, + ) + .await; +} diff --git a/seed/rust-sdk/inferred-auth-implicit-reference/dynamic-snippets/example1.rs b/seed/rust-sdk/inferred-auth-implicit-reference/dynamic-snippets/example1.rs new file mode 100644 index 000000000000..8030792c7e69 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-reference/dynamic-snippets/example1.rs @@ -0,0 +1,24 @@ +use seed_inferred_auth_implicit::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + base_url: "https://api.fern.com".to_string(), + ..Default::default() + }; + let client = InferredAuthImplicitClient::new(config).expect("Failed to build client"); + client + .auth + .refresh_token( + &RefreshTokenRequest { + client_id: "client_id".to_string(), + client_secret: "client_secret".to_string(), + refresh_token: "refresh_token".to_string(), + audience: "https://api.example.com".to_string(), + grant_type: "refresh_token".to_string(), + scope: Some("scope".to_string()), + }, + None, + ) + .await; +} diff --git a/seed/rust-sdk/inferred-auth-implicit-reference/dynamic-snippets/example2.rs b/seed/rust-sdk/inferred-auth-implicit-reference/dynamic-snippets/example2.rs new file mode 100644 index 000000000000..304bcb2180e5 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-reference/dynamic-snippets/example2.rs @@ -0,0 +1,11 @@ +use seed_inferred_auth_implicit::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + base_url: "https://api.fern.com".to_string(), + ..Default::default() + }; + let client = InferredAuthImplicitClient::new(config).expect("Failed to build client"); + client.nested_no_auth.api.get_something(None).await; +} diff --git a/seed/rust-sdk/inferred-auth-implicit-reference/dynamic-snippets/example3.rs b/seed/rust-sdk/inferred-auth-implicit-reference/dynamic-snippets/example3.rs new file mode 100644 index 000000000000..be4a6958f42d --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-reference/dynamic-snippets/example3.rs @@ -0,0 +1,11 @@ +use seed_inferred_auth_implicit::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + base_url: "https://api.fern.com".to_string(), + ..Default::default() + }; + let client = InferredAuthImplicitClient::new(config).expect("Failed to build client"); + client.nested.api.get_something(None).await; +} diff --git a/seed/rust-sdk/inferred-auth-implicit-reference/dynamic-snippets/example4.rs b/seed/rust-sdk/inferred-auth-implicit-reference/dynamic-snippets/example4.rs new file mode 100644 index 000000000000..14f0798114c1 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-reference/dynamic-snippets/example4.rs @@ -0,0 +1,11 @@ +use seed_inferred_auth_implicit::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + base_url: "https://api.fern.com".to_string(), + ..Default::default() + }; + let client = InferredAuthImplicitClient::new(config).expect("Failed to build client"); + client.simple.get_something(None).await; +} diff --git a/seed/rust-sdk/inferred-auth-implicit-reference/reference.md b/seed/rust-sdk/inferred-auth-implicit-reference/reference.md new file mode 100644 index 000000000000..9339d6df187f --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-reference/reference.md @@ -0,0 +1,199 @@ +# Reference +## Auth +
client.auth.get_token_with_client_credentials(request: GetTokenRequest) -> Result +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```rust +use seed_inferred_auth_implicit::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + ..Default::default() + }; + let client = InferredAuthImplicitClient::new(config).expect("Failed to build client"); + client + .auth + .get_token_with_client_credentials( + &GetTokenRequest { + client_id: "client_id".to_string(), + client_secret: "client_secret".to_string(), + audience: "https://api.example.com".to_string(), + grant_type: "client_credentials".to_string(), + scope: Some("scope".to_string()), + }, + None, + ) + .await; +} +``` +
+
+
+
+ + +
+
+
+ +
client.auth.refresh_token(request: RefreshTokenRequest) -> Result +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```rust +use seed_inferred_auth_implicit::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + ..Default::default() + }; + let client = InferredAuthImplicitClient::new(config).expect("Failed to build client"); + client + .auth + .refresh_token( + &RefreshTokenRequest { + client_id: "client_id".to_string(), + client_secret: "client_secret".to_string(), + refresh_token: "refresh_token".to_string(), + audience: "https://api.example.com".to_string(), + grant_type: "refresh_token".to_string(), + scope: Some("scope".to_string()), + }, + None, + ) + .await; +} +``` +
+
+
+
+ + +
+
+
+ +## NestedNoAuth Api +
client.nested_no_auth().api.get_something() -> Result<(), ApiError> +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```rust +use seed_inferred_auth_implicit::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + ..Default::default() + }; + let client = InferredAuthImplicitClient::new(config).expect("Failed to build client"); + client.nested_no_auth.api.get_something(None).await; +} +``` +
+
+
+
+ + +
+
+
+ +## Nested Api +
client.nested().api.get_something() -> Result<(), ApiError> +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```rust +use seed_inferred_auth_implicit::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + ..Default::default() + }; + let client = InferredAuthImplicitClient::new(config).expect("Failed to build client"); + client.nested.api.get_something(None).await; +} +``` +
+
+
+
+ + +
+
+
+ +## Simple +
client.simple.get_something() -> Result<(), ApiError> +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```rust +use seed_inferred_auth_implicit::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + ..Default::default() + }; + let client = InferredAuthImplicitClient::new(config).expect("Failed to build client"); + client.simple.get_something(None).await; +} +``` +
+
+
+
+ + +
+
+
diff --git a/seed/rust-sdk/inferred-auth-implicit-reference/rustfmt.toml b/seed/rust-sdk/inferred-auth-implicit-reference/rustfmt.toml new file mode 100644 index 000000000000..872221fb31fe --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-reference/rustfmt.toml @@ -0,0 +1,4 @@ +# Generated by Fern +edition = "2021" +max_width = 100 +use_small_heuristics = "Default" \ No newline at end of file diff --git a/seed/rust-sdk/inferred-auth-implicit-reference/snippet.json b/seed/rust-sdk/inferred-auth-implicit-reference/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/rust-sdk/inferred-auth-implicit-reference/src/api/mod.rs b/seed/rust-sdk/inferred-auth-implicit-reference/src/api/mod.rs new file mode 100644 index 000000000000..764c994d3429 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-reference/src/api/mod.rs @@ -0,0 +1,17 @@ +//! API client and types for the InferredAuthImplicit +//! +//! This module contains all the API definitions including request/response types +//! and client implementations for interacting with the API. +//! +//! ## Modules +//! +//! - [`resources`] - Service clients and endpoints +//! - [`types`] - Request, response, and model types + +pub mod resources; +pub mod types; + +pub use resources::{ + AuthClient, InferredAuthImplicitClient, NestedClient, NestedNoAuthClient, SimpleClient, +}; +pub use types::*; diff --git a/seed/rust-sdk/inferred-auth-implicit-reference/src/api/resources/auth/auth.rs b/seed/rust-sdk/inferred-auth-implicit-reference/src/api/resources/auth/auth.rs new file mode 100644 index 000000000000..60f2bd85379f --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-reference/src/api/resources/auth/auth.rs @@ -0,0 +1,47 @@ +use crate::api::*; +use crate::{ApiError, ClientConfig, HttpClient, RequestOptions}; +use reqwest::Method; + +pub struct AuthClient { + pub http_client: HttpClient, +} + +impl AuthClient { + pub fn new(config: ClientConfig) -> Result { + Ok(Self { + http_client: HttpClient::new(config.clone())?, + }) + } + + pub async fn get_token_with_client_credentials( + &self, + request: &GetTokenRequest, + options: Option, + ) -> Result { + self.http_client + .execute_request( + Method::POST, + "/token", + Some(serde_json::to_value(request).unwrap_or_default()), + None, + options, + ) + .await + } + + pub async fn refresh_token( + &self, + request: &RefreshTokenRequest, + options: Option, + ) -> Result { + self.http_client + .execute_request( + Method::POST, + "/token/refresh", + Some(serde_json::to_value(request).unwrap_or_default()), + None, + options, + ) + .await + } +} diff --git a/seed/rust-sdk/inferred-auth-implicit-reference/src/api/resources/auth/mod.rs b/seed/rust-sdk/inferred-auth-implicit-reference/src/api/resources/auth/mod.rs new file mode 100644 index 000000000000..c5d7cba0e123 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-reference/src/api/resources/auth/mod.rs @@ -0,0 +1,2 @@ +pub mod auth; +pub use auth::AuthClient; diff --git a/seed/rust-sdk/inferred-auth-implicit-reference/src/api/resources/mod.rs b/seed/rust-sdk/inferred-auth-implicit-reference/src/api/resources/mod.rs new file mode 100644 index 000000000000..27c069c85bec --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-reference/src/api/resources/mod.rs @@ -0,0 +1,39 @@ +//! Service clients and API endpoints +//! +//! This module contains client implementations for: +//! +//! - **Auth** +//! - **NestedNoAuth** +//! - **Nested** +//! - **Simple** + +use crate::{ApiError, ClientConfig}; + +pub mod auth; +pub mod nested; +pub mod nested_no_auth; +pub mod simple; +pub struct InferredAuthImplicitClient { + pub config: ClientConfig, + pub auth: AuthClient, + pub nested_no_auth: NestedNoAuthClient, + pub nested: NestedClient, + pub simple: SimpleClient, +} + +impl InferredAuthImplicitClient { + pub fn new(config: ClientConfig) -> Result { + Ok(Self { + config: config.clone(), + auth: AuthClient::new(config.clone())?, + nested_no_auth: NestedNoAuthClient::new(config.clone())?, + nested: NestedClient::new(config.clone())?, + simple: SimpleClient::new(config.clone())?, + }) + } +} + +pub use auth::AuthClient; +pub use nested::NestedClient; +pub use nested_no_auth::NestedNoAuthClient; +pub use simple::SimpleClient; diff --git a/seed/rust-sdk/inferred-auth-implicit-reference/src/api/resources/nested/api/mod.rs b/seed/rust-sdk/inferred-auth-implicit-reference/src/api/resources/nested/api/mod.rs new file mode 100644 index 000000000000..60b6b867df7b --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-reference/src/api/resources/nested/api/mod.rs @@ -0,0 +1,2 @@ +pub mod nested_api; +pub use nested_api::ApiClient2; diff --git a/seed/rust-sdk/inferred-auth-implicit-reference/src/api/resources/nested/api/nested_api.rs b/seed/rust-sdk/inferred-auth-implicit-reference/src/api/resources/nested/api/nested_api.rs new file mode 100644 index 000000000000..5a59312331ac --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-reference/src/api/resources/nested/api/nested_api.rs @@ -0,0 +1,20 @@ +use crate::{ApiError, ClientConfig, HttpClient, RequestOptions}; +use reqwest::Method; + +pub struct ApiClient2 { + pub http_client: HttpClient, +} + +impl ApiClient2 { + pub fn new(config: ClientConfig) -> Result { + Ok(Self { + http_client: HttpClient::new(config.clone())?, + }) + } + + pub async fn get_something(&self, options: Option) -> Result<(), ApiError> { + self.http_client + .execute_request(Method::GET, "/nested/get-something", None, None, options) + .await + } +} diff --git a/seed/rust-sdk/inferred-auth-implicit-reference/src/api/resources/nested/mod.rs b/seed/rust-sdk/inferred-auth-implicit-reference/src/api/resources/nested/mod.rs new file mode 100644 index 000000000000..0ae5397c7f44 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-reference/src/api/resources/nested/mod.rs @@ -0,0 +1,16 @@ +use crate::{ApiError, ClientConfig, HttpClient}; + +pub mod api; +pub use api::ApiClient2; +pub struct NestedClient { + pub http_client: HttpClient, + pub api: ApiClient2, +} +impl NestedClient { + pub fn new(config: ClientConfig) -> Result { + Ok(Self { + http_client: HttpClient::new(config.clone())?, + api: ApiClient2::new(config.clone())?, + }) + } +} diff --git a/seed/rust-sdk/inferred-auth-implicit-reference/src/api/resources/nested_no_auth/api/mod.rs b/seed/rust-sdk/inferred-auth-implicit-reference/src/api/resources/nested_no_auth/api/mod.rs new file mode 100644 index 000000000000..ac9a34097bf9 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-reference/src/api/resources/nested_no_auth/api/mod.rs @@ -0,0 +1,2 @@ +pub mod nested_no_auth_api; +pub use nested_no_auth_api::ApiClient; diff --git a/seed/rust-sdk/inferred-auth-implicit-reference/src/api/resources/nested_no_auth/api/nested_no_auth_api.rs b/seed/rust-sdk/inferred-auth-implicit-reference/src/api/resources/nested_no_auth/api/nested_no_auth_api.rs new file mode 100644 index 000000000000..f9ac674562d7 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-reference/src/api/resources/nested_no_auth/api/nested_no_auth_api.rs @@ -0,0 +1,26 @@ +use crate::{ApiError, ClientConfig, HttpClient, RequestOptions}; +use reqwest::Method; + +pub struct ApiClient { + pub http_client: HttpClient, +} + +impl ApiClient { + pub fn new(config: ClientConfig) -> Result { + Ok(Self { + http_client: HttpClient::new(config.clone())?, + }) + } + + pub async fn get_something(&self, options: Option) -> Result<(), ApiError> { + self.http_client + .execute_request( + Method::GET, + "/nested-no-auth/get-something", + None, + None, + options, + ) + .await + } +} diff --git a/seed/rust-sdk/inferred-auth-implicit-reference/src/api/resources/nested_no_auth/mod.rs b/seed/rust-sdk/inferred-auth-implicit-reference/src/api/resources/nested_no_auth/mod.rs new file mode 100644 index 000000000000..73f91ddc946a --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-reference/src/api/resources/nested_no_auth/mod.rs @@ -0,0 +1,16 @@ +use crate::{ApiError, ClientConfig, HttpClient}; + +pub mod api; +pub use api::ApiClient; +pub struct NestedNoAuthClient { + pub http_client: HttpClient, + pub api: ApiClient, +} +impl NestedNoAuthClient { + pub fn new(config: ClientConfig) -> Result { + Ok(Self { + http_client: HttpClient::new(config.clone())?, + api: ApiClient::new(config.clone())?, + }) + } +} diff --git a/seed/rust-sdk/inferred-auth-implicit-reference/src/api/resources/simple/mod.rs b/seed/rust-sdk/inferred-auth-implicit-reference/src/api/resources/simple/mod.rs new file mode 100644 index 000000000000..26f8c47f517d --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-reference/src/api/resources/simple/mod.rs @@ -0,0 +1,2 @@ +pub mod simple; +pub use simple::SimpleClient; diff --git a/seed/rust-sdk/inferred-auth-implicit-reference/src/api/resources/simple/simple.rs b/seed/rust-sdk/inferred-auth-implicit-reference/src/api/resources/simple/simple.rs new file mode 100644 index 000000000000..5ec45ae2b26c --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-reference/src/api/resources/simple/simple.rs @@ -0,0 +1,20 @@ +use crate::{ApiError, ClientConfig, HttpClient, RequestOptions}; +use reqwest::Method; + +pub struct SimpleClient { + pub http_client: HttpClient, +} + +impl SimpleClient { + pub fn new(config: ClientConfig) -> Result { + Ok(Self { + http_client: HttpClient::new(config.clone())?, + }) + } + + pub async fn get_something(&self, options: Option) -> Result<(), ApiError> { + self.http_client + .execute_request(Method::GET, "/get-something", None, None, options) + .await + } +} diff --git a/seed/rust-sdk/inferred-auth-implicit-reference/src/api/types/auth_get_token_request.rs b/seed/rust-sdk/inferred-auth-implicit-reference/src/api/types/auth_get_token_request.rs new file mode 100644 index 000000000000..d50f7adae64c --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-reference/src/api/types/auth_get_token_request.rs @@ -0,0 +1,12 @@ +pub use crate::prelude::*; + +/// A request to obtain an OAuth token. +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub struct GetTokenRequest { + pub client_id: String, + pub client_secret: String, + pub audience: String, + pub grant_type: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub scope: Option, +} diff --git a/seed/rust-sdk/inferred-auth-implicit-reference/src/api/types/auth_refresh_token_request.rs b/seed/rust-sdk/inferred-auth-implicit-reference/src/api/types/auth_refresh_token_request.rs new file mode 100644 index 000000000000..b12d9476b367 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-reference/src/api/types/auth_refresh_token_request.rs @@ -0,0 +1,13 @@ +pub use crate::prelude::*; + +/// A request to refresh an OAuth token. +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub struct RefreshTokenRequest { + pub client_id: String, + pub client_secret: String, + pub refresh_token: String, + pub audience: String, + pub grant_type: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub scope: Option, +} diff --git a/seed/rust-sdk/inferred-auth-implicit-reference/src/api/types/auth_token_response.rs b/seed/rust-sdk/inferred-auth-implicit-reference/src/api/types/auth_token_response.rs new file mode 100644 index 000000000000..834881adddb4 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-reference/src/api/types/auth_token_response.rs @@ -0,0 +1,10 @@ +pub use crate::prelude::*; + +/// An OAuth token response. +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub struct TokenResponse { + pub access_token: String, + pub expires_in: i64, + #[serde(skip_serializing_if = "Option::is_none")] + pub refresh_token: Option, +} diff --git a/seed/rust-sdk/inferred-auth-implicit-reference/src/api/types/mod.rs b/seed/rust-sdk/inferred-auth-implicit-reference/src/api/types/mod.rs new file mode 100644 index 000000000000..5bbbc5889ac5 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-reference/src/api/types/mod.rs @@ -0,0 +1,7 @@ +pub mod auth_get_token_request; +pub mod auth_refresh_token_request; +pub mod auth_token_response; + +pub use auth_get_token_request::GetTokenRequest; +pub use auth_refresh_token_request::RefreshTokenRequest; +pub use auth_token_response::TokenResponse; diff --git a/seed/rust-sdk/inferred-auth-implicit-reference/src/client.rs b/seed/rust-sdk/inferred-auth-implicit-reference/src/client.rs new file mode 100644 index 000000000000..fa4b8e806d88 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-reference/src/client.rs @@ -0,0 +1,92 @@ +use crate::api::resources::InferredAuthImplicitClient; +use crate::{ApiError, ClientConfig}; +use std::collections::HashMap; +use std::time::Duration; + +/* +Things to know: + +`impl Into` is a generic parameter constraint in Rust that means "accept any type that can be converted into a String." +It's essentially Rust's way of saying "I'll take a string in any form you give it to me." + +Types that implement Into: + +// All of these work: +builder.api_key("hello") // &str +builder.api_key("hello".to_string()) // String +builder.api_key(format!("key_{}", id)) // String from format! +builder.api_key(my_string_variable) // String variable +*/ + +/// Builder for creating API clients with custom configuration +pub struct ApiClientBuilder { + config: ClientConfig, +} + +impl ApiClientBuilder { + /// Create a new builder with the specified base URL + pub fn new(base_url: impl Into) -> Self { + let mut config = ClientConfig::default(); + config.base_url = base_url.into(); + Self { config } + } + + /// Set the API key for authentication + pub fn api_key(mut self, key: impl Into) -> Self { + self.config.api_key = Some(key.into()); + self + } + + /// Set the bearer token for authentication + pub fn token(mut self, token: impl Into) -> Self { + self.config.token = Some(token.into()); + self + } + + /// Set the username for basic authentication + pub fn username(mut self, username: impl Into) -> Self { + self.config.username = Some(username.into()); + self + } + + /// Set the password for basic authentication + pub fn password(mut self, password: impl Into) -> Self { + self.config.password = Some(password.into()); + self + } + + /// Set the request timeout + pub fn timeout(mut self, timeout: Duration) -> Self { + self.config.timeout = timeout; + self + } + + /// Set the maximum number of retries + pub fn max_retries(mut self, retries: u32) -> Self { + self.config.max_retries = retries; + self + } + + /// Add a custom header + pub fn custom_header(mut self, key: impl Into, value: impl Into) -> Self { + self.config.custom_headers.insert(key.into(), value.into()); + self + } + + /// Add multiple custom headers + pub fn custom_headers(mut self, headers: HashMap) -> Self { + self.config.custom_headers.extend(headers); + self + } + + /// Set the user agent + pub fn user_agent(mut self, user_agent: impl Into) -> Self { + self.config.user_agent = user_agent.into(); + self + } + + /// Build the client with validation + pub fn build(self) -> Result { + InferredAuthImplicitClient::new(self.config) + } +} diff --git a/seed/rust-sdk/inferred-auth-implicit-reference/src/config.rs b/seed/rust-sdk/inferred-auth-implicit-reference/src/config.rs new file mode 100644 index 000000000000..74be0ea0af98 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-reference/src/config.rs @@ -0,0 +1,30 @@ +use std::collections::HashMap; +use std::time::Duration; + +#[derive(Debug, Clone)] +pub struct ClientConfig { + pub base_url: String, + pub api_key: Option, + pub token: Option, + pub username: Option, + pub password: Option, + pub timeout: Duration, + pub max_retries: u32, + pub custom_headers: HashMap, + pub user_agent: String, +} +impl Default for ClientConfig { + fn default() -> Self { + Self { + base_url: String::new(), + api_key: None, + token: None, + username: None, + password: None, + timeout: Duration::from_secs(60), + max_retries: 3, + custom_headers: HashMap::new(), + user_agent: "InferredAuthImplicit Rust SDK".to_string(), + } + } +} diff --git a/seed/rust-sdk/inferred-auth-implicit-reference/src/core/http_client.rs b/seed/rust-sdk/inferred-auth-implicit-reference/src/core/http_client.rs new file mode 100644 index 000000000000..e1171a4537f3 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-reference/src/core/http_client.rs @@ -0,0 +1,512 @@ +use crate::{join_url, ApiError, ClientConfig, RequestOptions}; +use futures::{Stream, StreamExt}; +use reqwest::{ + header::{HeaderName, HeaderValue}, + Client, Method, Request, Response, +}; +use serde::de::DeserializeOwned; +use std::{ + pin::Pin, + str::FromStr, + task::{Context, Poll}, +}; + +/// A streaming byte stream for downloading files efficiently +pub struct ByteStream { + content_length: Option, + inner: Pin> + Send>>, +} + +impl ByteStream { + /// Create a new ByteStream from a Response + pub(crate) fn new(response: Response) -> Self { + let content_length = response.content_length(); + let stream = response.bytes_stream(); + + Self { + content_length, + inner: Box::pin(stream), + } + } + + /// Collect the entire stream into a `Vec` + /// + /// This consumes the stream and buffers all data into memory. + /// For large files, prefer using `try_next()` to process chunks incrementally. + /// + /// # Example + /// ```no_run + /// let stream = client.download_file().await?; + /// let bytes = stream.collect().await?; + /// ``` + pub async fn collect(mut self) -> Result, ApiError> { + let mut result = Vec::new(); + while let Some(chunk) = self.inner.next().await { + result.extend_from_slice(&chunk.map_err(ApiError::Network)?); + } + Ok(result) + } + + /// Get the next chunk from the stream + /// + /// Returns `Ok(Some(bytes))` if a chunk is available, + /// `Ok(None)` if the stream is finished, or an error. + /// + /// # Example + /// ```no_run + /// let mut stream = client.download_file().await?; + /// while let Some(chunk) = stream.try_next().await? { + /// process_chunk(&chunk); + /// } + /// ``` + pub async fn try_next(&mut self) -> Result, ApiError> { + match self.inner.next().await { + Some(Ok(bytes)) => Ok(Some(bytes)), + Some(Err(e)) => Err(ApiError::Network(e)), + None => Ok(None), + } + } + + /// Get the content length from response headers if available + pub fn content_length(&self) -> Option { + self.content_length + } +} + +impl Stream for ByteStream { + type Item = Result; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + match self.inner.as_mut().poll_next(cx) { + Poll::Ready(Some(Ok(bytes))) => Poll::Ready(Some(Ok(bytes))), + Poll::Ready(Some(Err(e))) => Poll::Ready(Some(Err(ApiError::Network(e)))), + Poll::Ready(None) => Poll::Ready(None), + Poll::Pending => Poll::Pending, + } + } +} + +/// Internal HTTP client that handles requests with authentication and retries +#[derive(Clone)] +pub struct HttpClient { + client: Client, + config: ClientConfig, +} + +impl HttpClient { + pub fn new(config: ClientConfig) -> Result { + let client = Client::builder() + .timeout(config.timeout) + .user_agent(&config.user_agent) + .build() + .map_err(ApiError::Network)?; + + Ok(Self { client, config }) + } + + /// Execute a request with the given method, path, and options + pub async fn execute_request( + &self, + method: Method, + path: &str, + body: Option, + query_params: Option>, + options: Option, + ) -> Result + where + T: DeserializeOwned, // Generic T: DeserializeOwned means the response will be automatically deserialized into whatever type you specify: + { + let url = join_url(&self.config.base_url, path); + let mut request = self.client.request(method, &url); + + // Apply query parameters if provided + if let Some(params) = query_params { + request = request.query(¶ms); + } + + // Apply additional query parameters from options + if let Some(opts) = &options { + if !opts.additional_query_params.is_empty() { + request = request.query(&opts.additional_query_params); + } + } + + // Apply body if provided + if let Some(body) = body { + request = request.json(&body); + } + + // Build the request + let mut req = request.build().map_err(|e| ApiError::Network(e))?; + + // Apply authentication and headers + self.apply_auth_headers(&mut req, &options)?; + self.apply_custom_headers(&mut req, &options)?; + + // Execute with retries + let response = self.execute_with_retries(req, &options).await?; + self.parse_response(response).await + } + + /// Execute a multipart/form-data request with the given method, path, and options + /// + /// This method is used for file uploads using reqwest's built-in multipart support. + /// Note: Multipart requests are not retried because they cannot be cloned. + /// + /// # Example + /// ```no_run + /// let form = reqwest::multipart::Form::new() + /// .part("file", reqwest::multipart::Part::bytes(vec![1, 2, 3])); + /// + /// let response: MyResponse = client.execute_multipart_request( + /// Method::POST, + /// "/upload", + /// form, + /// None, + /// None, + /// ).await?; + /// ``` + #[cfg(feature = "multipart")] + pub async fn execute_multipart_request( + &self, + method: Method, + path: &str, + form: reqwest::multipart::Form, + query_params: Option>, + options: Option, + ) -> Result + where + T: DeserializeOwned, + { + let url = join_url(&self.config.base_url, path); + let mut request = self.client.request(method, &url); + + // Apply query parameters if provided + if let Some(params) = query_params { + request = request.query(¶ms); + } + + // Apply additional query parameters from options + if let Some(opts) = &options { + if !opts.additional_query_params.is_empty() { + request = request.query(&opts.additional_query_params); + } + } + + // Use reqwest's built-in multipart support + request = request.multipart(form); + + // Build the request + let mut req = request.build().map_err(|e| ApiError::Network(e))?; + + // Apply authentication and headers + self.apply_auth_headers(&mut req, &options)?; + self.apply_custom_headers(&mut req, &options)?; + + // Execute directly without retries (multipart requests cannot be cloned) + let response = self.client.execute(req).await.map_err(ApiError::Network)?; + + // Check response status + if !response.status().is_success() { + let status_code = response.status().as_u16(); + let body = response.text().await.ok(); + return Err(ApiError::from_response(status_code, body.as_deref())); + } + + self.parse_response(response).await + } + + fn apply_auth_headers( + &self, + request: &mut Request, + options: &Option, + ) -> Result<(), ApiError> { + let headers = request.headers_mut(); + + // Apply API key (request options override config) + let api_key = options + .as_ref() + .and_then(|opts| opts.api_key.as_ref()) + .or(self.config.api_key.as_ref()); + + if let Some(key) = api_key { + headers.insert("api_key", key.parse().map_err(|_| ApiError::InvalidHeader)?); + } + + // Apply bearer token (request options override config) + let token = options + .as_ref() + .and_then(|opts| opts.token.as_ref()) + .or(self.config.token.as_ref()); + + if let Some(token) = token { + let auth_value = format!("Bearer {}", token); + headers.insert( + "Authorization", + auth_value.parse().map_err(|_| ApiError::InvalidHeader)?, + ); + } + + Ok(()) + } + + fn apply_custom_headers( + &self, + request: &mut Request, + options: &Option, + ) -> Result<(), ApiError> { + let headers = request.headers_mut(); + + // Apply config-level custom headers + for (key, value) in &self.config.custom_headers { + headers.insert( + HeaderName::from_str(key).map_err(|_| ApiError::InvalidHeader)?, + HeaderValue::from_str(value).map_err(|_| ApiError::InvalidHeader)?, + ); + } + + // Apply request-level custom headers (override config) + if let Some(options) = options { + for (key, value) in &options.additional_headers { + headers.insert( + HeaderName::from_str(key).map_err(|_| ApiError::InvalidHeader)?, + HeaderValue::from_str(value).map_err(|_| ApiError::InvalidHeader)?, + ); + } + } + + Ok(()) + } + + async fn execute_with_retries( + &self, + request: Request, + options: &Option, + ) -> Result { + let max_retries = options + .as_ref() + .and_then(|opts| opts.max_retries) + .unwrap_or(self.config.max_retries); + + let mut last_error = None; + + for attempt in 0..=max_retries { + let cloned_request = request.try_clone().ok_or(ApiError::RequestClone)?; + + match self.client.execute(cloned_request).await { + Ok(response) if response.status().is_success() => return Ok(response), + Ok(response) => { + let status_code = response.status().as_u16(); + let body = response.text().await.ok(); + return Err(ApiError::from_response(status_code, body.as_deref())); + } + Err(e) if attempt < max_retries => { + last_error = Some(e); + // Exponential backoff + let delay = std::time::Duration::from_millis(100 * 2_u64.pow(attempt)); + tokio::time::sleep(delay).await; + } + Err(e) => return Err(ApiError::Network(e)), + } + } + + Err(ApiError::Network(last_error.unwrap())) + } + + async fn parse_response(&self, response: Response) -> Result + where + T: DeserializeOwned, + { + let text = response.text().await.map_err(ApiError::Network)?; + serde_json::from_str(&text).map_err(ApiError::Serialization) + } + + /// Execute a request and return a streaming response (for large file downloads) + /// + /// This method returns a `ByteStream` that can be used to download large files + /// efficiently without loading the entire content into memory. The stream can be + /// consumed chunk by chunk, written directly to disk, or collected into bytes. + /// + /// # Examples + /// + /// **Option 1: Collect all bytes into memory** + /// ```no_run + /// let stream = client.execute_stream_request( + /// Method::GET, + /// "/file", + /// None, + /// None, + /// None, + /// ).await?; + /// + /// let bytes = stream.collect().await?; + /// ``` + /// + /// **Option 2: Process chunks with try_next()** + /// ```no_run + /// let mut stream = client.execute_stream_request( + /// Method::GET, + /// "/large-file", + /// None, + /// None, + /// None, + /// ).await?; + /// + /// while let Some(chunk) = stream.try_next().await? { + /// process_chunk(&chunk); + /// } + /// ``` + /// + /// **Option 3: Stream with futures::Stream trait** + /// ```no_run + /// use futures::StreamExt; + /// + /// let stream = client.execute_stream_request( + /// Method::GET, + /// "/large-file", + /// None, + /// None, + /// None, + /// ).await?; + /// + /// let mut file = tokio::fs::File::create("output.mp4").await?; + /// let mut stream = std::pin::pin!(stream); + /// while let Some(chunk) = stream.next().await { + /// let chunk = chunk?; + /// tokio::io::AsyncWriteExt::write_all(&mut file, &chunk).await?; + /// } + /// ``` + pub async fn execute_stream_request( + &self, + method: Method, + path: &str, + body: Option, + query_params: Option>, + options: Option, + ) -> Result { + let url = join_url(&self.config.base_url, path); + let mut request = self.client.request(method, &url); + + // Apply query parameters if provided + if let Some(params) = query_params { + request = request.query(¶ms); + } + + // Apply additional query parameters from options + if let Some(opts) = &options { + if !opts.additional_query_params.is_empty() { + request = request.query(&opts.additional_query_params); + } + } + + // Apply body if provided + if let Some(body) = body { + request = request.json(&body); + } + + // Build the request + let mut req = request.build().map_err(|e| ApiError::Network(e))?; + + // Apply authentication and headers + self.apply_auth_headers(&mut req, &options)?; + self.apply_custom_headers(&mut req, &options)?; + + // Execute with retries + let response = self.execute_with_retries(req, &options).await?; + + // Return streaming response + Ok(ByteStream::new(response)) + } + + /// Execute a request and return an SSE stream + /// + /// This method returns an `SseStream` that automatically parses + /// Server-Sent Events and deserializes the JSON data in each event. + /// + /// # SSE-Specific Headers + /// + /// This method automatically sets the following headers **after** applying custom headers, + /// which means these headers will override any user-supplied values: + /// - `Accept: text/event-stream` - Required for SSE protocol + /// - `Cache-Control: no-store` - Prevents caching of streaming responses + /// + /// This ensures proper SSE behavior even if custom headers are provided. + /// + /// # Example + /// ```no_run + /// use futures::StreamExt; + /// + /// let stream = client.execute_sse_request::( + /// Method::POST, + /// "/stream", + /// Some(serde_json::json!({"query": "Hello"})), + /// None, + /// None, + /// Some("[[DONE]]".to_string()), + /// ).await?; + /// + /// let mut stream = std::pin::pin!(stream); + /// while let Some(chunk) = stream.next().await { + /// let chunk = chunk?; + /// println!("Received: {:?}", chunk); + /// } + /// ``` + #[cfg(feature = "sse")] + pub async fn execute_sse_request( + &self, + method: Method, + path: &str, + body: Option, + query_params: Option>, + options: Option, + terminator: Option, + ) -> Result, ApiError> + where + T: DeserializeOwned + Send + 'static, + { + let url = join_url(&self.config.base_url, path); + let mut request = self.client.request(method, &url); + + // Apply query parameters if provided + if let Some(params) = query_params { + request = request.query(¶ms); + } + + // Apply additional query parameters from options + if let Some(opts) = &options { + if !opts.additional_query_params.is_empty() { + request = request.query(&opts.additional_query_params); + } + } + + // Apply body if provided + if let Some(body) = body { + request = request.json(&body); + } + + // Build the request + let mut req = request.build().map_err(|e| ApiError::Network(e))?; + + // Apply authentication and headers + self.apply_auth_headers(&mut req, &options)?; + self.apply_custom_headers(&mut req, &options)?; + + // SSE-specific headers + req.headers_mut().insert( + "Accept", + "text/event-stream" + .parse() + .map_err(|_| ApiError::InvalidHeader)?, + ); + req.headers_mut().insert( + "Cache-Control", + "no-store".parse().map_err(|_| ApiError::InvalidHeader)?, + ); + + // Execute with retries + let response = self.execute_with_retries(req, &options).await?; + + // Return SSE stream + crate::SseStream::new(response, terminator).await + } +} diff --git a/seed/rust-sdk/inferred-auth-implicit-reference/src/core/mod.rs b/seed/rust-sdk/inferred-auth-implicit-reference/src/core/mod.rs new file mode 100644 index 000000000000..ba23f97e4acd --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-reference/src/core/mod.rs @@ -0,0 +1,15 @@ +//! Core client infrastructure + +mod http_client; +mod query_parameter_builder; +mod request_options; +#[cfg(feature = "sse")] +mod sse_stream; +mod utils; + +pub use http_client::{ByteStream, HttpClient}; +pub use query_parameter_builder::{parse_structured_query, QueryBuilder, QueryBuilderError}; +pub use request_options::RequestOptions; +#[cfg(feature = "sse")] +pub use sse_stream::SseStream; +pub use utils::join_url; diff --git a/seed/rust-sdk/inferred-auth-implicit-reference/src/core/pagination.rs b/seed/rust-sdk/inferred-auth-implicit-reference/src/core/pagination.rs new file mode 100644 index 000000000000..2cd716dc82a3 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-reference/src/core/pagination.rs @@ -0,0 +1,282 @@ +use std::collections::VecDeque; +use std::future::Future; +use std::pin::Pin; +use std::sync::Arc; +use std::task::{Context, Poll}; + +use futures::Stream; +use serde_json::Value; + +use crate::{ApiError, HttpClient}; + +/// Result of a pagination request +#[derive(Debug)] +pub struct PaginationResult { + pub items: Vec, + pub next_cursor: Option, + pub has_next_page: bool, +} + +/// Async paginator that implements Stream for iterating over paginated results +pub struct AsyncPaginator { + http_client: Arc, + page_loader: Box< + dyn Fn( + Arc, + Option, + ) + -> Pin, ApiError>> + Send>> + + Send + + Sync, + >, + current_page: VecDeque, + current_cursor: Option, + has_next_page: bool, + loading_next: + Option, ApiError>> + Send>>>, +} + +impl AsyncPaginator { + pub fn new( + http_client: Arc, + page_loader: F, + initial_cursor: Option, + ) -> Result + where + F: Fn(Arc, Option) -> Fut + Send + Sync + 'static, + Fut: Future, ApiError>> + Send + 'static, + { + Ok(Self { + http_client, + page_loader: Box::new(move |client, cursor| Box::pin(page_loader(client, cursor))), + current_page: VecDeque::new(), + current_cursor: initial_cursor, + has_next_page: true, // Assume true initially, will be updated after first request + loading_next: None, + }) + } + + /// Check if there are more pages available + pub fn has_next_page(&self) -> bool { + !self.current_page.is_empty() || self.has_next_page + } + + /// Load the next page explicitly + pub async fn next_page(&mut self) -> Result, ApiError> { + if !self.has_next_page { + return Ok(Vec::new()); + } + + let result = + (self.page_loader)(self.http_client.clone(), self.current_cursor.clone()).await?; + + self.current_cursor = result.next_cursor; + self.has_next_page = result.has_next_page; + + Ok(result.items) + } +} + +impl Stream for AsyncPaginator +where + T: Unpin, +{ + type Item = Result; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + // If we have items in the current page, return the next one + if let Some(item) = self.current_page.pop_front() { + return Poll::Ready(Some(Ok(item))); + } + + // If we're already loading the next page, poll that future + if let Some(ref mut loading_future) = self.loading_next { + match loading_future.as_mut().poll(cx) { + Poll::Ready(Ok(result)) => { + self.current_page.extend(result.items); + self.current_cursor = result.next_cursor; + self.has_next_page = result.has_next_page; + self.loading_next = None; + + // Try to get the next item from the newly loaded page + if let Some(item) = self.current_page.pop_front() { + return Poll::Ready(Some(Ok(item))); + } else if !self.has_next_page { + return Poll::Ready(None); + } + // Fall through to start loading next page + } + Poll::Ready(Err(e)) => { + self.loading_next = None; + return Poll::Ready(Some(Err(e))); + } + Poll::Pending => return Poll::Pending, + } + } + + // If we have no more pages to load, we're done + if !self.has_next_page { + return Poll::Ready(None); + } + + // Start loading the next page + let future = (self.page_loader)(self.http_client.clone(), self.current_cursor.clone()); + self.loading_next = Some(future); + + // Poll the future immediately + if let Some(ref mut loading_future) = self.loading_next { + match loading_future.as_mut().poll(cx) { + Poll::Ready(Ok(result)) => { + self.current_page.extend(result.items); + self.current_cursor = result.next_cursor; + self.has_next_page = result.has_next_page; + self.loading_next = None; + + if let Some(item) = self.current_page.pop_front() { + Poll::Ready(Some(Ok(item))) + } else if !self.has_next_page { + Poll::Ready(None) + } else { + // This shouldn't happen, but just in case + cx.waker().wake_by_ref(); + Poll::Pending + } + } + Poll::Ready(Err(e)) => { + self.loading_next = None; + Poll::Ready(Some(Err(e))) + } + Poll::Pending => Poll::Pending, + } + } else { + Poll::Pending + } + } +} + +/// Synchronous paginator for blocking iteration +pub struct SyncPaginator { + http_client: Arc, + page_loader: Box< + dyn Fn(Arc, Option) -> Result, ApiError> + + Send + + Sync, + >, + current_page: VecDeque, + current_cursor: Option, + has_next_page: bool, +} + +impl SyncPaginator { + pub fn new( + http_client: Arc, + page_loader: F, + initial_cursor: Option, + ) -> Result + where + F: Fn(Arc, Option) -> Result, ApiError> + + Send + + Sync + + 'static, + { + Ok(Self { + http_client, + page_loader: Box::new(page_loader), + current_page: VecDeque::new(), + current_cursor: initial_cursor, + has_next_page: true, // Assume true initially + }) + } + + /// Check if there are more pages available + pub fn has_next_page(&self) -> bool { + !self.current_page.is_empty() || self.has_next_page + } + + /// Load the next page explicitly + pub fn next_page(&mut self) -> Result, ApiError> { + if !self.has_next_page { + return Ok(Vec::new()); + } + + let result = (self.page_loader)(self.http_client.clone(), self.current_cursor.clone())?; + + self.current_cursor = result.next_cursor; + self.has_next_page = result.has_next_page; + + Ok(result.items) + } + + /// Get all remaining items by loading all pages + pub fn collect_all(&mut self) -> Result, ApiError> { + let mut all_items = Vec::new(); + + // Add items from current page + while let Some(item) = self.current_page.pop_front() { + all_items.push(item); + } + + // Load all remaining pages + while self.has_next_page { + let page_items = self.next_page()?; + all_items.extend(page_items); + } + + Ok(all_items) + } +} + +impl Iterator for SyncPaginator { + type Item = Result; + + fn next(&mut self) -> Option { + // If we have items in the current page, return the next one + if let Some(item) = self.current_page.pop_front() { + return Some(Ok(item)); + } + + // If we have no more pages to load, we're done + if !self.has_next_page { + return None; + } + + // Load the next page + match (self.page_loader)(self.http_client.clone(), self.current_cursor.clone()) { + Ok(result) => { + self.current_page.extend(result.items); + self.current_cursor = result.next_cursor; + self.has_next_page = result.has_next_page; + + // Return the first item from the newly loaded page + self.current_page.pop_front().map(Ok) + } + Err(e) => Some(Err(e)), + } + } +} + +/// Trait for types that can provide pagination metadata +pub trait Paginated { + /// Extract the items from this page + fn items(&self) -> &[T]; + + /// Get the cursor for the next page, if any + fn next_cursor(&self) -> Option<&str>; + + /// Check if there's a next page available + fn has_next_page(&self) -> bool; +} + +/// Trait for types that can provide offset-based pagination metadata +pub trait OffsetPaginated { + /// Extract the items from this page + fn items(&self) -> &[T]; + + /// Check if there's a next page available + fn has_next_page(&self) -> bool; + + /// Get the current page size (for calculating next offset) + fn page_size(&self) -> usize { + self.items().len() + } +} diff --git a/seed/rust-sdk/inferred-auth-implicit-reference/src/core/query_parameter_builder.rs b/seed/rust-sdk/inferred-auth-implicit-reference/src/core/query_parameter_builder.rs new file mode 100644 index 000000000000..24e78f885611 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-reference/src/core/query_parameter_builder.rs @@ -0,0 +1,297 @@ +use chrono::{DateTime, Utc}; +use serde::Serialize; + +/// Modern query builder with type-safe method chaining +/// Provides a clean, Swift-like API for building HTTP query parameters +#[derive(Debug, Default)] +pub struct QueryBuilder { + params: Vec<(String, String)>, +} + +impl QueryBuilder { + /// Create a new query parameter builder + pub fn new() -> Self { + Self::default() + } + + /// Add a string parameter (accept both required/optional) + pub fn string(mut self, key: &str, value: impl Into>) -> Self { + if let Some(v) = value.into() { + self.params.push((key.to_string(), v)); + } + self + } + + /// Add multiple string parameters with the same key (for allow-multiple query params) + /// Accepts both Vec and Vec>, adding each non-None value as a separate query parameter + pub fn string_array(mut self, key: &str, values: I) -> Self + where + I: IntoIterator, + T: Into>, + { + for value in values { + if let Some(v) = value.into() { + self.params.push((key.to_string(), v)); + } + } + self + } + + /// Add an integer parameter (accept both required/optional) + pub fn int(mut self, key: &str, value: impl Into>) -> Self { + if let Some(v) = value.into() { + self.params.push((key.to_string(), v.to_string())); + } + self + } + + /// Add a big integer parameter (accept both required/optional) + pub fn big_int(mut self, key: &str, value: impl Into>) -> Self { + if let Some(v) = value.into() { + self.params.push((key.to_string(), v.to_string())); + } + self + } + + /// Add multiple integer parameters with the same key (for allow-multiple query params) + /// Accepts both Vec and Vec>, adding each non-None value as a separate query parameter + pub fn int_array(mut self, key: &str, values: I) -> Self + where + I: IntoIterator, + T: Into>, + { + for value in values { + if let Some(v) = value.into() { + self.params.push((key.to_string(), v.to_string())); + } + } + self + } + + /// Add a float parameter + pub fn float(mut self, key: &str, value: impl Into>) -> Self { + if let Some(v) = value.into() { + self.params.push((key.to_string(), v.to_string())); + } + self + } + + /// Add multiple float parameters with the same key (for allow-multiple query params) + /// Accepts both Vec and Vec>, adding each non-None value as a separate query parameter + pub fn float_array(mut self, key: &str, values: I) -> Self + where + I: IntoIterator, + T: Into>, + { + for value in values { + if let Some(v) = value.into() { + self.params.push((key.to_string(), v.to_string())); + } + } + self + } + + /// Add a boolean parameter + pub fn bool(mut self, key: &str, value: impl Into>) -> Self { + if let Some(v) = value.into() { + self.params.push((key.to_string(), v.to_string())); + } + self + } + + /// Add multiple boolean parameters with the same key (for allow-multiple query params) + /// Accepts both Vec and Vec>, adding each non-None value as a separate query parameter + pub fn bool_array(mut self, key: &str, values: I) -> Self + where + I: IntoIterator, + T: Into>, + { + for value in values { + if let Some(v) = value.into() { + self.params.push((key.to_string(), v.to_string())); + } + } + self + } + + /// Add a datetime parameter + pub fn datetime(mut self, key: &str, value: impl Into>>) -> Self { + if let Some(v) = value.into() { + self.params.push((key.to_string(), v.to_rfc3339())); + } + self + } + + /// Add a UUID parameter (converts to string) + pub fn uuid(mut self, key: &str, value: impl Into>) -> Self { + if let Some(v) = value.into() { + self.params.push((key.to_string(), v.to_string())); + } + self + } + + /// Add a date parameter (converts NaiveDate to DateTime) + pub fn date(mut self, key: &str, value: impl Into>) -> Self { + if let Some(v) = value.into() { + // Convert NaiveDate to DateTime at start of day + let datetime = v.and_hms_opt(0, 0, 0).unwrap().and_utc(); + self.params.push((key.to_string(), datetime.to_rfc3339())); + } + self + } + + /// Add any serializable parameter (for enums and complex types) + pub fn serialize(mut self, key: &str, value: Option) -> Self { + if let Some(v) = value { + // For enums that implement Display, use the Display implementation + // to avoid JSON quotes in query parameters + if let Ok(serialized) = serde_json::to_string(&v) { + // Remove JSON quotes if the value is a simple string + let cleaned = if serialized.starts_with('"') && serialized.ends_with('"') { + serialized.trim_matches('"').to_string() + } else { + serialized + }; + self.params.push((key.to_string(), cleaned)); + } + } + self + } + + /// Add multiple serializable parameters with the same key (for allow-multiple query params with enums) + /// Accepts both Vec and Vec>, adding each non-None value as a separate query parameter + pub fn serialize_array( + mut self, + key: &str, + values: impl IntoIterator, + ) -> Self { + for value in values { + if let Ok(serialized) = serde_json::to_string(&value) { + // Skip null values (from Option::None) + if serialized == "null" { + continue; + } + // Remove JSON quotes if the value is a simple string + let cleaned = if serialized.starts_with('"') && serialized.ends_with('"') { + serialized.trim_matches('"').to_string() + } else { + serialized + }; + self.params.push((key.to_string(), cleaned)); + } + } + self + } + + /// Parse and add a structured query string + /// Handles complex query patterns like: + /// - "key:value" patterns + /// - "key:value1,value2" (comma-separated values) + /// - Quoted values: "key:\"value with spaces\"" + /// - Space-separated terms (treated as AND logic) + pub fn structured_query(mut self, key: &str, value: impl Into>) -> Self { + if let Some(query_str) = value.into() { + if let Ok(parsed_params) = parse_structured_query(&query_str) { + self.params.extend(parsed_params); + } else { + // Fall back to simple query parameter if parsing fails + self.params.push((key.to_string(), query_str)); + } + } + self + } + + /// Build the final query parameters + pub fn build(self) -> Option> { + if self.params.is_empty() { + None + } else { + Some(self.params) + } + } +} + +/// Errors that can occur during structured query parsing +#[derive(Debug)] +pub enum QueryBuilderError { + InvalidQuerySyntax(String), +} + +impl std::fmt::Display for QueryBuilderError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + QueryBuilderError::InvalidQuerySyntax(msg) => { + write!(f, "Invalid query syntax: {}", msg) + } + } + } +} + +impl std::error::Error for QueryBuilderError {} + +/// Parse structured query strings like "key:value key2:value1,value2" +/// Used for complex filtering patterns in APIs like Foxglove +/// +/// Supported patterns: +/// - Simple: "status:active" +/// - Multiple values: "type:sensor,camera" +/// - Quoted values: "location:\"New York\"" +/// - Complex: "status:active type:sensor location:\"San Francisco\"" +pub fn parse_structured_query(query: &str) -> Result, QueryBuilderError> { + let mut params = Vec::new(); + let terms = tokenize_query(query); + + for term in terms { + if let Some((key, values)) = term.split_once(':') { + // Handle comma-separated values + for value in values.split(',') { + let clean_value = value.trim_matches('"'); // Remove quotes + params.push((key.to_string(), clean_value.to_string())); + } + } else { + // For terms without colons, return error to be explicit about expected format + return Err(QueryBuilderError::InvalidQuerySyntax(format!( + "Cannot parse term '{}' - expected 'key:value' format for structured queries", + term + ))); + } + } + + Ok(params) +} + +/// Tokenize a query string, properly handling quoted strings +fn tokenize_query(input: &str) -> Vec { + let mut tokens = Vec::new(); + let mut current_token = String::new(); + let mut in_quotes = false; + let mut chars = input.chars().peekable(); + + while let Some(c) = chars.next() { + match c { + '"' => { + // Toggle quote state and include the quote in the token + in_quotes = !in_quotes; + current_token.push(c); + } + ' ' if !in_quotes => { + // Space outside quotes - end current token + if !current_token.is_empty() { + tokens.push(current_token.trim().to_string()); + current_token.clear(); + } + } + _ => { + // Any other character (including spaces inside quotes) + current_token.push(c); + } + } + } + + // Add the last token if there is one + if !current_token.is_empty() { + tokens.push(current_token.trim().to_string()); + } + + tokens +} diff --git a/seed/rust-sdk/inferred-auth-implicit-reference/src/core/request_options.rs b/seed/rust-sdk/inferred-auth-implicit-reference/src/core/request_options.rs new file mode 100644 index 000000000000..dbd4d60d4940 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-reference/src/core/request_options.rs @@ -0,0 +1,58 @@ +use std::collections::HashMap; +/// Options for customizing individual requests +#[derive(Debug, Clone, Default)] +pub struct RequestOptions { + /// API key for authentication (overrides client-level API key) + pub api_key: Option, + /// Bearer token for authentication (overrides client-level token) + pub token: Option, + /// Maximum number of retry attempts for failed requests + pub max_retries: Option, + /// Request timeout in seconds (overrides client-level timeout) + pub timeout_seconds: Option, + /// Additional headers to include in the request + pub additional_headers: HashMap, + /// Additional query parameters to include in the request + pub additional_query_params: HashMap, +} + +impl RequestOptions { + pub fn new() -> Self { + Self::default() + } + + pub fn api_key(mut self, key: impl Into) -> Self { + self.api_key = Some(key.into()); + self + } + + pub fn token(mut self, token: impl Into) -> Self { + self.token = Some(token.into()); + self + } + + pub fn max_retries(mut self, retries: u32) -> Self { + self.max_retries = Some(retries); + self + } + + pub fn timeout_seconds(mut self, timeout: u64) -> Self { + self.timeout_seconds = Some(timeout); + self + } + + pub fn additional_header(mut self, key: impl Into, value: impl Into) -> Self { + self.additional_headers.insert(key.into(), value.into()); + self + } + + pub fn additional_query_param( + mut self, + key: impl Into, + value: impl Into, + ) -> Self { + self.additional_query_params + .insert(key.into(), value.into()); + self + } +} diff --git a/seed/rust-sdk/inferred-auth-implicit-reference/src/core/sse_stream.rs b/seed/rust-sdk/inferred-auth-implicit-reference/src/core/sse_stream.rs new file mode 100644 index 000000000000..50a375e0a7ff --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-reference/src/core/sse_stream.rs @@ -0,0 +1,281 @@ +use crate::ApiError; +use futures::Stream; +use pin_project::pin_project; +use reqwest::{header::CONTENT_TYPE, Response}; +use reqwest_sse::{error::EventError, Event, EventSource}; +use serde::de::DeserializeOwned; +use std::{ + marker::PhantomData, + pin::Pin, + task::{Context, Poll}, +}; + +/// Metadata from a Server-Sent Event +/// +/// Contains the SSE protocol fields (event, id, retry) that accompany the data payload. +/// This struct provides access to SSE metadata for advanced use cases. +/// +/// Per the SSE specification: +/// - `event` defaults to "message" when not specified by the server +/// - `id` is optional and used for reconnection support (Last-Event-ID header) +/// - `retry` is optional and specifies reconnection timeout in milliseconds +#[derive(Debug, Clone)] +pub struct SseMetadata { + /// The event type (defaults to "message" per SSE spec if not specified by server) + pub event: String, + /// The event ID for reconnection support (None if not specified) + pub id: Option, + /// Retry timeout in milliseconds (None if not specified) + pub retry: Option, +} + +/// A Server-Sent Event with both data and metadata +/// +/// Contains the deserialized data payload along with SSE protocol metadata. +/// Use this when you need access to event IDs, event types, or retry information. +#[derive(Debug)] +pub struct SseEvent { + /// The deserialized data payload + pub data: T, + /// SSE protocol metadata + pub metadata: SseMetadata, +} + +/// A type-safe wrapper around Server-Sent Events (SSE) streams +/// +/// Leverages `reqwest-sse` for SSE protocol parsing and adds: +/// - Automatic JSON deserialization to typed structs +/// - Stream terminator support (e.g., `[DONE]` for OpenAI-style APIs) +/// - Integrated error handling with `ApiError` +/// - Content-Type validation (`text/event-stream` required) +/// +/// # Charset Handling +/// +/// The `reqwest-sse` library automatically handles charset detection and decoding +/// based on the Content-Type header. If no charset is specified, UTF-8 is assumed. +/// This matches the SSE specification default behavior. +/// +/// # Example +/// +/// Basic usage with async iteration: +/// +/// ```no_run +/// use futures::StreamExt; +/// +/// let stream: SseStream = client.stream_completions(request).await?; +/// let mut stream = std::pin::pin!(stream); +/// +/// while let Some(result) = stream.next().await { +/// match result { +/// Ok(chunk) => println!("Received: {:?}", chunk), +/// Err(e) => eprintln!("Error: {}", e), +/// } +/// } +/// ``` +/// +/// # Error Handling +/// +/// The stream yields `Result` items. Errors can occur from: +/// - Invalid JSON in SSE data field (`ApiError::Serialization`) +/// - SSE protocol errors (`ApiError::SseParseError`) +/// - Network errors during streaming +/// +/// **Important:** When an error occurs for a single event (e.g., malformed JSON), +/// the stream yields `Err` for that item but **continues streaming** subsequent events. +/// The stream only ends when: +/// - A terminator is received (if configured) +/// - The server closes the connection +/// - A fatal network error occurs +/// +/// This allows the client to handle per-event errors gracefully without losing +/// the entire stream. Compare this to other error handling strategies where a single +/// bad event might terminate the stream. +/// +/// # Terminator Support +/// +/// When a terminator string is specified (e.g., `[DONE]`), the stream automatically +/// ends when an SSE event with that exact data is received. The terminator event +/// itself is not yielded to the consumer. +#[pin_project] +pub struct SseStream { + #[pin] + inner: Pin> + Send>>, + terminator: Option, + _phantom: PhantomData, +} + +impl SseStream +where + T: DeserializeOwned, +{ + /// Create a new SSE stream from a Response + /// + /// # Arguments + /// * `response` - The HTTP response to parse as SSE + /// * `terminator` - Optional terminator string (e.g., `"[DONE]"`) that signals end of stream + /// + /// # Errors + /// Returns `ApiError::SseParseError` if: + /// - Response Content-Type is not `text/event-stream` + /// - SSE stream cannot be created from response + pub(crate) async fn new( + response: Response, + terminator: Option, + ) -> Result { + // Validate Content-Type header (case-insensitive, handles parameters) + let content_type = response + .headers() + .get(CONTENT_TYPE) + .and_then(|v| v.to_str().ok()) + .unwrap_or(""); + + // Extract main content type (before ';' parameter separator) and compare case-insensitively + let content_type_main = content_type.split(';').next().unwrap_or("").trim(); + + if !content_type_main.eq_ignore_ascii_case("text/event-stream") { + return Err(ApiError::SseParseError(format!( + "Expected Content-Type to be 'text/event-stream', got '{}'", + content_type + ))); + } + + // Use reqwest-sse's EventSource trait to get SSE stream + let events = response + .events() + .await + .map_err(|e| ApiError::SseParseError(e.to_string()))?; + + Ok(Self { + inner: Box::pin(events), + terminator, + _phantom: PhantomData, + }) + } +} + +impl SseStream +where + T: DeserializeOwned, +{ + /// Convert this stream into one that yields events with metadata + /// + /// This consumes the stream and returns a new stream that yields `SseEvent` + /// instead of just `T`, providing access to SSE metadata (event type, id, retry). + /// + /// # Example + /// + /// ```no_run + /// use futures::StreamExt; + /// + /// let stream = client.stream_completions(request).await?; + /// let mut stream_with_metadata = stream.with_metadata(); + /// let mut stream_with_metadata = std::pin::pin!(stream_with_metadata); + /// + /// while let Some(result) = stream_with_metadata.next().await { + /// match result { + /// Ok(event) => { + /// println!("Data: {:?}", event.data); + /// println!("Event type: {}", event.metadata.event); + /// if let Some(id) = &event.metadata.id { + /// println!("Event ID: {}", id); + /// } + /// } + /// Err(e) => eprintln!("Error: {}", e), + /// } + /// } + /// ``` + pub fn with_metadata(self) -> SseStreamWithMetadata { + SseStreamWithMetadata { inner: self } + } +} + +impl Stream for SseStream +where + T: DeserializeOwned, +{ + type Item = Result; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + match this.inner.poll_next(cx) { + Poll::Ready(Some(Ok(event))) => { + // Check for terminator before parsing + if let Some(ref terminator) = this.terminator { + if event.data == *terminator { + // Terminator found - end stream cleanly + return Poll::Ready(None); + } + } + + // Deserialize JSON data to typed struct + match serde_json::from_str(&event.data) { + Ok(value) => Poll::Ready(Some(Ok(value))), + Err(e) => Poll::Ready(Some(Err(ApiError::Serialization(e)))), + } + } + Poll::Ready(Some(Err(e))) => { + Poll::Ready(Some(Err(ApiError::SseParseError(e.to_string())))) + } + Poll::Ready(None) => Poll::Ready(None), + Poll::Pending => Poll::Pending, + } + } +} + +/// Stream wrapper that yields events with metadata +/// +/// Created by calling [`SseStream::with_metadata()`]. This stream yields `SseEvent` +/// which includes both the deserialized data and SSE protocol metadata. +#[pin_project] +pub struct SseStreamWithMetadata { + #[pin] + inner: SseStream, +} + +impl Stream for SseStreamWithMetadata +where + T: DeserializeOwned, +{ + type Item = Result, ApiError>; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + + // Access the inner stream's fields through pin projection + let inner_pin = this.inner.project(); + + match inner_pin.inner.poll_next(cx) { + Poll::Ready(Some(Ok(event))) => { + // Check for terminator + if let Some(ref terminator) = inner_pin.terminator { + if event.data == *terminator { + return Poll::Ready(None); + } + } + + // Extract metadata + let metadata = SseMetadata { + // Default to "message" if event type is empty (per SSE spec) + event: if event.event_type.is_empty() { + "message".to_string() + } else { + event.event_type.clone() + }, + id: event.last_event_id.clone(), + retry: event.retry.map(|d| d.as_millis() as u64), + }; + + // Deserialize JSON data + match serde_json::from_str(&event.data) { + Ok(data) => Poll::Ready(Some(Ok(SseEvent { data, metadata }))), + Err(e) => Poll::Ready(Some(Err(ApiError::Serialization(e)))), + } + } + Poll::Ready(Some(Err(e))) => { + Poll::Ready(Some(Err(ApiError::SseParseError(e.to_string())))) + } + Poll::Ready(None) => Poll::Ready(None), + Poll::Pending => Poll::Pending, + } + } +} diff --git a/seed/rust-sdk/inferred-auth-implicit-reference/src/core/utils.rs b/seed/rust-sdk/inferred-auth-implicit-reference/src/core/utils.rs new file mode 100644 index 000000000000..808056dc34a2 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-reference/src/core/utils.rs @@ -0,0 +1,19 @@ +/// URL building utilities +/// Safely join a base URL with a path, handling slashes properly +/// +/// # Examples +/// ``` +/// use example_api::utils::url::join_url; +/// +/// assert_eq!(join_url("https://api.example.com", "users"), "https://api.example.com/users"); +/// assert_eq!(join_url("https://api.example.com/", "users"), "https://api.example.com/users"); +/// assert_eq!(join_url("https://api.example.com", "/users"), "https://api.example.com/users"); +/// assert_eq!(join_url("https://api.example.com/", "/users"), "https://api.example.com/users"); +/// ``` +pub fn join_url(base_url: &str, path: &str) -> String { + format!( + "{}/{}", + base_url.trim_end_matches('/'), + path.trim_start_matches('/') + ) +} diff --git a/seed/rust-sdk/inferred-auth-implicit-reference/src/error.rs b/seed/rust-sdk/inferred-auth-implicit-reference/src/error.rs new file mode 100644 index 000000000000..7feca158a561 --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-reference/src/error.rs @@ -0,0 +1,32 @@ +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum ApiError { + #[error("HTTP error {status}: {message}")] + Http { status: u16, message: String }, + #[error("Network error: {0}")] + Network(reqwest::Error), + #[error("Serialization error: {0}")] + Serialization(serde_json::Error), + #[error("Configuration error: {0}")] + Configuration(String), + #[error("Invalid header value")] + InvalidHeader, + #[error("Could not clone request for retry")] + RequestClone, + #[error("SSE stream terminated")] + StreamTerminated, + #[error("SSE parse error: {0}")] + SseParseError(String), +} + +impl ApiError { + pub fn from_response(status_code: u16, body: Option<&str>) -> Self { + match status_code { + _ => Self::Http { + status: status_code, + message: body.unwrap_or("Unknown error").to_string(), + }, + } + } +} diff --git a/seed/rust-sdk/inferred-auth-implicit-reference/src/lib.rs b/seed/rust-sdk/inferred-auth-implicit-reference/src/lib.rs new file mode 100644 index 000000000000..94057e0212ab --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-reference/src/lib.rs @@ -0,0 +1,52 @@ +//! # InferredAuthImplicit SDK +//! +//! The official Rust SDK for the InferredAuthImplicit. +//! +//! ## Getting Started +//! +//! ```rust +//! use seed_inferred_auth_implicit::prelude::*; +//! +//! #[tokio::main] +//! async fn main() { +//! let config = ClientConfig { +//! ..Default::default() +//! }; +//! let client = InferredAuthImplicitClient::new(config).expect("Failed to build client"); +//! client +//! .auth +//! .get_token_with_client_credentials( +//! &GetTokenRequest { +//! client_id: "client_id".to_string(), +//! client_secret: "client_secret".to_string(), +//! audience: "https://api.example.com".to_string(), +//! grant_type: "client_credentials".to_string(), +//! scope: Some("scope".to_string()), +//! }, +//! None, +//! ) +//! .await; +//! } +//! ``` +//! +//! ## Modules +//! +//! - [`api`] - Core API types and models +//! - [`client`] - Client implementations +//! - [`config`] - Configuration options +//! - [`core`] - Core utilities and infrastructure +//! - [`error`] - Error types and handling +//! - [`prelude`] - Common imports for convenience + +pub mod api; +pub mod client; +pub mod config; +pub mod core; +pub mod error; +pub mod prelude; + +pub use api::*; +pub use client::*; +pub use config::*; +pub use core::*; +pub use error::ApiError; diff --git a/seed/rust-sdk/inferred-auth-implicit-reference/src/prelude.rs b/seed/rust-sdk/inferred-auth-implicit-reference/src/prelude.rs new file mode 100644 index 000000000000..68272ae0584f --- /dev/null +++ b/seed/rust-sdk/inferred-auth-implicit-reference/src/prelude.rs @@ -0,0 +1,19 @@ +//! Prelude module for convenient imports +//! +//! This module re-exports the most commonly used types and traits. +//! Import it with: `use seed_inferred_auth_implicit::prelude::*;` + +// Client and configuration +pub use crate::config::ClientConfig; +pub use crate::core::{HttpClient, RequestOptions}; +pub use crate::error::ApiError; + +// Main client and resource clients +pub use crate::api::*; + +// Re-export commonly used external types +pub use ordered_float::OrderedFloat; +pub use serde::{Deserialize, Serialize}; +pub use serde_json::{json, Value}; +pub use std::collections::{HashMap, HashSet}; +pub use std::fmt; diff --git a/seed/rust-sdk/nullable-allof-extends/.fern/metadata.json b/seed/rust-sdk/nullable-allof-extends/.fern/metadata.json new file mode 100644 index 000000000000..ce1c0cd76077 --- /dev/null +++ b/seed/rust-sdk/nullable-allof-extends/.fern/metadata.json @@ -0,0 +1,5 @@ +{ + "cliVersion": "DUMMY", + "generatorName": "fernapi/fern-rust-sdk", + "generatorVersion": "latest" +} \ No newline at end of file diff --git a/seed/rust-sdk/nullable-allof-extends/.github/workflows/ci.yml b/seed/rust-sdk/nullable-allof-extends/.github/workflows/ci.yml new file mode 100644 index 000000000000..1a311af94152 --- /dev/null +++ b/seed/rust-sdk/nullable-allof-extends/.github/workflows/ci.yml @@ -0,0 +1,59 @@ +name: ci + +on: [push] + +env: + RUSTFLAGS: "-A warnings" + +jobs: + check: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Rust + uses: actions-rust-lang/setup-rust-toolchain@v1 + + - name: Check + run: cargo check + + compile: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Rust + uses: actions-rust-lang/setup-rust-toolchain@v1 + + - name: Compile + run: cargo build + + test: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Rust + uses: actions-rust-lang/setup-rust-toolchain@v1 + + - name: Test + run: cargo test + + publish: + needs: [check, compile, test] + if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Rust + uses: actions-rust-lang/setup-rust-toolchain@v1 + + - name: Publish + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + run: cargo publish \ No newline at end of file diff --git a/seed/rust-sdk/nullable-allof-extends/.gitignore b/seed/rust-sdk/nullable-allof-extends/.gitignore new file mode 100644 index 000000000000..f75d27799da1 --- /dev/null +++ b/seed/rust-sdk/nullable-allof-extends/.gitignore @@ -0,0 +1,5 @@ +/target +**/*.rs.bk +Cargo.lock +.DS_Store +*.swp \ No newline at end of file diff --git a/seed/rust-sdk/nullable-allof-extends/Cargo.toml b/seed/rust-sdk/nullable-allof-extends/Cargo.toml new file mode 100644 index 000000000000..ca5b93473101 --- /dev/null +++ b/seed/rust-sdk/nullable-allof-extends/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "seed_api" +version = "0.0.1" +edition = "2021" +description = "Rust SDK for seed_api generated by Fern" +license = "MIT" +repository = "https://github.com/fern-api/fern" +documentation = "https://docs.rs/seed_api" + +[lib] +doctest = false + +[dependencies] +bytes = "1.0" +chrono = { version = "0.4", features = ["serde"] } +futures = "0.3" +num-bigint = { version = "0.4", features = ["serde"] } +ordered-float = { version = "4.5", features = ["serde"] } +percent-encoding = "2.3" +reqwest = { version = "0.12", features = ["json", "stream"], default-features = false } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +thiserror = "1.0" +tokio = { version = "1.0", features = ["full"] } +uuid = { version = "1.0", features = ["serde"] } + +[dev-dependencies] +tokio-test = "0.4" + +[features] +multipart = [] +sse = [] diff --git a/seed/rust-sdk/nullable-allof-extends/README.md b/seed/rust-sdk/nullable-allof-extends/README.md new file mode 100644 index 000000000000..22d757a87c2f --- /dev/null +++ b/seed/rust-sdk/nullable-allof-extends/README.md @@ -0,0 +1,153 @@ +# Seed Rust Library + +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FRust) +[![crates.io shield](https://img.shields.io/crates/v/seed_api)](https://crates.io/crates/seed_api) + +The Seed Rust library provides convenient access to the Seed APIs from Rust. + +## Table of Contents + +- [Installation](#installation) +- [Reference](#reference) +- [Usage](#usage) +- [Errors](#errors) +- [Advanced](#advanced) + - [Retries](#retries) + - [Timeouts](#timeouts) + - [Additional Headers](#additional-headers) + - [Additional Query String Parameters](#additional-query-string-parameters) +- [Contributing](#contributing) + +## Installation + +Add this to your `Cargo.toml`: + +```toml +[dependencies] +seed_api = "0.0.1" +``` + +Or install via cargo: + +```sh +cargo add seed_api +``` + +## Reference + +A full reference for this library is available [here](./reference.md). + +## Usage + +Instantiate and use the client with the following: + +```rust +use seed_api::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + ..Default::default() + }; + let client = ApiClient::new(config).expect("Failed to build client"); + client + .create_test( + &RootObject { + normal_field: None, + nullable_field: None, + }, + None, + ) + .await; +} +``` + +## Errors + +When the API returns a non-success status code (4xx or 5xx response), an error will be returned. + +```rust +match client.create_test(None)?.await { + Ok(response) => { + println!("Success: {:?}", response); + }, + Err(ApiError::HTTP { status, message }) => { + println!("API Error {}: {:?}", status, message); + }, + Err(e) => { + println!("Other error: {:?}", e); + } +} +``` + +## Advanced + +### Retries + +The SDK is instrumented with automatic retries with exponential backoff. A request will be retried as long +as the request is deemed retryable and the number of retry attempts has not grown larger than the configured +retry limit (default: 2). + +A request is deemed retryable when any of the following HTTP status codes is returned: + +- [408](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/408) (Timeout) +- [429](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429) (Too Many Requests) +- [5XX](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500) (Internal Server Errors) + +Use the `max_retries` method to configure this behavior. + +```rust +let response = client.create_test( + Some(RequestOptions::new().max_retries(3)) +)?.await; +``` + +### Timeouts + +The SDK defaults to a 30 second timeout. Use the `timeout` method to configure this behavior. + +```rust +let response = client.create_test( + Some(RequestOptions::new().timeout_seconds(30)) +)?.await; +``` + +### Additional Headers + +You can add custom headers to requests using `RequestOptions`. + +```rust +let response = client.create_test( + Some( + RequestOptions::new() + .additional_header("X-Custom-Header", "custom-value") + .additional_header("X-Another-Header", "another-value") + ) +)? +.await; +``` + +### Additional Query String Parameters + +You can add custom query parameters to requests using `RequestOptions`. + +```rust +let response = client.create_test( + Some( + RequestOptions::new() + .additional_query_param("filter", "active") + .additional_query_param("sort", "desc") + ) +)? +.await; +``` + +## Contributing + +While we value open-source contributions to this SDK, this library is generated programmatically. +Additions made directly to this library would have to be moved over to our generation code, +otherwise they would be overwritten upon the next generated release. Feel free to open a PR as +a proof of concept, but know that we will not be able to merge it as-is. We suggest opening +an issue first to discuss with us! + +On the other hand, contributions to the README are always very welcome! \ No newline at end of file diff --git a/seed/rust-sdk/nullable-allof-extends/dynamic-snippets/example0.rs b/seed/rust-sdk/nullable-allof-extends/dynamic-snippets/example0.rs new file mode 100644 index 000000000000..df4021570fd3 --- /dev/null +++ b/seed/rust-sdk/nullable-allof-extends/dynamic-snippets/example0.rs @@ -0,0 +1,11 @@ +use seed_api::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + base_url: "https://api.fern.com".to_string(), + ..Default::default() + }; + let client = ApiClient::new(config).expect("Failed to build client"); + client.get_test(None).await; +} diff --git a/seed/rust-sdk/nullable-allof-extends/dynamic-snippets/example1.rs b/seed/rust-sdk/nullable-allof-extends/dynamic-snippets/example1.rs new file mode 100644 index 000000000000..df4021570fd3 --- /dev/null +++ b/seed/rust-sdk/nullable-allof-extends/dynamic-snippets/example1.rs @@ -0,0 +1,11 @@ +use seed_api::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + base_url: "https://api.fern.com".to_string(), + ..Default::default() + }; + let client = ApiClient::new(config).expect("Failed to build client"); + client.get_test(None).await; +} diff --git a/seed/rust-sdk/nullable-allof-extends/dynamic-snippets/example2.rs b/seed/rust-sdk/nullable-allof-extends/dynamic-snippets/example2.rs new file mode 100644 index 000000000000..993e78a49207 --- /dev/null +++ b/seed/rust-sdk/nullable-allof-extends/dynamic-snippets/example2.rs @@ -0,0 +1,19 @@ +use seed_api::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + base_url: "https://api.fern.com".to_string(), + ..Default::default() + }; + let client = ApiClient::new(config).expect("Failed to build client"); + client + .create_test( + &RootObject { + normal_field: None, + nullable_field: None, + }, + None, + ) + .await; +} diff --git a/seed/rust-sdk/nullable-allof-extends/dynamic-snippets/example3.rs b/seed/rust-sdk/nullable-allof-extends/dynamic-snippets/example3.rs new file mode 100644 index 000000000000..f1526818587e --- /dev/null +++ b/seed/rust-sdk/nullable-allof-extends/dynamic-snippets/example3.rs @@ -0,0 +1,19 @@ +use seed_api::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + base_url: "https://api.fern.com".to_string(), + ..Default::default() + }; + let client = ApiClient::new(config).expect("Failed to build client"); + client + .create_test( + &RootObject { + normal_field: Some("normalField".to_string()), + nullable_field: Some("nullableField".to_string()), + }, + None, + ) + .await; +} diff --git a/seed/rust-sdk/nullable-allof-extends/reference.md b/seed/rust-sdk/nullable-allof-extends/reference.md new file mode 100644 index 000000000000..ab521a128ec5 --- /dev/null +++ b/seed/rust-sdk/nullable-allof-extends/reference.md @@ -0,0 +1,104 @@ +# Reference +
client.get_test() -> Result +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Returns a RootObject which inherits from a nullable schema. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```rust +use seed_api::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + ..Default::default() + }; + let client = ApiClient::new(config).expect("Failed to build client"); + client.get_test(None).await; +} +``` +
+
+
+
+ + +
+
+
+ +
client.create_test(request: RootObject) -> Result +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Creates a test object with nullable allOf in request body. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```rust +use seed_api::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + ..Default::default() + }; + let client = ApiClient::new(config).expect("Failed to build client"); + client + .create_test( + &RootObject { + normal_field: None, + nullable_field: None, + }, + None, + ) + .await; +} +``` +
+
+
+
+ + +
+
+
diff --git a/seed/rust-sdk/nullable-allof-extends/rustfmt.toml b/seed/rust-sdk/nullable-allof-extends/rustfmt.toml new file mode 100644 index 000000000000..872221fb31fe --- /dev/null +++ b/seed/rust-sdk/nullable-allof-extends/rustfmt.toml @@ -0,0 +1,4 @@ +# Generated by Fern +edition = "2021" +max_width = 100 +use_small_heuristics = "Default" \ No newline at end of file diff --git a/seed/rust-sdk/nullable-allof-extends/snippet.json b/seed/rust-sdk/nullable-allof-extends/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/rust-sdk/nullable-allof-extends/src/api/mod.rs b/seed/rust-sdk/nullable-allof-extends/src/api/mod.rs new file mode 100644 index 000000000000..f0aa5eae22e9 --- /dev/null +++ b/seed/rust-sdk/nullable-allof-extends/src/api/mod.rs @@ -0,0 +1,15 @@ +//! API client and types for the Nullable AllOf Extends Test +//! +//! This module contains all the API definitions including request/response types +//! and client implementations for interacting with the API. +//! +//! ## Modules +//! +//! - [`resources`] - Service clients and endpoints +//! - [`types`] - Request, response, and model types + +pub mod resources; +pub mod types; + +pub use resources::ApiClient; +pub use types::*; diff --git a/seed/rust-sdk/nullable-allof-extends/src/api/resources/mod.rs b/seed/rust-sdk/nullable-allof-extends/src/api/resources/mod.rs new file mode 100644 index 000000000000..e2964679a784 --- /dev/null +++ b/seed/rust-sdk/nullable-allof-extends/src/api/resources/mod.rs @@ -0,0 +1,17 @@ +//! Service clients and API endpoints +//! +//! This module provides the client implementations for all available services. + +use crate::{ApiError, ClientConfig}; + +pub struct ApiClient { + pub config: ClientConfig, +} + +impl ApiClient { + pub fn new(config: ClientConfig) -> Result { + Ok(Self { + config: config.clone(), + }) + } +} diff --git a/seed/rust-sdk/nullable-allof-extends/src/api/types/mod.rs b/seed/rust-sdk/nullable-allof-extends/src/api/types/mod.rs new file mode 100644 index 000000000000..54fe68bb5c6b --- /dev/null +++ b/seed/rust-sdk/nullable-allof-extends/src/api/types/mod.rs @@ -0,0 +1,7 @@ +pub mod normal_object; +pub mod nullable_object; +pub mod root_object; + +pub use normal_object::NormalObject; +pub use nullable_object::NullableObject; +pub use root_object::RootObject; diff --git a/seed/rust-sdk/nullable-allof-extends/src/api/types/normal_object.rs b/seed/rust-sdk/nullable-allof-extends/src/api/types/normal_object.rs new file mode 100644 index 000000000000..a7ff367a8a65 --- /dev/null +++ b/seed/rust-sdk/nullable-allof-extends/src/api/types/normal_object.rs @@ -0,0 +1,9 @@ +pub use crate::prelude::*; + +/// A standard object with no nullable issues. +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub struct NormalObject { + #[serde(rename = "normalField")] + #[serde(skip_serializing_if = "Option::is_none")] + pub normal_field: Option, +} diff --git a/seed/rust-sdk/nullable-allof-extends/src/api/types/nullable_object.rs b/seed/rust-sdk/nullable-allof-extends/src/api/types/nullable_object.rs new file mode 100644 index 000000000000..a9f2b8a2b3e6 --- /dev/null +++ b/seed/rust-sdk/nullable-allof-extends/src/api/types/nullable_object.rs @@ -0,0 +1,9 @@ +pub use crate::prelude::*; + +/// This schema has nullable:true at the top level. +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub struct NullableObject { + #[serde(rename = "nullableField")] + #[serde(skip_serializing_if = "Option::is_none")] + pub nullable_field: Option, +} diff --git a/seed/rust-sdk/nullable-allof-extends/src/api/types/root_object.rs b/seed/rust-sdk/nullable-allof-extends/src/api/types/root_object.rs new file mode 100644 index 000000000000..7e9728a9c579 --- /dev/null +++ b/seed/rust-sdk/nullable-allof-extends/src/api/types/root_object.rs @@ -0,0 +1,10 @@ +pub use crate::prelude::*; + +/// Object inheriting from a nullable schema via allOf. +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub struct RootObject { + #[serde(flatten)] + pub normal_object_fields: NormalObject, + #[serde(flatten)] + pub nullable_object_fields: NullableObject, +} diff --git a/seed/rust-sdk/nullable-allof-extends/src/client.rs b/seed/rust-sdk/nullable-allof-extends/src/client.rs new file mode 100644 index 000000000000..bcd68c3400b1 --- /dev/null +++ b/seed/rust-sdk/nullable-allof-extends/src/client.rs @@ -0,0 +1,92 @@ +use crate::api::resources::ApiClient; +use crate::{ApiError, ClientConfig}; +use std::collections::HashMap; +use std::time::Duration; + +/* +Things to know: + +`impl Into` is a generic parameter constraint in Rust that means "accept any type that can be converted into a String." +It's essentially Rust's way of saying "I'll take a string in any form you give it to me." + +Types that implement Into: + +// All of these work: +builder.api_key("hello") // &str +builder.api_key("hello".to_string()) // String +builder.api_key(format!("key_{}", id)) // String from format! +builder.api_key(my_string_variable) // String variable +*/ + +/// Builder for creating API clients with custom configuration +pub struct ApiClientBuilder { + config: ClientConfig, +} + +impl ApiClientBuilder { + /// Create a new builder with the specified base URL + pub fn new(base_url: impl Into) -> Self { + let mut config = ClientConfig::default(); + config.base_url = base_url.into(); + Self { config } + } + + /// Set the API key for authentication + pub fn api_key(mut self, key: impl Into) -> Self { + self.config.api_key = Some(key.into()); + self + } + + /// Set the bearer token for authentication + pub fn token(mut self, token: impl Into) -> Self { + self.config.token = Some(token.into()); + self + } + + /// Set the username for basic authentication + pub fn username(mut self, username: impl Into) -> Self { + self.config.username = Some(username.into()); + self + } + + /// Set the password for basic authentication + pub fn password(mut self, password: impl Into) -> Self { + self.config.password = Some(password.into()); + self + } + + /// Set the request timeout + pub fn timeout(mut self, timeout: Duration) -> Self { + self.config.timeout = timeout; + self + } + + /// Set the maximum number of retries + pub fn max_retries(mut self, retries: u32) -> Self { + self.config.max_retries = retries; + self + } + + /// Add a custom header + pub fn custom_header(mut self, key: impl Into, value: impl Into) -> Self { + self.config.custom_headers.insert(key.into(), value.into()); + self + } + + /// Add multiple custom headers + pub fn custom_headers(mut self, headers: HashMap) -> Self { + self.config.custom_headers.extend(headers); + self + } + + /// Set the user agent + pub fn user_agent(mut self, user_agent: impl Into) -> Self { + self.config.user_agent = user_agent.into(); + self + } + + /// Build the client with validation + pub fn build(self) -> Result { + ApiClient::new(self.config) + } +} diff --git a/seed/rust-sdk/nullable-allof-extends/src/config.rs b/seed/rust-sdk/nullable-allof-extends/src/config.rs new file mode 100644 index 000000000000..ff9556a7accb --- /dev/null +++ b/seed/rust-sdk/nullable-allof-extends/src/config.rs @@ -0,0 +1,31 @@ +use crate::Environment; +use std::collections::HashMap; +use std::time::Duration; + +#[derive(Debug, Clone)] +pub struct ClientConfig { + pub base_url: String, + pub api_key: Option, + pub token: Option, + pub username: Option, + pub password: Option, + pub timeout: Duration, + pub max_retries: u32, + pub custom_headers: HashMap, + pub user_agent: String, +} +impl Default for ClientConfig { + fn default() -> Self { + Self { + base_url: Environment::default().url().to_string(), + api_key: None, + token: None, + username: None, + password: None, + timeout: Duration::from_secs(60), + max_retries: 3, + custom_headers: HashMap::new(), + user_agent: "Api Rust SDK".to_string(), + } + } +} diff --git a/seed/rust-sdk/nullable-allof-extends/src/core/http_client.rs b/seed/rust-sdk/nullable-allof-extends/src/core/http_client.rs new file mode 100644 index 000000000000..e1171a4537f3 --- /dev/null +++ b/seed/rust-sdk/nullable-allof-extends/src/core/http_client.rs @@ -0,0 +1,512 @@ +use crate::{join_url, ApiError, ClientConfig, RequestOptions}; +use futures::{Stream, StreamExt}; +use reqwest::{ + header::{HeaderName, HeaderValue}, + Client, Method, Request, Response, +}; +use serde::de::DeserializeOwned; +use std::{ + pin::Pin, + str::FromStr, + task::{Context, Poll}, +}; + +/// A streaming byte stream for downloading files efficiently +pub struct ByteStream { + content_length: Option, + inner: Pin> + Send>>, +} + +impl ByteStream { + /// Create a new ByteStream from a Response + pub(crate) fn new(response: Response) -> Self { + let content_length = response.content_length(); + let stream = response.bytes_stream(); + + Self { + content_length, + inner: Box::pin(stream), + } + } + + /// Collect the entire stream into a `Vec` + /// + /// This consumes the stream and buffers all data into memory. + /// For large files, prefer using `try_next()` to process chunks incrementally. + /// + /// # Example + /// ```no_run + /// let stream = client.download_file().await?; + /// let bytes = stream.collect().await?; + /// ``` + pub async fn collect(mut self) -> Result, ApiError> { + let mut result = Vec::new(); + while let Some(chunk) = self.inner.next().await { + result.extend_from_slice(&chunk.map_err(ApiError::Network)?); + } + Ok(result) + } + + /// Get the next chunk from the stream + /// + /// Returns `Ok(Some(bytes))` if a chunk is available, + /// `Ok(None)` if the stream is finished, or an error. + /// + /// # Example + /// ```no_run + /// let mut stream = client.download_file().await?; + /// while let Some(chunk) = stream.try_next().await? { + /// process_chunk(&chunk); + /// } + /// ``` + pub async fn try_next(&mut self) -> Result, ApiError> { + match self.inner.next().await { + Some(Ok(bytes)) => Ok(Some(bytes)), + Some(Err(e)) => Err(ApiError::Network(e)), + None => Ok(None), + } + } + + /// Get the content length from response headers if available + pub fn content_length(&self) -> Option { + self.content_length + } +} + +impl Stream for ByteStream { + type Item = Result; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + match self.inner.as_mut().poll_next(cx) { + Poll::Ready(Some(Ok(bytes))) => Poll::Ready(Some(Ok(bytes))), + Poll::Ready(Some(Err(e))) => Poll::Ready(Some(Err(ApiError::Network(e)))), + Poll::Ready(None) => Poll::Ready(None), + Poll::Pending => Poll::Pending, + } + } +} + +/// Internal HTTP client that handles requests with authentication and retries +#[derive(Clone)] +pub struct HttpClient { + client: Client, + config: ClientConfig, +} + +impl HttpClient { + pub fn new(config: ClientConfig) -> Result { + let client = Client::builder() + .timeout(config.timeout) + .user_agent(&config.user_agent) + .build() + .map_err(ApiError::Network)?; + + Ok(Self { client, config }) + } + + /// Execute a request with the given method, path, and options + pub async fn execute_request( + &self, + method: Method, + path: &str, + body: Option, + query_params: Option>, + options: Option, + ) -> Result + where + T: DeserializeOwned, // Generic T: DeserializeOwned means the response will be automatically deserialized into whatever type you specify: + { + let url = join_url(&self.config.base_url, path); + let mut request = self.client.request(method, &url); + + // Apply query parameters if provided + if let Some(params) = query_params { + request = request.query(¶ms); + } + + // Apply additional query parameters from options + if let Some(opts) = &options { + if !opts.additional_query_params.is_empty() { + request = request.query(&opts.additional_query_params); + } + } + + // Apply body if provided + if let Some(body) = body { + request = request.json(&body); + } + + // Build the request + let mut req = request.build().map_err(|e| ApiError::Network(e))?; + + // Apply authentication and headers + self.apply_auth_headers(&mut req, &options)?; + self.apply_custom_headers(&mut req, &options)?; + + // Execute with retries + let response = self.execute_with_retries(req, &options).await?; + self.parse_response(response).await + } + + /// Execute a multipart/form-data request with the given method, path, and options + /// + /// This method is used for file uploads using reqwest's built-in multipart support. + /// Note: Multipart requests are not retried because they cannot be cloned. + /// + /// # Example + /// ```no_run + /// let form = reqwest::multipart::Form::new() + /// .part("file", reqwest::multipart::Part::bytes(vec![1, 2, 3])); + /// + /// let response: MyResponse = client.execute_multipart_request( + /// Method::POST, + /// "/upload", + /// form, + /// None, + /// None, + /// ).await?; + /// ``` + #[cfg(feature = "multipart")] + pub async fn execute_multipart_request( + &self, + method: Method, + path: &str, + form: reqwest::multipart::Form, + query_params: Option>, + options: Option, + ) -> Result + where + T: DeserializeOwned, + { + let url = join_url(&self.config.base_url, path); + let mut request = self.client.request(method, &url); + + // Apply query parameters if provided + if let Some(params) = query_params { + request = request.query(¶ms); + } + + // Apply additional query parameters from options + if let Some(opts) = &options { + if !opts.additional_query_params.is_empty() { + request = request.query(&opts.additional_query_params); + } + } + + // Use reqwest's built-in multipart support + request = request.multipart(form); + + // Build the request + let mut req = request.build().map_err(|e| ApiError::Network(e))?; + + // Apply authentication and headers + self.apply_auth_headers(&mut req, &options)?; + self.apply_custom_headers(&mut req, &options)?; + + // Execute directly without retries (multipart requests cannot be cloned) + let response = self.client.execute(req).await.map_err(ApiError::Network)?; + + // Check response status + if !response.status().is_success() { + let status_code = response.status().as_u16(); + let body = response.text().await.ok(); + return Err(ApiError::from_response(status_code, body.as_deref())); + } + + self.parse_response(response).await + } + + fn apply_auth_headers( + &self, + request: &mut Request, + options: &Option, + ) -> Result<(), ApiError> { + let headers = request.headers_mut(); + + // Apply API key (request options override config) + let api_key = options + .as_ref() + .and_then(|opts| opts.api_key.as_ref()) + .or(self.config.api_key.as_ref()); + + if let Some(key) = api_key { + headers.insert("api_key", key.parse().map_err(|_| ApiError::InvalidHeader)?); + } + + // Apply bearer token (request options override config) + let token = options + .as_ref() + .and_then(|opts| opts.token.as_ref()) + .or(self.config.token.as_ref()); + + if let Some(token) = token { + let auth_value = format!("Bearer {}", token); + headers.insert( + "Authorization", + auth_value.parse().map_err(|_| ApiError::InvalidHeader)?, + ); + } + + Ok(()) + } + + fn apply_custom_headers( + &self, + request: &mut Request, + options: &Option, + ) -> Result<(), ApiError> { + let headers = request.headers_mut(); + + // Apply config-level custom headers + for (key, value) in &self.config.custom_headers { + headers.insert( + HeaderName::from_str(key).map_err(|_| ApiError::InvalidHeader)?, + HeaderValue::from_str(value).map_err(|_| ApiError::InvalidHeader)?, + ); + } + + // Apply request-level custom headers (override config) + if let Some(options) = options { + for (key, value) in &options.additional_headers { + headers.insert( + HeaderName::from_str(key).map_err(|_| ApiError::InvalidHeader)?, + HeaderValue::from_str(value).map_err(|_| ApiError::InvalidHeader)?, + ); + } + } + + Ok(()) + } + + async fn execute_with_retries( + &self, + request: Request, + options: &Option, + ) -> Result { + let max_retries = options + .as_ref() + .and_then(|opts| opts.max_retries) + .unwrap_or(self.config.max_retries); + + let mut last_error = None; + + for attempt in 0..=max_retries { + let cloned_request = request.try_clone().ok_or(ApiError::RequestClone)?; + + match self.client.execute(cloned_request).await { + Ok(response) if response.status().is_success() => return Ok(response), + Ok(response) => { + let status_code = response.status().as_u16(); + let body = response.text().await.ok(); + return Err(ApiError::from_response(status_code, body.as_deref())); + } + Err(e) if attempt < max_retries => { + last_error = Some(e); + // Exponential backoff + let delay = std::time::Duration::from_millis(100 * 2_u64.pow(attempt)); + tokio::time::sleep(delay).await; + } + Err(e) => return Err(ApiError::Network(e)), + } + } + + Err(ApiError::Network(last_error.unwrap())) + } + + async fn parse_response(&self, response: Response) -> Result + where + T: DeserializeOwned, + { + let text = response.text().await.map_err(ApiError::Network)?; + serde_json::from_str(&text).map_err(ApiError::Serialization) + } + + /// Execute a request and return a streaming response (for large file downloads) + /// + /// This method returns a `ByteStream` that can be used to download large files + /// efficiently without loading the entire content into memory. The stream can be + /// consumed chunk by chunk, written directly to disk, or collected into bytes. + /// + /// # Examples + /// + /// **Option 1: Collect all bytes into memory** + /// ```no_run + /// let stream = client.execute_stream_request( + /// Method::GET, + /// "/file", + /// None, + /// None, + /// None, + /// ).await?; + /// + /// let bytes = stream.collect().await?; + /// ``` + /// + /// **Option 2: Process chunks with try_next()** + /// ```no_run + /// let mut stream = client.execute_stream_request( + /// Method::GET, + /// "/large-file", + /// None, + /// None, + /// None, + /// ).await?; + /// + /// while let Some(chunk) = stream.try_next().await? { + /// process_chunk(&chunk); + /// } + /// ``` + /// + /// **Option 3: Stream with futures::Stream trait** + /// ```no_run + /// use futures::StreamExt; + /// + /// let stream = client.execute_stream_request( + /// Method::GET, + /// "/large-file", + /// None, + /// None, + /// None, + /// ).await?; + /// + /// let mut file = tokio::fs::File::create("output.mp4").await?; + /// let mut stream = std::pin::pin!(stream); + /// while let Some(chunk) = stream.next().await { + /// let chunk = chunk?; + /// tokio::io::AsyncWriteExt::write_all(&mut file, &chunk).await?; + /// } + /// ``` + pub async fn execute_stream_request( + &self, + method: Method, + path: &str, + body: Option, + query_params: Option>, + options: Option, + ) -> Result { + let url = join_url(&self.config.base_url, path); + let mut request = self.client.request(method, &url); + + // Apply query parameters if provided + if let Some(params) = query_params { + request = request.query(¶ms); + } + + // Apply additional query parameters from options + if let Some(opts) = &options { + if !opts.additional_query_params.is_empty() { + request = request.query(&opts.additional_query_params); + } + } + + // Apply body if provided + if let Some(body) = body { + request = request.json(&body); + } + + // Build the request + let mut req = request.build().map_err(|e| ApiError::Network(e))?; + + // Apply authentication and headers + self.apply_auth_headers(&mut req, &options)?; + self.apply_custom_headers(&mut req, &options)?; + + // Execute with retries + let response = self.execute_with_retries(req, &options).await?; + + // Return streaming response + Ok(ByteStream::new(response)) + } + + /// Execute a request and return an SSE stream + /// + /// This method returns an `SseStream` that automatically parses + /// Server-Sent Events and deserializes the JSON data in each event. + /// + /// # SSE-Specific Headers + /// + /// This method automatically sets the following headers **after** applying custom headers, + /// which means these headers will override any user-supplied values: + /// - `Accept: text/event-stream` - Required for SSE protocol + /// - `Cache-Control: no-store` - Prevents caching of streaming responses + /// + /// This ensures proper SSE behavior even if custom headers are provided. + /// + /// # Example + /// ```no_run + /// use futures::StreamExt; + /// + /// let stream = client.execute_sse_request::( + /// Method::POST, + /// "/stream", + /// Some(serde_json::json!({"query": "Hello"})), + /// None, + /// None, + /// Some("[[DONE]]".to_string()), + /// ).await?; + /// + /// let mut stream = std::pin::pin!(stream); + /// while let Some(chunk) = stream.next().await { + /// let chunk = chunk?; + /// println!("Received: {:?}", chunk); + /// } + /// ``` + #[cfg(feature = "sse")] + pub async fn execute_sse_request( + &self, + method: Method, + path: &str, + body: Option, + query_params: Option>, + options: Option, + terminator: Option, + ) -> Result, ApiError> + where + T: DeserializeOwned + Send + 'static, + { + let url = join_url(&self.config.base_url, path); + let mut request = self.client.request(method, &url); + + // Apply query parameters if provided + if let Some(params) = query_params { + request = request.query(¶ms); + } + + // Apply additional query parameters from options + if let Some(opts) = &options { + if !opts.additional_query_params.is_empty() { + request = request.query(&opts.additional_query_params); + } + } + + // Apply body if provided + if let Some(body) = body { + request = request.json(&body); + } + + // Build the request + let mut req = request.build().map_err(|e| ApiError::Network(e))?; + + // Apply authentication and headers + self.apply_auth_headers(&mut req, &options)?; + self.apply_custom_headers(&mut req, &options)?; + + // SSE-specific headers + req.headers_mut().insert( + "Accept", + "text/event-stream" + .parse() + .map_err(|_| ApiError::InvalidHeader)?, + ); + req.headers_mut().insert( + "Cache-Control", + "no-store".parse().map_err(|_| ApiError::InvalidHeader)?, + ); + + // Execute with retries + let response = self.execute_with_retries(req, &options).await?; + + // Return SSE stream + crate::SseStream::new(response, terminator).await + } +} diff --git a/seed/rust-sdk/nullable-allof-extends/src/core/mod.rs b/seed/rust-sdk/nullable-allof-extends/src/core/mod.rs new file mode 100644 index 000000000000..ba23f97e4acd --- /dev/null +++ b/seed/rust-sdk/nullable-allof-extends/src/core/mod.rs @@ -0,0 +1,15 @@ +//! Core client infrastructure + +mod http_client; +mod query_parameter_builder; +mod request_options; +#[cfg(feature = "sse")] +mod sse_stream; +mod utils; + +pub use http_client::{ByteStream, HttpClient}; +pub use query_parameter_builder::{parse_structured_query, QueryBuilder, QueryBuilderError}; +pub use request_options::RequestOptions; +#[cfg(feature = "sse")] +pub use sse_stream::SseStream; +pub use utils::join_url; diff --git a/seed/rust-sdk/nullable-allof-extends/src/core/pagination.rs b/seed/rust-sdk/nullable-allof-extends/src/core/pagination.rs new file mode 100644 index 000000000000..2cd716dc82a3 --- /dev/null +++ b/seed/rust-sdk/nullable-allof-extends/src/core/pagination.rs @@ -0,0 +1,282 @@ +use std::collections::VecDeque; +use std::future::Future; +use std::pin::Pin; +use std::sync::Arc; +use std::task::{Context, Poll}; + +use futures::Stream; +use serde_json::Value; + +use crate::{ApiError, HttpClient}; + +/// Result of a pagination request +#[derive(Debug)] +pub struct PaginationResult { + pub items: Vec, + pub next_cursor: Option, + pub has_next_page: bool, +} + +/// Async paginator that implements Stream for iterating over paginated results +pub struct AsyncPaginator { + http_client: Arc, + page_loader: Box< + dyn Fn( + Arc, + Option, + ) + -> Pin, ApiError>> + Send>> + + Send + + Sync, + >, + current_page: VecDeque, + current_cursor: Option, + has_next_page: bool, + loading_next: + Option, ApiError>> + Send>>>, +} + +impl AsyncPaginator { + pub fn new( + http_client: Arc, + page_loader: F, + initial_cursor: Option, + ) -> Result + where + F: Fn(Arc, Option) -> Fut + Send + Sync + 'static, + Fut: Future, ApiError>> + Send + 'static, + { + Ok(Self { + http_client, + page_loader: Box::new(move |client, cursor| Box::pin(page_loader(client, cursor))), + current_page: VecDeque::new(), + current_cursor: initial_cursor, + has_next_page: true, // Assume true initially, will be updated after first request + loading_next: None, + }) + } + + /// Check if there are more pages available + pub fn has_next_page(&self) -> bool { + !self.current_page.is_empty() || self.has_next_page + } + + /// Load the next page explicitly + pub async fn next_page(&mut self) -> Result, ApiError> { + if !self.has_next_page { + return Ok(Vec::new()); + } + + let result = + (self.page_loader)(self.http_client.clone(), self.current_cursor.clone()).await?; + + self.current_cursor = result.next_cursor; + self.has_next_page = result.has_next_page; + + Ok(result.items) + } +} + +impl Stream for AsyncPaginator +where + T: Unpin, +{ + type Item = Result; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + // If we have items in the current page, return the next one + if let Some(item) = self.current_page.pop_front() { + return Poll::Ready(Some(Ok(item))); + } + + // If we're already loading the next page, poll that future + if let Some(ref mut loading_future) = self.loading_next { + match loading_future.as_mut().poll(cx) { + Poll::Ready(Ok(result)) => { + self.current_page.extend(result.items); + self.current_cursor = result.next_cursor; + self.has_next_page = result.has_next_page; + self.loading_next = None; + + // Try to get the next item from the newly loaded page + if let Some(item) = self.current_page.pop_front() { + return Poll::Ready(Some(Ok(item))); + } else if !self.has_next_page { + return Poll::Ready(None); + } + // Fall through to start loading next page + } + Poll::Ready(Err(e)) => { + self.loading_next = None; + return Poll::Ready(Some(Err(e))); + } + Poll::Pending => return Poll::Pending, + } + } + + // If we have no more pages to load, we're done + if !self.has_next_page { + return Poll::Ready(None); + } + + // Start loading the next page + let future = (self.page_loader)(self.http_client.clone(), self.current_cursor.clone()); + self.loading_next = Some(future); + + // Poll the future immediately + if let Some(ref mut loading_future) = self.loading_next { + match loading_future.as_mut().poll(cx) { + Poll::Ready(Ok(result)) => { + self.current_page.extend(result.items); + self.current_cursor = result.next_cursor; + self.has_next_page = result.has_next_page; + self.loading_next = None; + + if let Some(item) = self.current_page.pop_front() { + Poll::Ready(Some(Ok(item))) + } else if !self.has_next_page { + Poll::Ready(None) + } else { + // This shouldn't happen, but just in case + cx.waker().wake_by_ref(); + Poll::Pending + } + } + Poll::Ready(Err(e)) => { + self.loading_next = None; + Poll::Ready(Some(Err(e))) + } + Poll::Pending => Poll::Pending, + } + } else { + Poll::Pending + } + } +} + +/// Synchronous paginator for blocking iteration +pub struct SyncPaginator { + http_client: Arc, + page_loader: Box< + dyn Fn(Arc, Option) -> Result, ApiError> + + Send + + Sync, + >, + current_page: VecDeque, + current_cursor: Option, + has_next_page: bool, +} + +impl SyncPaginator { + pub fn new( + http_client: Arc, + page_loader: F, + initial_cursor: Option, + ) -> Result + where + F: Fn(Arc, Option) -> Result, ApiError> + + Send + + Sync + + 'static, + { + Ok(Self { + http_client, + page_loader: Box::new(page_loader), + current_page: VecDeque::new(), + current_cursor: initial_cursor, + has_next_page: true, // Assume true initially + }) + } + + /// Check if there are more pages available + pub fn has_next_page(&self) -> bool { + !self.current_page.is_empty() || self.has_next_page + } + + /// Load the next page explicitly + pub fn next_page(&mut self) -> Result, ApiError> { + if !self.has_next_page { + return Ok(Vec::new()); + } + + let result = (self.page_loader)(self.http_client.clone(), self.current_cursor.clone())?; + + self.current_cursor = result.next_cursor; + self.has_next_page = result.has_next_page; + + Ok(result.items) + } + + /// Get all remaining items by loading all pages + pub fn collect_all(&mut self) -> Result, ApiError> { + let mut all_items = Vec::new(); + + // Add items from current page + while let Some(item) = self.current_page.pop_front() { + all_items.push(item); + } + + // Load all remaining pages + while self.has_next_page { + let page_items = self.next_page()?; + all_items.extend(page_items); + } + + Ok(all_items) + } +} + +impl Iterator for SyncPaginator { + type Item = Result; + + fn next(&mut self) -> Option { + // If we have items in the current page, return the next one + if let Some(item) = self.current_page.pop_front() { + return Some(Ok(item)); + } + + // If we have no more pages to load, we're done + if !self.has_next_page { + return None; + } + + // Load the next page + match (self.page_loader)(self.http_client.clone(), self.current_cursor.clone()) { + Ok(result) => { + self.current_page.extend(result.items); + self.current_cursor = result.next_cursor; + self.has_next_page = result.has_next_page; + + // Return the first item from the newly loaded page + self.current_page.pop_front().map(Ok) + } + Err(e) => Some(Err(e)), + } + } +} + +/// Trait for types that can provide pagination metadata +pub trait Paginated { + /// Extract the items from this page + fn items(&self) -> &[T]; + + /// Get the cursor for the next page, if any + fn next_cursor(&self) -> Option<&str>; + + /// Check if there's a next page available + fn has_next_page(&self) -> bool; +} + +/// Trait for types that can provide offset-based pagination metadata +pub trait OffsetPaginated { + /// Extract the items from this page + fn items(&self) -> &[T]; + + /// Check if there's a next page available + fn has_next_page(&self) -> bool; + + /// Get the current page size (for calculating next offset) + fn page_size(&self) -> usize { + self.items().len() + } +} diff --git a/seed/rust-sdk/nullable-allof-extends/src/core/query_parameter_builder.rs b/seed/rust-sdk/nullable-allof-extends/src/core/query_parameter_builder.rs new file mode 100644 index 000000000000..24e78f885611 --- /dev/null +++ b/seed/rust-sdk/nullable-allof-extends/src/core/query_parameter_builder.rs @@ -0,0 +1,297 @@ +use chrono::{DateTime, Utc}; +use serde::Serialize; + +/// Modern query builder with type-safe method chaining +/// Provides a clean, Swift-like API for building HTTP query parameters +#[derive(Debug, Default)] +pub struct QueryBuilder { + params: Vec<(String, String)>, +} + +impl QueryBuilder { + /// Create a new query parameter builder + pub fn new() -> Self { + Self::default() + } + + /// Add a string parameter (accept both required/optional) + pub fn string(mut self, key: &str, value: impl Into>) -> Self { + if let Some(v) = value.into() { + self.params.push((key.to_string(), v)); + } + self + } + + /// Add multiple string parameters with the same key (for allow-multiple query params) + /// Accepts both Vec and Vec>, adding each non-None value as a separate query parameter + pub fn string_array(mut self, key: &str, values: I) -> Self + where + I: IntoIterator, + T: Into>, + { + for value in values { + if let Some(v) = value.into() { + self.params.push((key.to_string(), v)); + } + } + self + } + + /// Add an integer parameter (accept both required/optional) + pub fn int(mut self, key: &str, value: impl Into>) -> Self { + if let Some(v) = value.into() { + self.params.push((key.to_string(), v.to_string())); + } + self + } + + /// Add a big integer parameter (accept both required/optional) + pub fn big_int(mut self, key: &str, value: impl Into>) -> Self { + if let Some(v) = value.into() { + self.params.push((key.to_string(), v.to_string())); + } + self + } + + /// Add multiple integer parameters with the same key (for allow-multiple query params) + /// Accepts both Vec and Vec>, adding each non-None value as a separate query parameter + pub fn int_array(mut self, key: &str, values: I) -> Self + where + I: IntoIterator, + T: Into>, + { + for value in values { + if let Some(v) = value.into() { + self.params.push((key.to_string(), v.to_string())); + } + } + self + } + + /// Add a float parameter + pub fn float(mut self, key: &str, value: impl Into>) -> Self { + if let Some(v) = value.into() { + self.params.push((key.to_string(), v.to_string())); + } + self + } + + /// Add multiple float parameters with the same key (for allow-multiple query params) + /// Accepts both Vec and Vec>, adding each non-None value as a separate query parameter + pub fn float_array(mut self, key: &str, values: I) -> Self + where + I: IntoIterator, + T: Into>, + { + for value in values { + if let Some(v) = value.into() { + self.params.push((key.to_string(), v.to_string())); + } + } + self + } + + /// Add a boolean parameter + pub fn bool(mut self, key: &str, value: impl Into>) -> Self { + if let Some(v) = value.into() { + self.params.push((key.to_string(), v.to_string())); + } + self + } + + /// Add multiple boolean parameters with the same key (for allow-multiple query params) + /// Accepts both Vec and Vec>, adding each non-None value as a separate query parameter + pub fn bool_array(mut self, key: &str, values: I) -> Self + where + I: IntoIterator, + T: Into>, + { + for value in values { + if let Some(v) = value.into() { + self.params.push((key.to_string(), v.to_string())); + } + } + self + } + + /// Add a datetime parameter + pub fn datetime(mut self, key: &str, value: impl Into>>) -> Self { + if let Some(v) = value.into() { + self.params.push((key.to_string(), v.to_rfc3339())); + } + self + } + + /// Add a UUID parameter (converts to string) + pub fn uuid(mut self, key: &str, value: impl Into>) -> Self { + if let Some(v) = value.into() { + self.params.push((key.to_string(), v.to_string())); + } + self + } + + /// Add a date parameter (converts NaiveDate to DateTime) + pub fn date(mut self, key: &str, value: impl Into>) -> Self { + if let Some(v) = value.into() { + // Convert NaiveDate to DateTime at start of day + let datetime = v.and_hms_opt(0, 0, 0).unwrap().and_utc(); + self.params.push((key.to_string(), datetime.to_rfc3339())); + } + self + } + + /// Add any serializable parameter (for enums and complex types) + pub fn serialize(mut self, key: &str, value: Option) -> Self { + if let Some(v) = value { + // For enums that implement Display, use the Display implementation + // to avoid JSON quotes in query parameters + if let Ok(serialized) = serde_json::to_string(&v) { + // Remove JSON quotes if the value is a simple string + let cleaned = if serialized.starts_with('"') && serialized.ends_with('"') { + serialized.trim_matches('"').to_string() + } else { + serialized + }; + self.params.push((key.to_string(), cleaned)); + } + } + self + } + + /// Add multiple serializable parameters with the same key (for allow-multiple query params with enums) + /// Accepts both Vec and Vec>, adding each non-None value as a separate query parameter + pub fn serialize_array( + mut self, + key: &str, + values: impl IntoIterator, + ) -> Self { + for value in values { + if let Ok(serialized) = serde_json::to_string(&value) { + // Skip null values (from Option::None) + if serialized == "null" { + continue; + } + // Remove JSON quotes if the value is a simple string + let cleaned = if serialized.starts_with('"') && serialized.ends_with('"') { + serialized.trim_matches('"').to_string() + } else { + serialized + }; + self.params.push((key.to_string(), cleaned)); + } + } + self + } + + /// Parse and add a structured query string + /// Handles complex query patterns like: + /// - "key:value" patterns + /// - "key:value1,value2" (comma-separated values) + /// - Quoted values: "key:\"value with spaces\"" + /// - Space-separated terms (treated as AND logic) + pub fn structured_query(mut self, key: &str, value: impl Into>) -> Self { + if let Some(query_str) = value.into() { + if let Ok(parsed_params) = parse_structured_query(&query_str) { + self.params.extend(parsed_params); + } else { + // Fall back to simple query parameter if parsing fails + self.params.push((key.to_string(), query_str)); + } + } + self + } + + /// Build the final query parameters + pub fn build(self) -> Option> { + if self.params.is_empty() { + None + } else { + Some(self.params) + } + } +} + +/// Errors that can occur during structured query parsing +#[derive(Debug)] +pub enum QueryBuilderError { + InvalidQuerySyntax(String), +} + +impl std::fmt::Display for QueryBuilderError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + QueryBuilderError::InvalidQuerySyntax(msg) => { + write!(f, "Invalid query syntax: {}", msg) + } + } + } +} + +impl std::error::Error for QueryBuilderError {} + +/// Parse structured query strings like "key:value key2:value1,value2" +/// Used for complex filtering patterns in APIs like Foxglove +/// +/// Supported patterns: +/// - Simple: "status:active" +/// - Multiple values: "type:sensor,camera" +/// - Quoted values: "location:\"New York\"" +/// - Complex: "status:active type:sensor location:\"San Francisco\"" +pub fn parse_structured_query(query: &str) -> Result, QueryBuilderError> { + let mut params = Vec::new(); + let terms = tokenize_query(query); + + for term in terms { + if let Some((key, values)) = term.split_once(':') { + // Handle comma-separated values + for value in values.split(',') { + let clean_value = value.trim_matches('"'); // Remove quotes + params.push((key.to_string(), clean_value.to_string())); + } + } else { + // For terms without colons, return error to be explicit about expected format + return Err(QueryBuilderError::InvalidQuerySyntax(format!( + "Cannot parse term '{}' - expected 'key:value' format for structured queries", + term + ))); + } + } + + Ok(params) +} + +/// Tokenize a query string, properly handling quoted strings +fn tokenize_query(input: &str) -> Vec { + let mut tokens = Vec::new(); + let mut current_token = String::new(); + let mut in_quotes = false; + let mut chars = input.chars().peekable(); + + while let Some(c) = chars.next() { + match c { + '"' => { + // Toggle quote state and include the quote in the token + in_quotes = !in_quotes; + current_token.push(c); + } + ' ' if !in_quotes => { + // Space outside quotes - end current token + if !current_token.is_empty() { + tokens.push(current_token.trim().to_string()); + current_token.clear(); + } + } + _ => { + // Any other character (including spaces inside quotes) + current_token.push(c); + } + } + } + + // Add the last token if there is one + if !current_token.is_empty() { + tokens.push(current_token.trim().to_string()); + } + + tokens +} diff --git a/seed/rust-sdk/nullable-allof-extends/src/core/request_options.rs b/seed/rust-sdk/nullable-allof-extends/src/core/request_options.rs new file mode 100644 index 000000000000..dbd4d60d4940 --- /dev/null +++ b/seed/rust-sdk/nullable-allof-extends/src/core/request_options.rs @@ -0,0 +1,58 @@ +use std::collections::HashMap; +/// Options for customizing individual requests +#[derive(Debug, Clone, Default)] +pub struct RequestOptions { + /// API key for authentication (overrides client-level API key) + pub api_key: Option, + /// Bearer token for authentication (overrides client-level token) + pub token: Option, + /// Maximum number of retry attempts for failed requests + pub max_retries: Option, + /// Request timeout in seconds (overrides client-level timeout) + pub timeout_seconds: Option, + /// Additional headers to include in the request + pub additional_headers: HashMap, + /// Additional query parameters to include in the request + pub additional_query_params: HashMap, +} + +impl RequestOptions { + pub fn new() -> Self { + Self::default() + } + + pub fn api_key(mut self, key: impl Into) -> Self { + self.api_key = Some(key.into()); + self + } + + pub fn token(mut self, token: impl Into) -> Self { + self.token = Some(token.into()); + self + } + + pub fn max_retries(mut self, retries: u32) -> Self { + self.max_retries = Some(retries); + self + } + + pub fn timeout_seconds(mut self, timeout: u64) -> Self { + self.timeout_seconds = Some(timeout); + self + } + + pub fn additional_header(mut self, key: impl Into, value: impl Into) -> Self { + self.additional_headers.insert(key.into(), value.into()); + self + } + + pub fn additional_query_param( + mut self, + key: impl Into, + value: impl Into, + ) -> Self { + self.additional_query_params + .insert(key.into(), value.into()); + self + } +} diff --git a/seed/rust-sdk/nullable-allof-extends/src/core/sse_stream.rs b/seed/rust-sdk/nullable-allof-extends/src/core/sse_stream.rs new file mode 100644 index 000000000000..50a375e0a7ff --- /dev/null +++ b/seed/rust-sdk/nullable-allof-extends/src/core/sse_stream.rs @@ -0,0 +1,281 @@ +use crate::ApiError; +use futures::Stream; +use pin_project::pin_project; +use reqwest::{header::CONTENT_TYPE, Response}; +use reqwest_sse::{error::EventError, Event, EventSource}; +use serde::de::DeserializeOwned; +use std::{ + marker::PhantomData, + pin::Pin, + task::{Context, Poll}, +}; + +/// Metadata from a Server-Sent Event +/// +/// Contains the SSE protocol fields (event, id, retry) that accompany the data payload. +/// This struct provides access to SSE metadata for advanced use cases. +/// +/// Per the SSE specification: +/// - `event` defaults to "message" when not specified by the server +/// - `id` is optional and used for reconnection support (Last-Event-ID header) +/// - `retry` is optional and specifies reconnection timeout in milliseconds +#[derive(Debug, Clone)] +pub struct SseMetadata { + /// The event type (defaults to "message" per SSE spec if not specified by server) + pub event: String, + /// The event ID for reconnection support (None if not specified) + pub id: Option, + /// Retry timeout in milliseconds (None if not specified) + pub retry: Option, +} + +/// A Server-Sent Event with both data and metadata +/// +/// Contains the deserialized data payload along with SSE protocol metadata. +/// Use this when you need access to event IDs, event types, or retry information. +#[derive(Debug)] +pub struct SseEvent { + /// The deserialized data payload + pub data: T, + /// SSE protocol metadata + pub metadata: SseMetadata, +} + +/// A type-safe wrapper around Server-Sent Events (SSE) streams +/// +/// Leverages `reqwest-sse` for SSE protocol parsing and adds: +/// - Automatic JSON deserialization to typed structs +/// - Stream terminator support (e.g., `[DONE]` for OpenAI-style APIs) +/// - Integrated error handling with `ApiError` +/// - Content-Type validation (`text/event-stream` required) +/// +/// # Charset Handling +/// +/// The `reqwest-sse` library automatically handles charset detection and decoding +/// based on the Content-Type header. If no charset is specified, UTF-8 is assumed. +/// This matches the SSE specification default behavior. +/// +/// # Example +/// +/// Basic usage with async iteration: +/// +/// ```no_run +/// use futures::StreamExt; +/// +/// let stream: SseStream = client.stream_completions(request).await?; +/// let mut stream = std::pin::pin!(stream); +/// +/// while let Some(result) = stream.next().await { +/// match result { +/// Ok(chunk) => println!("Received: {:?}", chunk), +/// Err(e) => eprintln!("Error: {}", e), +/// } +/// } +/// ``` +/// +/// # Error Handling +/// +/// The stream yields `Result` items. Errors can occur from: +/// - Invalid JSON in SSE data field (`ApiError::Serialization`) +/// - SSE protocol errors (`ApiError::SseParseError`) +/// - Network errors during streaming +/// +/// **Important:** When an error occurs for a single event (e.g., malformed JSON), +/// the stream yields `Err` for that item but **continues streaming** subsequent events. +/// The stream only ends when: +/// - A terminator is received (if configured) +/// - The server closes the connection +/// - A fatal network error occurs +/// +/// This allows the client to handle per-event errors gracefully without losing +/// the entire stream. Compare this to other error handling strategies where a single +/// bad event might terminate the stream. +/// +/// # Terminator Support +/// +/// When a terminator string is specified (e.g., `[DONE]`), the stream automatically +/// ends when an SSE event with that exact data is received. The terminator event +/// itself is not yielded to the consumer. +#[pin_project] +pub struct SseStream { + #[pin] + inner: Pin> + Send>>, + terminator: Option, + _phantom: PhantomData, +} + +impl SseStream +where + T: DeserializeOwned, +{ + /// Create a new SSE stream from a Response + /// + /// # Arguments + /// * `response` - The HTTP response to parse as SSE + /// * `terminator` - Optional terminator string (e.g., `"[DONE]"`) that signals end of stream + /// + /// # Errors + /// Returns `ApiError::SseParseError` if: + /// - Response Content-Type is not `text/event-stream` + /// - SSE stream cannot be created from response + pub(crate) async fn new( + response: Response, + terminator: Option, + ) -> Result { + // Validate Content-Type header (case-insensitive, handles parameters) + let content_type = response + .headers() + .get(CONTENT_TYPE) + .and_then(|v| v.to_str().ok()) + .unwrap_or(""); + + // Extract main content type (before ';' parameter separator) and compare case-insensitively + let content_type_main = content_type.split(';').next().unwrap_or("").trim(); + + if !content_type_main.eq_ignore_ascii_case("text/event-stream") { + return Err(ApiError::SseParseError(format!( + "Expected Content-Type to be 'text/event-stream', got '{}'", + content_type + ))); + } + + // Use reqwest-sse's EventSource trait to get SSE stream + let events = response + .events() + .await + .map_err(|e| ApiError::SseParseError(e.to_string()))?; + + Ok(Self { + inner: Box::pin(events), + terminator, + _phantom: PhantomData, + }) + } +} + +impl SseStream +where + T: DeserializeOwned, +{ + /// Convert this stream into one that yields events with metadata + /// + /// This consumes the stream and returns a new stream that yields `SseEvent` + /// instead of just `T`, providing access to SSE metadata (event type, id, retry). + /// + /// # Example + /// + /// ```no_run + /// use futures::StreamExt; + /// + /// let stream = client.stream_completions(request).await?; + /// let mut stream_with_metadata = stream.with_metadata(); + /// let mut stream_with_metadata = std::pin::pin!(stream_with_metadata); + /// + /// while let Some(result) = stream_with_metadata.next().await { + /// match result { + /// Ok(event) => { + /// println!("Data: {:?}", event.data); + /// println!("Event type: {}", event.metadata.event); + /// if let Some(id) = &event.metadata.id { + /// println!("Event ID: {}", id); + /// } + /// } + /// Err(e) => eprintln!("Error: {}", e), + /// } + /// } + /// ``` + pub fn with_metadata(self) -> SseStreamWithMetadata { + SseStreamWithMetadata { inner: self } + } +} + +impl Stream for SseStream +where + T: DeserializeOwned, +{ + type Item = Result; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + match this.inner.poll_next(cx) { + Poll::Ready(Some(Ok(event))) => { + // Check for terminator before parsing + if let Some(ref terminator) = this.terminator { + if event.data == *terminator { + // Terminator found - end stream cleanly + return Poll::Ready(None); + } + } + + // Deserialize JSON data to typed struct + match serde_json::from_str(&event.data) { + Ok(value) => Poll::Ready(Some(Ok(value))), + Err(e) => Poll::Ready(Some(Err(ApiError::Serialization(e)))), + } + } + Poll::Ready(Some(Err(e))) => { + Poll::Ready(Some(Err(ApiError::SseParseError(e.to_string())))) + } + Poll::Ready(None) => Poll::Ready(None), + Poll::Pending => Poll::Pending, + } + } +} + +/// Stream wrapper that yields events with metadata +/// +/// Created by calling [`SseStream::with_metadata()`]. This stream yields `SseEvent` +/// which includes both the deserialized data and SSE protocol metadata. +#[pin_project] +pub struct SseStreamWithMetadata { + #[pin] + inner: SseStream, +} + +impl Stream for SseStreamWithMetadata +where + T: DeserializeOwned, +{ + type Item = Result, ApiError>; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + + // Access the inner stream's fields through pin projection + let inner_pin = this.inner.project(); + + match inner_pin.inner.poll_next(cx) { + Poll::Ready(Some(Ok(event))) => { + // Check for terminator + if let Some(ref terminator) = inner_pin.terminator { + if event.data == *terminator { + return Poll::Ready(None); + } + } + + // Extract metadata + let metadata = SseMetadata { + // Default to "message" if event type is empty (per SSE spec) + event: if event.event_type.is_empty() { + "message".to_string() + } else { + event.event_type.clone() + }, + id: event.last_event_id.clone(), + retry: event.retry.map(|d| d.as_millis() as u64), + }; + + // Deserialize JSON data + match serde_json::from_str(&event.data) { + Ok(data) => Poll::Ready(Some(Ok(SseEvent { data, metadata }))), + Err(e) => Poll::Ready(Some(Err(ApiError::Serialization(e)))), + } + } + Poll::Ready(Some(Err(e))) => { + Poll::Ready(Some(Err(ApiError::SseParseError(e.to_string())))) + } + Poll::Ready(None) => Poll::Ready(None), + Poll::Pending => Poll::Pending, + } + } +} diff --git a/seed/rust-sdk/nullable-allof-extends/src/core/utils.rs b/seed/rust-sdk/nullable-allof-extends/src/core/utils.rs new file mode 100644 index 000000000000..808056dc34a2 --- /dev/null +++ b/seed/rust-sdk/nullable-allof-extends/src/core/utils.rs @@ -0,0 +1,19 @@ +/// URL building utilities +/// Safely join a base URL with a path, handling slashes properly +/// +/// # Examples +/// ``` +/// use example_api::utils::url::join_url; +/// +/// assert_eq!(join_url("https://api.example.com", "users"), "https://api.example.com/users"); +/// assert_eq!(join_url("https://api.example.com/", "users"), "https://api.example.com/users"); +/// assert_eq!(join_url("https://api.example.com", "/users"), "https://api.example.com/users"); +/// assert_eq!(join_url("https://api.example.com/", "/users"), "https://api.example.com/users"); +/// ``` +pub fn join_url(base_url: &str, path: &str) -> String { + format!( + "{}/{}", + base_url.trim_end_matches('/'), + path.trim_start_matches('/') + ) +} diff --git a/seed/rust-sdk/nullable-allof-extends/src/environment.rs b/seed/rust-sdk/nullable-allof-extends/src/environment.rs new file mode 100644 index 000000000000..9034ff803954 --- /dev/null +++ b/seed/rust-sdk/nullable-allof-extends/src/environment.rs @@ -0,0 +1,19 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +pub enum Environment { + #[serde(rename = "default")] + Default, +} +impl Environment { + pub fn url(&self) -> &'static str { + match self { + Self::Default => "https://api.example.com", + } + } +} +impl Default for Environment { + fn default() -> Self { + Self::Default + } +} diff --git a/seed/rust-sdk/nullable-allof-extends/src/error.rs b/seed/rust-sdk/nullable-allof-extends/src/error.rs new file mode 100644 index 000000000000..7feca158a561 --- /dev/null +++ b/seed/rust-sdk/nullable-allof-extends/src/error.rs @@ -0,0 +1,32 @@ +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum ApiError { + #[error("HTTP error {status}: {message}")] + Http { status: u16, message: String }, + #[error("Network error: {0}")] + Network(reqwest::Error), + #[error("Serialization error: {0}")] + Serialization(serde_json::Error), + #[error("Configuration error: {0}")] + Configuration(String), + #[error("Invalid header value")] + InvalidHeader, + #[error("Could not clone request for retry")] + RequestClone, + #[error("SSE stream terminated")] + StreamTerminated, + #[error("SSE parse error: {0}")] + SseParseError(String), +} + +impl ApiError { + pub fn from_response(status_code: u16, body: Option<&str>) -> Self { + match status_code { + _ => Self::Http { + status: status_code, + message: body.unwrap_or("Unknown error").to_string(), + }, + } + } +} diff --git a/seed/rust-sdk/nullable-allof-extends/src/lib.rs b/seed/rust-sdk/nullable-allof-extends/src/lib.rs new file mode 100644 index 000000000000..1df2bd971ad5 --- /dev/null +++ b/seed/rust-sdk/nullable-allof-extends/src/lib.rs @@ -0,0 +1,42 @@ +//! # Nullable AllOf Extends Test SDK +//! +//! The official Rust SDK for the Nullable AllOf Extends Test. +//! +//! ## Getting Started +//! +//! ```rust +//! use seed_api::prelude::*; +//! +//! #[tokio::main] +//! async fn main() { +//! let config = ClientConfig { +//! ..Default::default() +//! }; +//! let client = ApiClient::new(config).expect("Failed to build client"); +//! client.get_test(None).await; +//! } +//! ``` +//! +//! ## Modules +//! +//! - [`api`] - Core API types and models +//! - [`client`] - Client implementations +//! - [`config`] - Configuration options +//! - [`core`] - Core utilities and infrastructure +//! - [`error`] - Error types and handling +//! - [`prelude`] - Common imports for convenience + +pub mod api; +pub mod client; +pub mod config; +pub mod core; +pub mod environment; +pub mod error; +pub mod prelude; + +pub use api::*; +pub use client::*; +pub use config::*; +pub use core::*; +pub use environment::*; +pub use error::ApiError; diff --git a/seed/rust-sdk/nullable-allof-extends/src/prelude.rs b/seed/rust-sdk/nullable-allof-extends/src/prelude.rs new file mode 100644 index 000000000000..b92b2b0155ef --- /dev/null +++ b/seed/rust-sdk/nullable-allof-extends/src/prelude.rs @@ -0,0 +1,19 @@ +//! Prelude module for convenient imports +//! +//! This module re-exports the most commonly used types and traits. +//! Import it with: `use seed_api::prelude::*;` + +// Client and configuration +pub use crate::config::ClientConfig; +pub use crate::core::{HttpClient, RequestOptions}; +pub use crate::error::ApiError; + +// Main client and resource clients +pub use crate::api::*; + +// Re-export commonly used external types +pub use ordered_float::OrderedFloat; +pub use serde::{Deserialize, Serialize}; +pub use serde_json::{json, Value}; +pub use std::collections::{HashMap, HashSet}; +pub use std::fmt; diff --git a/seed/rust-sdk/oauth-client-credentials-mandatory-auth/.fern/metadata.json b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/.fern/metadata.json new file mode 100644 index 000000000000..ce1c0cd76077 --- /dev/null +++ b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/.fern/metadata.json @@ -0,0 +1,5 @@ +{ + "cliVersion": "DUMMY", + "generatorName": "fernapi/fern-rust-sdk", + "generatorVersion": "latest" +} \ No newline at end of file diff --git a/seed/rust-sdk/oauth-client-credentials-mandatory-auth/.github/workflows/ci.yml b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/.github/workflows/ci.yml new file mode 100644 index 000000000000..1a311af94152 --- /dev/null +++ b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/.github/workflows/ci.yml @@ -0,0 +1,59 @@ +name: ci + +on: [push] + +env: + RUSTFLAGS: "-A warnings" + +jobs: + check: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Rust + uses: actions-rust-lang/setup-rust-toolchain@v1 + + - name: Check + run: cargo check + + compile: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Rust + uses: actions-rust-lang/setup-rust-toolchain@v1 + + - name: Compile + run: cargo build + + test: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Rust + uses: actions-rust-lang/setup-rust-toolchain@v1 + + - name: Test + run: cargo test + + publish: + needs: [check, compile, test] + if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Rust + uses: actions-rust-lang/setup-rust-toolchain@v1 + + - name: Publish + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + run: cargo publish \ No newline at end of file diff --git a/seed/rust-sdk/oauth-client-credentials-mandatory-auth/.gitignore b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/.gitignore new file mode 100644 index 000000000000..f75d27799da1 --- /dev/null +++ b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/.gitignore @@ -0,0 +1,5 @@ +/target +**/*.rs.bk +Cargo.lock +.DS_Store +*.swp \ No newline at end of file diff --git a/seed/rust-sdk/oauth-client-credentials-mandatory-auth/Cargo.toml b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/Cargo.toml new file mode 100644 index 000000000000..9ba39f039340 --- /dev/null +++ b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "seed_oauth_client_credentials_mandatory_auth" +version = "0.0.1" +edition = "2021" +description = "Rust SDK for seed_oauth_client_credentials_mandatory_auth generated by Fern" +license = "MIT" +repository = "https://github.com/fern-api/fern" +documentation = "https://docs.rs/seed_oauth_client_credentials_mandatory_auth" + +[lib] +doctest = false + +[dependencies] +bytes = "1.0" +chrono = { version = "0.4", features = ["serde"] } +futures = "0.3" +num-bigint = { version = "0.4", features = ["serde"] } +ordered-float = { version = "4.5", features = ["serde"] } +percent-encoding = "2.3" +reqwest = { version = "0.12", features = ["json", "stream"], default-features = false } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +thiserror = "1.0" +tokio = { version = "1.0", features = ["full"] } +uuid = { version = "1.0", features = ["serde"] } + +[dev-dependencies] +tokio-test = "0.4" + +[features] +multipart = [] +sse = [] diff --git a/seed/rust-sdk/oauth-client-credentials-mandatory-auth/README.md b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/README.md new file mode 100644 index 000000000000..03f22629cba1 --- /dev/null +++ b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/README.md @@ -0,0 +1,171 @@ +# Seed Rust Library + +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Seed%2FRust) +[![crates.io shield](https://img.shields.io/crates/v/seed_oauth_client_credentials_mandatory_auth)](https://crates.io/crates/seed_oauth_client_credentials_mandatory_auth) + +The Seed Rust library provides convenient access to the Seed APIs from Rust. + +## Table of Contents + +- [Installation](#installation) +- [Reference](#reference) +- [Usage](#usage) +- [Errors](#errors) +- [Request Types](#request-types) +- [Advanced](#advanced) + - [Retries](#retries) + - [Timeouts](#timeouts) + - [Additional Headers](#additional-headers) + - [Additional Query String Parameters](#additional-query-string-parameters) +- [Contributing](#contributing) + +## Installation + +Add this to your `Cargo.toml`: + +```toml +[dependencies] +seed_oauth_client_credentials_mandatory_auth = "0.0.1" +``` + +Or install via cargo: + +```sh +cargo add seed_oauth_client_credentials_mandatory_auth +``` + +## Reference + +A full reference for this library is available [here](./reference.md). + +## Usage + +Instantiate and use the client with the following: + +```rust +use seed_oauth_client_credentials_mandatory_auth::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + ..Default::default() + }; + let client = + OauthClientCredentialsMandatoryAuthClient::new(config).expect("Failed to build client"); + client + .auth + .get_token_with_client_credentials( + &GetTokenRequest { + client_id: "my_oauth_app_123".to_string(), + client_secret: "sk_live_abcdef123456789".to_string(), + audience: "https://api.example.com".to_string(), + grant_type: "client_credentials".to_string(), + scope: Some("read:users".to_string()), + }, + None, + ) + .await; +} +``` + +## Errors + +When the API returns a non-success status code (4xx or 5xx response), an error will be returned. + +```rust +match client.auth.get_token_with_client_credentials(None)?.await { + Ok(response) => { + println!("Success: {:?}", response); + }, + Err(ApiError::HTTP { status, message }) => { + println!("API Error {}: {:?}", status, message); + }, + Err(e) => { + println!("Other error: {:?}", e); + } +} +``` + +## Request Types + +The SDK exports all request types as Rust structs. Simply import them from the crate to access them: + +```rust +use seed_oauth_client_credentials_mandatory_auth::prelude::{*}; + +let request = GetTokenRequest { + ... +}; +``` + +## Advanced + +### Retries + +The SDK is instrumented with automatic retries with exponential backoff. A request will be retried as long +as the request is deemed retryable and the number of retry attempts has not grown larger than the configured +retry limit (default: 2). + +A request is deemed retryable when any of the following HTTP status codes is returned: + +- [408](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/408) (Timeout) +- [429](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429) (Too Many Requests) +- [5XX](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500) (Internal Server Errors) + +Use the `max_retries` method to configure this behavior. + +```rust +let response = client.auth.get_token_with_client_credentials( + Some(RequestOptions::new().max_retries(3)) +)?.await; +``` + +### Timeouts + +The SDK defaults to a 30 second timeout. Use the `timeout` method to configure this behavior. + +```rust +let response = client.auth.get_token_with_client_credentials( + Some(RequestOptions::new().timeout_seconds(30)) +)?.await; +``` + +### Additional Headers + +You can add custom headers to requests using `RequestOptions`. + +```rust +let response = client.auth.get_token_with_client_credentials( + Some( + RequestOptions::new() + .additional_header("X-Custom-Header", "custom-value") + .additional_header("X-Another-Header", "another-value") + ) +)? +.await; +``` + +### Additional Query String Parameters + +You can add custom query parameters to requests using `RequestOptions`. + +```rust +let response = client.auth.get_token_with_client_credentials( + Some( + RequestOptions::new() + .additional_query_param("filter", "active") + .additional_query_param("sort", "desc") + ) +)? +.await; +``` + +## Contributing + +While we value open-source contributions to this SDK, this library is generated programmatically. +Additions made directly to this library would have to be moved over to our generation code, +otherwise they would be overwritten upon the next generated release. Feel free to open a PR as +a proof of concept, but know that we will not be able to merge it as-is. We suggest opening +an issue first to discuss with us! + +On the other hand, contributions to the README are always very welcome! \ No newline at end of file diff --git a/seed/rust-sdk/oauth-client-credentials-mandatory-auth/dynamic-snippets/example0.rs b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/dynamic-snippets/example0.rs new file mode 100644 index 000000000000..664a19f4b0d0 --- /dev/null +++ b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/dynamic-snippets/example0.rs @@ -0,0 +1,24 @@ +use seed_oauth_client_credentials_mandatory_auth::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + base_url: "https://api.fern.com".to_string(), + ..Default::default() + }; + let client = + OauthClientCredentialsMandatoryAuthClient::new(config).expect("Failed to build client"); + client + .auth + .get_token_with_client_credentials( + &GetTokenRequest { + client_id: "my_oauth_app_123".to_string(), + client_secret: "sk_live_abcdef123456789".to_string(), + audience: "https://api.example.com".to_string(), + grant_type: "client_credentials".to_string(), + scope: Some("read:users".to_string()), + }, + None, + ) + .await; +} diff --git a/seed/rust-sdk/oauth-client-credentials-mandatory-auth/dynamic-snippets/example1.rs b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/dynamic-snippets/example1.rs new file mode 100644 index 000000000000..bdaf0ea7e7cd --- /dev/null +++ b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/dynamic-snippets/example1.rs @@ -0,0 +1,24 @@ +use seed_oauth_client_credentials_mandatory_auth::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + base_url: "https://api.fern.com".to_string(), + ..Default::default() + }; + let client = + OauthClientCredentialsMandatoryAuthClient::new(config).expect("Failed to build client"); + client + .auth + .get_token_with_client_credentials( + &GetTokenRequest { + client_id: "client_id".to_string(), + client_secret: "client_secret".to_string(), + audience: "https://api.example.com".to_string(), + grant_type: "client_credentials".to_string(), + scope: Some("scope".to_string()), + }, + None, + ) + .await; +} diff --git a/seed/rust-sdk/oauth-client-credentials-mandatory-auth/dynamic-snippets/example2.rs b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/dynamic-snippets/example2.rs new file mode 100644 index 000000000000..2ecc089dc796 --- /dev/null +++ b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/dynamic-snippets/example2.rs @@ -0,0 +1,25 @@ +use seed_oauth_client_credentials_mandatory_auth::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + base_url: "https://api.fern.com".to_string(), + ..Default::default() + }; + let client = + OauthClientCredentialsMandatoryAuthClient::new(config).expect("Failed to build client"); + client + .auth + .refresh_token( + &RefreshTokenRequest { + client_id: "my_oauth_app_123".to_string(), + client_secret: "sk_live_abcdef123456789".to_string(), + refresh_token: "refresh_token".to_string(), + audience: "https://api.example.com".to_string(), + grant_type: "refresh_token".to_string(), + scope: Some("read:users".to_string()), + }, + None, + ) + .await; +} diff --git a/seed/rust-sdk/oauth-client-credentials-mandatory-auth/dynamic-snippets/example3.rs b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/dynamic-snippets/example3.rs new file mode 100644 index 000000000000..1dc8767bb7a5 --- /dev/null +++ b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/dynamic-snippets/example3.rs @@ -0,0 +1,25 @@ +use seed_oauth_client_credentials_mandatory_auth::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + base_url: "https://api.fern.com".to_string(), + ..Default::default() + }; + let client = + OauthClientCredentialsMandatoryAuthClient::new(config).expect("Failed to build client"); + client + .auth + .refresh_token( + &RefreshTokenRequest { + client_id: "client_id".to_string(), + client_secret: "client_secret".to_string(), + refresh_token: "refresh_token".to_string(), + audience: "https://api.example.com".to_string(), + grant_type: "refresh_token".to_string(), + scope: Some("scope".to_string()), + }, + None, + ) + .await; +} diff --git a/seed/rust-sdk/oauth-client-credentials-mandatory-auth/dynamic-snippets/example4.rs b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/dynamic-snippets/example4.rs new file mode 100644 index 000000000000..bbb5e9b8c07a --- /dev/null +++ b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/dynamic-snippets/example4.rs @@ -0,0 +1,12 @@ +use seed_oauth_client_credentials_mandatory_auth::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + base_url: "https://api.fern.com".to_string(), + ..Default::default() + }; + let client = + OauthClientCredentialsMandatoryAuthClient::new(config).expect("Failed to build client"); + client.nested.api.get_something(None).await; +} diff --git a/seed/rust-sdk/oauth-client-credentials-mandatory-auth/dynamic-snippets/example5.rs b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/dynamic-snippets/example5.rs new file mode 100644 index 000000000000..1e62aff84bbe --- /dev/null +++ b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/dynamic-snippets/example5.rs @@ -0,0 +1,12 @@ +use seed_oauth_client_credentials_mandatory_auth::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + base_url: "https://api.fern.com".to_string(), + ..Default::default() + }; + let client = + OauthClientCredentialsMandatoryAuthClient::new(config).expect("Failed to build client"); + client.simple.get_something(None).await; +} diff --git a/seed/rust-sdk/oauth-client-credentials-mandatory-auth/reference.md b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/reference.md new file mode 100644 index 000000000000..8de76e15f8ef --- /dev/null +++ b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/reference.md @@ -0,0 +1,270 @@ +# Reference +## Auth +
client.auth.get_token_with_client_credentials(request: GetTokenRequest) -> Result +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```rust +use seed_oauth_client_credentials_mandatory_auth::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + ..Default::default() + }; + let client = + OauthClientCredentialsMandatoryAuthClient::new(config).expect("Failed to build client"); + client + .auth + .get_token_with_client_credentials( + &GetTokenRequest { + client_id: "my_oauth_app_123".to_string(), + client_secret: "sk_live_abcdef123456789".to_string(), + audience: "https://api.example.com".to_string(), + grant_type: "client_credentials".to_string(), + scope: Some("read:users".to_string()), + }, + None, + ) + .await; +} +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**client_id:** `String` + +
+
+ +
+
+ +**client_secret:** `String` + +
+
+ +
+
+ +**audience:** `String` + +
+
+ +
+
+ +**grant_type:** `String` + +
+
+ +
+
+ +**scope:** `Option` + +
+
+
+
+ + +
+
+
+ +
client.auth.refresh_token(request: RefreshTokenRequest) -> Result +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```rust +use seed_oauth_client_credentials_mandatory_auth::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + ..Default::default() + }; + let client = + OauthClientCredentialsMandatoryAuthClient::new(config).expect("Failed to build client"); + client + .auth + .refresh_token( + &RefreshTokenRequest { + client_id: "my_oauth_app_123".to_string(), + client_secret: "sk_live_abcdef123456789".to_string(), + refresh_token: "refresh_token".to_string(), + audience: "https://api.example.com".to_string(), + grant_type: "refresh_token".to_string(), + scope: Some("read:users".to_string()), + }, + None, + ) + .await; +} +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**client_id:** `String` + +
+
+ +
+
+ +**client_secret:** `String` + +
+
+ +
+
+ +**refresh_token:** `String` + +
+
+ +
+
+ +**audience:** `String` + +
+
+ +
+
+ +**grant_type:** `String` + +
+
+ +
+
+ +**scope:** `Option` + +
+
+
+
+ + +
+
+
+ +## Nested Api +
client.nested().api.get_something() -> Result<(), ApiError> +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```rust +use seed_oauth_client_credentials_mandatory_auth::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + ..Default::default() + }; + let client = + OauthClientCredentialsMandatoryAuthClient::new(config).expect("Failed to build client"); + client.nested.api.get_something(None).await; +} +``` +
+
+
+
+ + +
+
+
+ +## Simple +
client.simple.get_something() -> Result<(), ApiError> +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```rust +use seed_oauth_client_credentials_mandatory_auth::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + ..Default::default() + }; + let client = + OauthClientCredentialsMandatoryAuthClient::new(config).expect("Failed to build client"); + client.simple.get_something(None).await; +} +``` +
+
+
+
+ + +
+
+
diff --git a/seed/rust-sdk/oauth-client-credentials-mandatory-auth/rustfmt.toml b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/rustfmt.toml new file mode 100644 index 000000000000..872221fb31fe --- /dev/null +++ b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/rustfmt.toml @@ -0,0 +1,4 @@ +# Generated by Fern +edition = "2021" +max_width = 100 +use_small_heuristics = "Default" \ No newline at end of file diff --git a/seed/rust-sdk/oauth-client-credentials-mandatory-auth/snippet.json b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/api/mod.rs b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/api/mod.rs new file mode 100644 index 000000000000..f46de96c4512 --- /dev/null +++ b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/api/mod.rs @@ -0,0 +1,17 @@ +//! API client and types for the OauthClientCredentialsMandatoryAuth +//! +//! This module contains all the API definitions including request/response types +//! and client implementations for interacting with the API. +//! +//! ## Modules +//! +//! - [`resources`] - Service clients and endpoints +//! - [`types`] - Request, response, and model types + +pub mod resources; +pub mod types; + +pub use resources::{ + AuthClient, NestedClient, OauthClientCredentialsMandatoryAuthClient, SimpleClient, +}; +pub use types::*; diff --git a/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/api/resources/auth/auth.rs b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/api/resources/auth/auth.rs new file mode 100644 index 000000000000..8c373956f3e6 --- /dev/null +++ b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/api/resources/auth/auth.rs @@ -0,0 +1,47 @@ +use crate::api::*; +use crate::{ApiError, ClientConfig, HttpClient, RequestOptions}; +use reqwest::Method; + +pub struct AuthClient { + pub http_client: HttpClient, +} + +impl AuthClient { + pub fn new(config: ClientConfig) -> Result { + Ok(Self { + http_client: HttpClient::new(config.clone())?, + }) + } + + pub async fn get_token_with_client_credentials( + &self, + request: &GetTokenRequest, + options: Option, + ) -> Result { + self.http_client + .execute_request( + Method::POST, + "/token", + Some(serde_json::to_value(request).unwrap_or_default()), + None, + options, + ) + .await + } + + pub async fn refresh_token( + &self, + request: &RefreshTokenRequest, + options: Option, + ) -> Result { + self.http_client + .execute_request( + Method::POST, + "/token", + Some(serde_json::to_value(request).unwrap_or_default()), + None, + options, + ) + .await + } +} diff --git a/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/api/resources/auth/mod.rs b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/api/resources/auth/mod.rs new file mode 100644 index 000000000000..c5d7cba0e123 --- /dev/null +++ b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/api/resources/auth/mod.rs @@ -0,0 +1,2 @@ +pub mod auth; +pub use auth::AuthClient; diff --git a/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/api/resources/mod.rs b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/api/resources/mod.rs new file mode 100644 index 000000000000..6cac8e4b2176 --- /dev/null +++ b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/api/resources/mod.rs @@ -0,0 +1,34 @@ +//! Service clients and API endpoints +//! +//! This module contains client implementations for: +//! +//! - **Auth** +//! - **Nested** +//! - **Simple** + +use crate::{ApiError, ClientConfig}; + +pub mod auth; +pub mod nested; +pub mod simple; +pub struct OauthClientCredentialsMandatoryAuthClient { + pub config: ClientConfig, + pub auth: AuthClient, + pub nested: NestedClient, + pub simple: SimpleClient, +} + +impl OauthClientCredentialsMandatoryAuthClient { + pub fn new(config: ClientConfig) -> Result { + Ok(Self { + config: config.clone(), + auth: AuthClient::new(config.clone())?, + nested: NestedClient::new(config.clone())?, + simple: SimpleClient::new(config.clone())?, + }) + } +} + +pub use auth::AuthClient; +pub use nested::NestedClient; +pub use simple::SimpleClient; diff --git a/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/api/resources/nested/api/mod.rs b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/api/resources/nested/api/mod.rs new file mode 100644 index 000000000000..38417a1b17ee --- /dev/null +++ b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/api/resources/nested/api/mod.rs @@ -0,0 +1,2 @@ +pub mod nested_api; +pub use nested_api::ApiClient; diff --git a/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/api/resources/nested/api/nested_api.rs b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/api/resources/nested/api/nested_api.rs new file mode 100644 index 000000000000..1bf352f05b0b --- /dev/null +++ b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/api/resources/nested/api/nested_api.rs @@ -0,0 +1,20 @@ +use crate::{ApiError, ClientConfig, HttpClient, RequestOptions}; +use reqwest::Method; + +pub struct ApiClient { + pub http_client: HttpClient, +} + +impl ApiClient { + pub fn new(config: ClientConfig) -> Result { + Ok(Self { + http_client: HttpClient::new(config.clone())?, + }) + } + + pub async fn get_something(&self, options: Option) -> Result<(), ApiError> { + self.http_client + .execute_request(Method::GET, "/nested/get-something", None, None, options) + .await + } +} diff --git a/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/api/resources/nested/mod.rs b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/api/resources/nested/mod.rs new file mode 100644 index 000000000000..8bafd63fc68f --- /dev/null +++ b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/api/resources/nested/mod.rs @@ -0,0 +1,16 @@ +use crate::{ApiError, ClientConfig, HttpClient}; + +pub mod api; +pub use api::ApiClient; +pub struct NestedClient { + pub http_client: HttpClient, + pub api: ApiClient, +} +impl NestedClient { + pub fn new(config: ClientConfig) -> Result { + Ok(Self { + http_client: HttpClient::new(config.clone())?, + api: ApiClient::new(config.clone())?, + }) + } +} diff --git a/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/api/resources/simple/mod.rs b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/api/resources/simple/mod.rs new file mode 100644 index 000000000000..26f8c47f517d --- /dev/null +++ b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/api/resources/simple/mod.rs @@ -0,0 +1,2 @@ +pub mod simple; +pub use simple::SimpleClient; diff --git a/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/api/resources/simple/simple.rs b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/api/resources/simple/simple.rs new file mode 100644 index 000000000000..5ec45ae2b26c --- /dev/null +++ b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/api/resources/simple/simple.rs @@ -0,0 +1,20 @@ +use crate::{ApiError, ClientConfig, HttpClient, RequestOptions}; +use reqwest::Method; + +pub struct SimpleClient { + pub http_client: HttpClient, +} + +impl SimpleClient { + pub fn new(config: ClientConfig) -> Result { + Ok(Self { + http_client: HttpClient::new(config.clone())?, + }) + } + + pub async fn get_something(&self, options: Option) -> Result<(), ApiError> { + self.http_client + .execute_request(Method::GET, "/get-something", None, None, options) + .await + } +} diff --git a/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/api/types/auth_token_response.rs b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/api/types/auth_token_response.rs new file mode 100644 index 000000000000..834881adddb4 --- /dev/null +++ b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/api/types/auth_token_response.rs @@ -0,0 +1,10 @@ +pub use crate::prelude::*; + +/// An OAuth token response. +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub struct TokenResponse { + pub access_token: String, + pub expires_in: i64, + #[serde(skip_serializing_if = "Option::is_none")] + pub refresh_token: Option, +} diff --git a/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/api/types/get_token_request.rs b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/api/types/get_token_request.rs new file mode 100644 index 000000000000..e314e4edbf5f --- /dev/null +++ b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/api/types/get_token_request.rs @@ -0,0 +1,12 @@ +pub use crate::prelude::*; + +/// Request type for API operation +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub struct GetTokenRequest { + pub client_id: String, + pub client_secret: String, + pub audience: String, + pub grant_type: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub scope: Option, +} diff --git a/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/api/types/mod.rs b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/api/types/mod.rs new file mode 100644 index 000000000000..148d7a889b00 --- /dev/null +++ b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/api/types/mod.rs @@ -0,0 +1,7 @@ +pub mod auth_token_response; +pub mod get_token_request; +pub mod refresh_token_request; + +pub use auth_token_response::TokenResponse; +pub use get_token_request::GetTokenRequest; +pub use refresh_token_request::RefreshTokenRequest; diff --git a/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/api/types/refresh_token_request.rs b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/api/types/refresh_token_request.rs new file mode 100644 index 000000000000..98fa73284fb7 --- /dev/null +++ b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/api/types/refresh_token_request.rs @@ -0,0 +1,13 @@ +pub use crate::prelude::*; + +/// Request type for API operation +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub struct RefreshTokenRequest { + pub client_id: String, + pub client_secret: String, + pub refresh_token: String, + pub audience: String, + pub grant_type: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub scope: Option, +} diff --git a/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/client.rs b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/client.rs new file mode 100644 index 000000000000..006538c35a08 --- /dev/null +++ b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/client.rs @@ -0,0 +1,92 @@ +use crate::api::resources::OauthClientCredentialsMandatoryAuthClient; +use crate::{ApiError, ClientConfig}; +use std::collections::HashMap; +use std::time::Duration; + +/* +Things to know: + +`impl Into` is a generic parameter constraint in Rust that means "accept any type that can be converted into a String." +It's essentially Rust's way of saying "I'll take a string in any form you give it to me." + +Types that implement Into: + +// All of these work: +builder.api_key("hello") // &str +builder.api_key("hello".to_string()) // String +builder.api_key(format!("key_{}", id)) // String from format! +builder.api_key(my_string_variable) // String variable +*/ + +/// Builder for creating API clients with custom configuration +pub struct ApiClientBuilder { + config: ClientConfig, +} + +impl ApiClientBuilder { + /// Create a new builder with the specified base URL + pub fn new(base_url: impl Into) -> Self { + let mut config = ClientConfig::default(); + config.base_url = base_url.into(); + Self { config } + } + + /// Set the API key for authentication + pub fn api_key(mut self, key: impl Into) -> Self { + self.config.api_key = Some(key.into()); + self + } + + /// Set the bearer token for authentication + pub fn token(mut self, token: impl Into) -> Self { + self.config.token = Some(token.into()); + self + } + + /// Set the username for basic authentication + pub fn username(mut self, username: impl Into) -> Self { + self.config.username = Some(username.into()); + self + } + + /// Set the password for basic authentication + pub fn password(mut self, password: impl Into) -> Self { + self.config.password = Some(password.into()); + self + } + + /// Set the request timeout + pub fn timeout(mut self, timeout: Duration) -> Self { + self.config.timeout = timeout; + self + } + + /// Set the maximum number of retries + pub fn max_retries(mut self, retries: u32) -> Self { + self.config.max_retries = retries; + self + } + + /// Add a custom header + pub fn custom_header(mut self, key: impl Into, value: impl Into) -> Self { + self.config.custom_headers.insert(key.into(), value.into()); + self + } + + /// Add multiple custom headers + pub fn custom_headers(mut self, headers: HashMap) -> Self { + self.config.custom_headers.extend(headers); + self + } + + /// Set the user agent + pub fn user_agent(mut self, user_agent: impl Into) -> Self { + self.config.user_agent = user_agent.into(); + self + } + + /// Build the client with validation + pub fn build(self) -> Result { + OauthClientCredentialsMandatoryAuthClient::new(self.config) + } +} diff --git a/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/config.rs b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/config.rs new file mode 100644 index 000000000000..d6282e8c4be9 --- /dev/null +++ b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/config.rs @@ -0,0 +1,30 @@ +use std::collections::HashMap; +use std::time::Duration; + +#[derive(Debug, Clone)] +pub struct ClientConfig { + pub base_url: String, + pub api_key: Option, + pub token: Option, + pub username: Option, + pub password: Option, + pub timeout: Duration, + pub max_retries: u32, + pub custom_headers: HashMap, + pub user_agent: String, +} +impl Default for ClientConfig { + fn default() -> Self { + Self { + base_url: String::new(), + api_key: None, + token: None, + username: None, + password: None, + timeout: Duration::from_secs(60), + max_retries: 3, + custom_headers: HashMap::new(), + user_agent: "OauthClientCredentialsMandatoryAuth Rust SDK".to_string(), + } + } +} diff --git a/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/core/http_client.rs b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/core/http_client.rs new file mode 100644 index 000000000000..e1171a4537f3 --- /dev/null +++ b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/core/http_client.rs @@ -0,0 +1,512 @@ +use crate::{join_url, ApiError, ClientConfig, RequestOptions}; +use futures::{Stream, StreamExt}; +use reqwest::{ + header::{HeaderName, HeaderValue}, + Client, Method, Request, Response, +}; +use serde::de::DeserializeOwned; +use std::{ + pin::Pin, + str::FromStr, + task::{Context, Poll}, +}; + +/// A streaming byte stream for downloading files efficiently +pub struct ByteStream { + content_length: Option, + inner: Pin> + Send>>, +} + +impl ByteStream { + /// Create a new ByteStream from a Response + pub(crate) fn new(response: Response) -> Self { + let content_length = response.content_length(); + let stream = response.bytes_stream(); + + Self { + content_length, + inner: Box::pin(stream), + } + } + + /// Collect the entire stream into a `Vec` + /// + /// This consumes the stream and buffers all data into memory. + /// For large files, prefer using `try_next()` to process chunks incrementally. + /// + /// # Example + /// ```no_run + /// let stream = client.download_file().await?; + /// let bytes = stream.collect().await?; + /// ``` + pub async fn collect(mut self) -> Result, ApiError> { + let mut result = Vec::new(); + while let Some(chunk) = self.inner.next().await { + result.extend_from_slice(&chunk.map_err(ApiError::Network)?); + } + Ok(result) + } + + /// Get the next chunk from the stream + /// + /// Returns `Ok(Some(bytes))` if a chunk is available, + /// `Ok(None)` if the stream is finished, or an error. + /// + /// # Example + /// ```no_run + /// let mut stream = client.download_file().await?; + /// while let Some(chunk) = stream.try_next().await? { + /// process_chunk(&chunk); + /// } + /// ``` + pub async fn try_next(&mut self) -> Result, ApiError> { + match self.inner.next().await { + Some(Ok(bytes)) => Ok(Some(bytes)), + Some(Err(e)) => Err(ApiError::Network(e)), + None => Ok(None), + } + } + + /// Get the content length from response headers if available + pub fn content_length(&self) -> Option { + self.content_length + } +} + +impl Stream for ByteStream { + type Item = Result; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + match self.inner.as_mut().poll_next(cx) { + Poll::Ready(Some(Ok(bytes))) => Poll::Ready(Some(Ok(bytes))), + Poll::Ready(Some(Err(e))) => Poll::Ready(Some(Err(ApiError::Network(e)))), + Poll::Ready(None) => Poll::Ready(None), + Poll::Pending => Poll::Pending, + } + } +} + +/// Internal HTTP client that handles requests with authentication and retries +#[derive(Clone)] +pub struct HttpClient { + client: Client, + config: ClientConfig, +} + +impl HttpClient { + pub fn new(config: ClientConfig) -> Result { + let client = Client::builder() + .timeout(config.timeout) + .user_agent(&config.user_agent) + .build() + .map_err(ApiError::Network)?; + + Ok(Self { client, config }) + } + + /// Execute a request with the given method, path, and options + pub async fn execute_request( + &self, + method: Method, + path: &str, + body: Option, + query_params: Option>, + options: Option, + ) -> Result + where + T: DeserializeOwned, // Generic T: DeserializeOwned means the response will be automatically deserialized into whatever type you specify: + { + let url = join_url(&self.config.base_url, path); + let mut request = self.client.request(method, &url); + + // Apply query parameters if provided + if let Some(params) = query_params { + request = request.query(¶ms); + } + + // Apply additional query parameters from options + if let Some(opts) = &options { + if !opts.additional_query_params.is_empty() { + request = request.query(&opts.additional_query_params); + } + } + + // Apply body if provided + if let Some(body) = body { + request = request.json(&body); + } + + // Build the request + let mut req = request.build().map_err(|e| ApiError::Network(e))?; + + // Apply authentication and headers + self.apply_auth_headers(&mut req, &options)?; + self.apply_custom_headers(&mut req, &options)?; + + // Execute with retries + let response = self.execute_with_retries(req, &options).await?; + self.parse_response(response).await + } + + /// Execute a multipart/form-data request with the given method, path, and options + /// + /// This method is used for file uploads using reqwest's built-in multipart support. + /// Note: Multipart requests are not retried because they cannot be cloned. + /// + /// # Example + /// ```no_run + /// let form = reqwest::multipart::Form::new() + /// .part("file", reqwest::multipart::Part::bytes(vec![1, 2, 3])); + /// + /// let response: MyResponse = client.execute_multipart_request( + /// Method::POST, + /// "/upload", + /// form, + /// None, + /// None, + /// ).await?; + /// ``` + #[cfg(feature = "multipart")] + pub async fn execute_multipart_request( + &self, + method: Method, + path: &str, + form: reqwest::multipart::Form, + query_params: Option>, + options: Option, + ) -> Result + where + T: DeserializeOwned, + { + let url = join_url(&self.config.base_url, path); + let mut request = self.client.request(method, &url); + + // Apply query parameters if provided + if let Some(params) = query_params { + request = request.query(¶ms); + } + + // Apply additional query parameters from options + if let Some(opts) = &options { + if !opts.additional_query_params.is_empty() { + request = request.query(&opts.additional_query_params); + } + } + + // Use reqwest's built-in multipart support + request = request.multipart(form); + + // Build the request + let mut req = request.build().map_err(|e| ApiError::Network(e))?; + + // Apply authentication and headers + self.apply_auth_headers(&mut req, &options)?; + self.apply_custom_headers(&mut req, &options)?; + + // Execute directly without retries (multipart requests cannot be cloned) + let response = self.client.execute(req).await.map_err(ApiError::Network)?; + + // Check response status + if !response.status().is_success() { + let status_code = response.status().as_u16(); + let body = response.text().await.ok(); + return Err(ApiError::from_response(status_code, body.as_deref())); + } + + self.parse_response(response).await + } + + fn apply_auth_headers( + &self, + request: &mut Request, + options: &Option, + ) -> Result<(), ApiError> { + let headers = request.headers_mut(); + + // Apply API key (request options override config) + let api_key = options + .as_ref() + .and_then(|opts| opts.api_key.as_ref()) + .or(self.config.api_key.as_ref()); + + if let Some(key) = api_key { + headers.insert("api_key", key.parse().map_err(|_| ApiError::InvalidHeader)?); + } + + // Apply bearer token (request options override config) + let token = options + .as_ref() + .and_then(|opts| opts.token.as_ref()) + .or(self.config.token.as_ref()); + + if let Some(token) = token { + let auth_value = format!("Bearer {}", token); + headers.insert( + "Authorization", + auth_value.parse().map_err(|_| ApiError::InvalidHeader)?, + ); + } + + Ok(()) + } + + fn apply_custom_headers( + &self, + request: &mut Request, + options: &Option, + ) -> Result<(), ApiError> { + let headers = request.headers_mut(); + + // Apply config-level custom headers + for (key, value) in &self.config.custom_headers { + headers.insert( + HeaderName::from_str(key).map_err(|_| ApiError::InvalidHeader)?, + HeaderValue::from_str(value).map_err(|_| ApiError::InvalidHeader)?, + ); + } + + // Apply request-level custom headers (override config) + if let Some(options) = options { + for (key, value) in &options.additional_headers { + headers.insert( + HeaderName::from_str(key).map_err(|_| ApiError::InvalidHeader)?, + HeaderValue::from_str(value).map_err(|_| ApiError::InvalidHeader)?, + ); + } + } + + Ok(()) + } + + async fn execute_with_retries( + &self, + request: Request, + options: &Option, + ) -> Result { + let max_retries = options + .as_ref() + .and_then(|opts| opts.max_retries) + .unwrap_or(self.config.max_retries); + + let mut last_error = None; + + for attempt in 0..=max_retries { + let cloned_request = request.try_clone().ok_or(ApiError::RequestClone)?; + + match self.client.execute(cloned_request).await { + Ok(response) if response.status().is_success() => return Ok(response), + Ok(response) => { + let status_code = response.status().as_u16(); + let body = response.text().await.ok(); + return Err(ApiError::from_response(status_code, body.as_deref())); + } + Err(e) if attempt < max_retries => { + last_error = Some(e); + // Exponential backoff + let delay = std::time::Duration::from_millis(100 * 2_u64.pow(attempt)); + tokio::time::sleep(delay).await; + } + Err(e) => return Err(ApiError::Network(e)), + } + } + + Err(ApiError::Network(last_error.unwrap())) + } + + async fn parse_response(&self, response: Response) -> Result + where + T: DeserializeOwned, + { + let text = response.text().await.map_err(ApiError::Network)?; + serde_json::from_str(&text).map_err(ApiError::Serialization) + } + + /// Execute a request and return a streaming response (for large file downloads) + /// + /// This method returns a `ByteStream` that can be used to download large files + /// efficiently without loading the entire content into memory. The stream can be + /// consumed chunk by chunk, written directly to disk, or collected into bytes. + /// + /// # Examples + /// + /// **Option 1: Collect all bytes into memory** + /// ```no_run + /// let stream = client.execute_stream_request( + /// Method::GET, + /// "/file", + /// None, + /// None, + /// None, + /// ).await?; + /// + /// let bytes = stream.collect().await?; + /// ``` + /// + /// **Option 2: Process chunks with try_next()** + /// ```no_run + /// let mut stream = client.execute_stream_request( + /// Method::GET, + /// "/large-file", + /// None, + /// None, + /// None, + /// ).await?; + /// + /// while let Some(chunk) = stream.try_next().await? { + /// process_chunk(&chunk); + /// } + /// ``` + /// + /// **Option 3: Stream with futures::Stream trait** + /// ```no_run + /// use futures::StreamExt; + /// + /// let stream = client.execute_stream_request( + /// Method::GET, + /// "/large-file", + /// None, + /// None, + /// None, + /// ).await?; + /// + /// let mut file = tokio::fs::File::create("output.mp4").await?; + /// let mut stream = std::pin::pin!(stream); + /// while let Some(chunk) = stream.next().await { + /// let chunk = chunk?; + /// tokio::io::AsyncWriteExt::write_all(&mut file, &chunk).await?; + /// } + /// ``` + pub async fn execute_stream_request( + &self, + method: Method, + path: &str, + body: Option, + query_params: Option>, + options: Option, + ) -> Result { + let url = join_url(&self.config.base_url, path); + let mut request = self.client.request(method, &url); + + // Apply query parameters if provided + if let Some(params) = query_params { + request = request.query(¶ms); + } + + // Apply additional query parameters from options + if let Some(opts) = &options { + if !opts.additional_query_params.is_empty() { + request = request.query(&opts.additional_query_params); + } + } + + // Apply body if provided + if let Some(body) = body { + request = request.json(&body); + } + + // Build the request + let mut req = request.build().map_err(|e| ApiError::Network(e))?; + + // Apply authentication and headers + self.apply_auth_headers(&mut req, &options)?; + self.apply_custom_headers(&mut req, &options)?; + + // Execute with retries + let response = self.execute_with_retries(req, &options).await?; + + // Return streaming response + Ok(ByteStream::new(response)) + } + + /// Execute a request and return an SSE stream + /// + /// This method returns an `SseStream` that automatically parses + /// Server-Sent Events and deserializes the JSON data in each event. + /// + /// # SSE-Specific Headers + /// + /// This method automatically sets the following headers **after** applying custom headers, + /// which means these headers will override any user-supplied values: + /// - `Accept: text/event-stream` - Required for SSE protocol + /// - `Cache-Control: no-store` - Prevents caching of streaming responses + /// + /// This ensures proper SSE behavior even if custom headers are provided. + /// + /// # Example + /// ```no_run + /// use futures::StreamExt; + /// + /// let stream = client.execute_sse_request::( + /// Method::POST, + /// "/stream", + /// Some(serde_json::json!({"query": "Hello"})), + /// None, + /// None, + /// Some("[[DONE]]".to_string()), + /// ).await?; + /// + /// let mut stream = std::pin::pin!(stream); + /// while let Some(chunk) = stream.next().await { + /// let chunk = chunk?; + /// println!("Received: {:?}", chunk); + /// } + /// ``` + #[cfg(feature = "sse")] + pub async fn execute_sse_request( + &self, + method: Method, + path: &str, + body: Option, + query_params: Option>, + options: Option, + terminator: Option, + ) -> Result, ApiError> + where + T: DeserializeOwned + Send + 'static, + { + let url = join_url(&self.config.base_url, path); + let mut request = self.client.request(method, &url); + + // Apply query parameters if provided + if let Some(params) = query_params { + request = request.query(¶ms); + } + + // Apply additional query parameters from options + if let Some(opts) = &options { + if !opts.additional_query_params.is_empty() { + request = request.query(&opts.additional_query_params); + } + } + + // Apply body if provided + if let Some(body) = body { + request = request.json(&body); + } + + // Build the request + let mut req = request.build().map_err(|e| ApiError::Network(e))?; + + // Apply authentication and headers + self.apply_auth_headers(&mut req, &options)?; + self.apply_custom_headers(&mut req, &options)?; + + // SSE-specific headers + req.headers_mut().insert( + "Accept", + "text/event-stream" + .parse() + .map_err(|_| ApiError::InvalidHeader)?, + ); + req.headers_mut().insert( + "Cache-Control", + "no-store".parse().map_err(|_| ApiError::InvalidHeader)?, + ); + + // Execute with retries + let response = self.execute_with_retries(req, &options).await?; + + // Return SSE stream + crate::SseStream::new(response, terminator).await + } +} diff --git a/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/core/mod.rs b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/core/mod.rs new file mode 100644 index 000000000000..ba23f97e4acd --- /dev/null +++ b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/core/mod.rs @@ -0,0 +1,15 @@ +//! Core client infrastructure + +mod http_client; +mod query_parameter_builder; +mod request_options; +#[cfg(feature = "sse")] +mod sse_stream; +mod utils; + +pub use http_client::{ByteStream, HttpClient}; +pub use query_parameter_builder::{parse_structured_query, QueryBuilder, QueryBuilderError}; +pub use request_options::RequestOptions; +#[cfg(feature = "sse")] +pub use sse_stream::SseStream; +pub use utils::join_url; diff --git a/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/core/pagination.rs b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/core/pagination.rs new file mode 100644 index 000000000000..2cd716dc82a3 --- /dev/null +++ b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/core/pagination.rs @@ -0,0 +1,282 @@ +use std::collections::VecDeque; +use std::future::Future; +use std::pin::Pin; +use std::sync::Arc; +use std::task::{Context, Poll}; + +use futures::Stream; +use serde_json::Value; + +use crate::{ApiError, HttpClient}; + +/// Result of a pagination request +#[derive(Debug)] +pub struct PaginationResult { + pub items: Vec, + pub next_cursor: Option, + pub has_next_page: bool, +} + +/// Async paginator that implements Stream for iterating over paginated results +pub struct AsyncPaginator { + http_client: Arc, + page_loader: Box< + dyn Fn( + Arc, + Option, + ) + -> Pin, ApiError>> + Send>> + + Send + + Sync, + >, + current_page: VecDeque, + current_cursor: Option, + has_next_page: bool, + loading_next: + Option, ApiError>> + Send>>>, +} + +impl AsyncPaginator { + pub fn new( + http_client: Arc, + page_loader: F, + initial_cursor: Option, + ) -> Result + where + F: Fn(Arc, Option) -> Fut + Send + Sync + 'static, + Fut: Future, ApiError>> + Send + 'static, + { + Ok(Self { + http_client, + page_loader: Box::new(move |client, cursor| Box::pin(page_loader(client, cursor))), + current_page: VecDeque::new(), + current_cursor: initial_cursor, + has_next_page: true, // Assume true initially, will be updated after first request + loading_next: None, + }) + } + + /// Check if there are more pages available + pub fn has_next_page(&self) -> bool { + !self.current_page.is_empty() || self.has_next_page + } + + /// Load the next page explicitly + pub async fn next_page(&mut self) -> Result, ApiError> { + if !self.has_next_page { + return Ok(Vec::new()); + } + + let result = + (self.page_loader)(self.http_client.clone(), self.current_cursor.clone()).await?; + + self.current_cursor = result.next_cursor; + self.has_next_page = result.has_next_page; + + Ok(result.items) + } +} + +impl Stream for AsyncPaginator +where + T: Unpin, +{ + type Item = Result; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + // If we have items in the current page, return the next one + if let Some(item) = self.current_page.pop_front() { + return Poll::Ready(Some(Ok(item))); + } + + // If we're already loading the next page, poll that future + if let Some(ref mut loading_future) = self.loading_next { + match loading_future.as_mut().poll(cx) { + Poll::Ready(Ok(result)) => { + self.current_page.extend(result.items); + self.current_cursor = result.next_cursor; + self.has_next_page = result.has_next_page; + self.loading_next = None; + + // Try to get the next item from the newly loaded page + if let Some(item) = self.current_page.pop_front() { + return Poll::Ready(Some(Ok(item))); + } else if !self.has_next_page { + return Poll::Ready(None); + } + // Fall through to start loading next page + } + Poll::Ready(Err(e)) => { + self.loading_next = None; + return Poll::Ready(Some(Err(e))); + } + Poll::Pending => return Poll::Pending, + } + } + + // If we have no more pages to load, we're done + if !self.has_next_page { + return Poll::Ready(None); + } + + // Start loading the next page + let future = (self.page_loader)(self.http_client.clone(), self.current_cursor.clone()); + self.loading_next = Some(future); + + // Poll the future immediately + if let Some(ref mut loading_future) = self.loading_next { + match loading_future.as_mut().poll(cx) { + Poll::Ready(Ok(result)) => { + self.current_page.extend(result.items); + self.current_cursor = result.next_cursor; + self.has_next_page = result.has_next_page; + self.loading_next = None; + + if let Some(item) = self.current_page.pop_front() { + Poll::Ready(Some(Ok(item))) + } else if !self.has_next_page { + Poll::Ready(None) + } else { + // This shouldn't happen, but just in case + cx.waker().wake_by_ref(); + Poll::Pending + } + } + Poll::Ready(Err(e)) => { + self.loading_next = None; + Poll::Ready(Some(Err(e))) + } + Poll::Pending => Poll::Pending, + } + } else { + Poll::Pending + } + } +} + +/// Synchronous paginator for blocking iteration +pub struct SyncPaginator { + http_client: Arc, + page_loader: Box< + dyn Fn(Arc, Option) -> Result, ApiError> + + Send + + Sync, + >, + current_page: VecDeque, + current_cursor: Option, + has_next_page: bool, +} + +impl SyncPaginator { + pub fn new( + http_client: Arc, + page_loader: F, + initial_cursor: Option, + ) -> Result + where + F: Fn(Arc, Option) -> Result, ApiError> + + Send + + Sync + + 'static, + { + Ok(Self { + http_client, + page_loader: Box::new(page_loader), + current_page: VecDeque::new(), + current_cursor: initial_cursor, + has_next_page: true, // Assume true initially + }) + } + + /// Check if there are more pages available + pub fn has_next_page(&self) -> bool { + !self.current_page.is_empty() || self.has_next_page + } + + /// Load the next page explicitly + pub fn next_page(&mut self) -> Result, ApiError> { + if !self.has_next_page { + return Ok(Vec::new()); + } + + let result = (self.page_loader)(self.http_client.clone(), self.current_cursor.clone())?; + + self.current_cursor = result.next_cursor; + self.has_next_page = result.has_next_page; + + Ok(result.items) + } + + /// Get all remaining items by loading all pages + pub fn collect_all(&mut self) -> Result, ApiError> { + let mut all_items = Vec::new(); + + // Add items from current page + while let Some(item) = self.current_page.pop_front() { + all_items.push(item); + } + + // Load all remaining pages + while self.has_next_page { + let page_items = self.next_page()?; + all_items.extend(page_items); + } + + Ok(all_items) + } +} + +impl Iterator for SyncPaginator { + type Item = Result; + + fn next(&mut self) -> Option { + // If we have items in the current page, return the next one + if let Some(item) = self.current_page.pop_front() { + return Some(Ok(item)); + } + + // If we have no more pages to load, we're done + if !self.has_next_page { + return None; + } + + // Load the next page + match (self.page_loader)(self.http_client.clone(), self.current_cursor.clone()) { + Ok(result) => { + self.current_page.extend(result.items); + self.current_cursor = result.next_cursor; + self.has_next_page = result.has_next_page; + + // Return the first item from the newly loaded page + self.current_page.pop_front().map(Ok) + } + Err(e) => Some(Err(e)), + } + } +} + +/// Trait for types that can provide pagination metadata +pub trait Paginated { + /// Extract the items from this page + fn items(&self) -> &[T]; + + /// Get the cursor for the next page, if any + fn next_cursor(&self) -> Option<&str>; + + /// Check if there's a next page available + fn has_next_page(&self) -> bool; +} + +/// Trait for types that can provide offset-based pagination metadata +pub trait OffsetPaginated { + /// Extract the items from this page + fn items(&self) -> &[T]; + + /// Check if there's a next page available + fn has_next_page(&self) -> bool; + + /// Get the current page size (for calculating next offset) + fn page_size(&self) -> usize { + self.items().len() + } +} diff --git a/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/core/query_parameter_builder.rs b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/core/query_parameter_builder.rs new file mode 100644 index 000000000000..24e78f885611 --- /dev/null +++ b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/core/query_parameter_builder.rs @@ -0,0 +1,297 @@ +use chrono::{DateTime, Utc}; +use serde::Serialize; + +/// Modern query builder with type-safe method chaining +/// Provides a clean, Swift-like API for building HTTP query parameters +#[derive(Debug, Default)] +pub struct QueryBuilder { + params: Vec<(String, String)>, +} + +impl QueryBuilder { + /// Create a new query parameter builder + pub fn new() -> Self { + Self::default() + } + + /// Add a string parameter (accept both required/optional) + pub fn string(mut self, key: &str, value: impl Into>) -> Self { + if let Some(v) = value.into() { + self.params.push((key.to_string(), v)); + } + self + } + + /// Add multiple string parameters with the same key (for allow-multiple query params) + /// Accepts both Vec and Vec>, adding each non-None value as a separate query parameter + pub fn string_array(mut self, key: &str, values: I) -> Self + where + I: IntoIterator, + T: Into>, + { + for value in values { + if let Some(v) = value.into() { + self.params.push((key.to_string(), v)); + } + } + self + } + + /// Add an integer parameter (accept both required/optional) + pub fn int(mut self, key: &str, value: impl Into>) -> Self { + if let Some(v) = value.into() { + self.params.push((key.to_string(), v.to_string())); + } + self + } + + /// Add a big integer parameter (accept both required/optional) + pub fn big_int(mut self, key: &str, value: impl Into>) -> Self { + if let Some(v) = value.into() { + self.params.push((key.to_string(), v.to_string())); + } + self + } + + /// Add multiple integer parameters with the same key (for allow-multiple query params) + /// Accepts both Vec and Vec>, adding each non-None value as a separate query parameter + pub fn int_array(mut self, key: &str, values: I) -> Self + where + I: IntoIterator, + T: Into>, + { + for value in values { + if let Some(v) = value.into() { + self.params.push((key.to_string(), v.to_string())); + } + } + self + } + + /// Add a float parameter + pub fn float(mut self, key: &str, value: impl Into>) -> Self { + if let Some(v) = value.into() { + self.params.push((key.to_string(), v.to_string())); + } + self + } + + /// Add multiple float parameters with the same key (for allow-multiple query params) + /// Accepts both Vec and Vec>, adding each non-None value as a separate query parameter + pub fn float_array(mut self, key: &str, values: I) -> Self + where + I: IntoIterator, + T: Into>, + { + for value in values { + if let Some(v) = value.into() { + self.params.push((key.to_string(), v.to_string())); + } + } + self + } + + /// Add a boolean parameter + pub fn bool(mut self, key: &str, value: impl Into>) -> Self { + if let Some(v) = value.into() { + self.params.push((key.to_string(), v.to_string())); + } + self + } + + /// Add multiple boolean parameters with the same key (for allow-multiple query params) + /// Accepts both Vec and Vec>, adding each non-None value as a separate query parameter + pub fn bool_array(mut self, key: &str, values: I) -> Self + where + I: IntoIterator, + T: Into>, + { + for value in values { + if let Some(v) = value.into() { + self.params.push((key.to_string(), v.to_string())); + } + } + self + } + + /// Add a datetime parameter + pub fn datetime(mut self, key: &str, value: impl Into>>) -> Self { + if let Some(v) = value.into() { + self.params.push((key.to_string(), v.to_rfc3339())); + } + self + } + + /// Add a UUID parameter (converts to string) + pub fn uuid(mut self, key: &str, value: impl Into>) -> Self { + if let Some(v) = value.into() { + self.params.push((key.to_string(), v.to_string())); + } + self + } + + /// Add a date parameter (converts NaiveDate to DateTime) + pub fn date(mut self, key: &str, value: impl Into>) -> Self { + if let Some(v) = value.into() { + // Convert NaiveDate to DateTime at start of day + let datetime = v.and_hms_opt(0, 0, 0).unwrap().and_utc(); + self.params.push((key.to_string(), datetime.to_rfc3339())); + } + self + } + + /// Add any serializable parameter (for enums and complex types) + pub fn serialize(mut self, key: &str, value: Option) -> Self { + if let Some(v) = value { + // For enums that implement Display, use the Display implementation + // to avoid JSON quotes in query parameters + if let Ok(serialized) = serde_json::to_string(&v) { + // Remove JSON quotes if the value is a simple string + let cleaned = if serialized.starts_with('"') && serialized.ends_with('"') { + serialized.trim_matches('"').to_string() + } else { + serialized + }; + self.params.push((key.to_string(), cleaned)); + } + } + self + } + + /// Add multiple serializable parameters with the same key (for allow-multiple query params with enums) + /// Accepts both Vec and Vec>, adding each non-None value as a separate query parameter + pub fn serialize_array( + mut self, + key: &str, + values: impl IntoIterator, + ) -> Self { + for value in values { + if let Ok(serialized) = serde_json::to_string(&value) { + // Skip null values (from Option::None) + if serialized == "null" { + continue; + } + // Remove JSON quotes if the value is a simple string + let cleaned = if serialized.starts_with('"') && serialized.ends_with('"') { + serialized.trim_matches('"').to_string() + } else { + serialized + }; + self.params.push((key.to_string(), cleaned)); + } + } + self + } + + /// Parse and add a structured query string + /// Handles complex query patterns like: + /// - "key:value" patterns + /// - "key:value1,value2" (comma-separated values) + /// - Quoted values: "key:\"value with spaces\"" + /// - Space-separated terms (treated as AND logic) + pub fn structured_query(mut self, key: &str, value: impl Into>) -> Self { + if let Some(query_str) = value.into() { + if let Ok(parsed_params) = parse_structured_query(&query_str) { + self.params.extend(parsed_params); + } else { + // Fall back to simple query parameter if parsing fails + self.params.push((key.to_string(), query_str)); + } + } + self + } + + /// Build the final query parameters + pub fn build(self) -> Option> { + if self.params.is_empty() { + None + } else { + Some(self.params) + } + } +} + +/// Errors that can occur during structured query parsing +#[derive(Debug)] +pub enum QueryBuilderError { + InvalidQuerySyntax(String), +} + +impl std::fmt::Display for QueryBuilderError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + QueryBuilderError::InvalidQuerySyntax(msg) => { + write!(f, "Invalid query syntax: {}", msg) + } + } + } +} + +impl std::error::Error for QueryBuilderError {} + +/// Parse structured query strings like "key:value key2:value1,value2" +/// Used for complex filtering patterns in APIs like Foxglove +/// +/// Supported patterns: +/// - Simple: "status:active" +/// - Multiple values: "type:sensor,camera" +/// - Quoted values: "location:\"New York\"" +/// - Complex: "status:active type:sensor location:\"San Francisco\"" +pub fn parse_structured_query(query: &str) -> Result, QueryBuilderError> { + let mut params = Vec::new(); + let terms = tokenize_query(query); + + for term in terms { + if let Some((key, values)) = term.split_once(':') { + // Handle comma-separated values + for value in values.split(',') { + let clean_value = value.trim_matches('"'); // Remove quotes + params.push((key.to_string(), clean_value.to_string())); + } + } else { + // For terms without colons, return error to be explicit about expected format + return Err(QueryBuilderError::InvalidQuerySyntax(format!( + "Cannot parse term '{}' - expected 'key:value' format for structured queries", + term + ))); + } + } + + Ok(params) +} + +/// Tokenize a query string, properly handling quoted strings +fn tokenize_query(input: &str) -> Vec { + let mut tokens = Vec::new(); + let mut current_token = String::new(); + let mut in_quotes = false; + let mut chars = input.chars().peekable(); + + while let Some(c) = chars.next() { + match c { + '"' => { + // Toggle quote state and include the quote in the token + in_quotes = !in_quotes; + current_token.push(c); + } + ' ' if !in_quotes => { + // Space outside quotes - end current token + if !current_token.is_empty() { + tokens.push(current_token.trim().to_string()); + current_token.clear(); + } + } + _ => { + // Any other character (including spaces inside quotes) + current_token.push(c); + } + } + } + + // Add the last token if there is one + if !current_token.is_empty() { + tokens.push(current_token.trim().to_string()); + } + + tokens +} diff --git a/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/core/request_options.rs b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/core/request_options.rs new file mode 100644 index 000000000000..dbd4d60d4940 --- /dev/null +++ b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/core/request_options.rs @@ -0,0 +1,58 @@ +use std::collections::HashMap; +/// Options for customizing individual requests +#[derive(Debug, Clone, Default)] +pub struct RequestOptions { + /// API key for authentication (overrides client-level API key) + pub api_key: Option, + /// Bearer token for authentication (overrides client-level token) + pub token: Option, + /// Maximum number of retry attempts for failed requests + pub max_retries: Option, + /// Request timeout in seconds (overrides client-level timeout) + pub timeout_seconds: Option, + /// Additional headers to include in the request + pub additional_headers: HashMap, + /// Additional query parameters to include in the request + pub additional_query_params: HashMap, +} + +impl RequestOptions { + pub fn new() -> Self { + Self::default() + } + + pub fn api_key(mut self, key: impl Into) -> Self { + self.api_key = Some(key.into()); + self + } + + pub fn token(mut self, token: impl Into) -> Self { + self.token = Some(token.into()); + self + } + + pub fn max_retries(mut self, retries: u32) -> Self { + self.max_retries = Some(retries); + self + } + + pub fn timeout_seconds(mut self, timeout: u64) -> Self { + self.timeout_seconds = Some(timeout); + self + } + + pub fn additional_header(mut self, key: impl Into, value: impl Into) -> Self { + self.additional_headers.insert(key.into(), value.into()); + self + } + + pub fn additional_query_param( + mut self, + key: impl Into, + value: impl Into, + ) -> Self { + self.additional_query_params + .insert(key.into(), value.into()); + self + } +} diff --git a/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/core/sse_stream.rs b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/core/sse_stream.rs new file mode 100644 index 000000000000..50a375e0a7ff --- /dev/null +++ b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/core/sse_stream.rs @@ -0,0 +1,281 @@ +use crate::ApiError; +use futures::Stream; +use pin_project::pin_project; +use reqwest::{header::CONTENT_TYPE, Response}; +use reqwest_sse::{error::EventError, Event, EventSource}; +use serde::de::DeserializeOwned; +use std::{ + marker::PhantomData, + pin::Pin, + task::{Context, Poll}, +}; + +/// Metadata from a Server-Sent Event +/// +/// Contains the SSE protocol fields (event, id, retry) that accompany the data payload. +/// This struct provides access to SSE metadata for advanced use cases. +/// +/// Per the SSE specification: +/// - `event` defaults to "message" when not specified by the server +/// - `id` is optional and used for reconnection support (Last-Event-ID header) +/// - `retry` is optional and specifies reconnection timeout in milliseconds +#[derive(Debug, Clone)] +pub struct SseMetadata { + /// The event type (defaults to "message" per SSE spec if not specified by server) + pub event: String, + /// The event ID for reconnection support (None if not specified) + pub id: Option, + /// Retry timeout in milliseconds (None if not specified) + pub retry: Option, +} + +/// A Server-Sent Event with both data and metadata +/// +/// Contains the deserialized data payload along with SSE protocol metadata. +/// Use this when you need access to event IDs, event types, or retry information. +#[derive(Debug)] +pub struct SseEvent { + /// The deserialized data payload + pub data: T, + /// SSE protocol metadata + pub metadata: SseMetadata, +} + +/// A type-safe wrapper around Server-Sent Events (SSE) streams +/// +/// Leverages `reqwest-sse` for SSE protocol parsing and adds: +/// - Automatic JSON deserialization to typed structs +/// - Stream terminator support (e.g., `[DONE]` for OpenAI-style APIs) +/// - Integrated error handling with `ApiError` +/// - Content-Type validation (`text/event-stream` required) +/// +/// # Charset Handling +/// +/// The `reqwest-sse` library automatically handles charset detection and decoding +/// based on the Content-Type header. If no charset is specified, UTF-8 is assumed. +/// This matches the SSE specification default behavior. +/// +/// # Example +/// +/// Basic usage with async iteration: +/// +/// ```no_run +/// use futures::StreamExt; +/// +/// let stream: SseStream = client.stream_completions(request).await?; +/// let mut stream = std::pin::pin!(stream); +/// +/// while let Some(result) = stream.next().await { +/// match result { +/// Ok(chunk) => println!("Received: {:?}", chunk), +/// Err(e) => eprintln!("Error: {}", e), +/// } +/// } +/// ``` +/// +/// # Error Handling +/// +/// The stream yields `Result` items. Errors can occur from: +/// - Invalid JSON in SSE data field (`ApiError::Serialization`) +/// - SSE protocol errors (`ApiError::SseParseError`) +/// - Network errors during streaming +/// +/// **Important:** When an error occurs for a single event (e.g., malformed JSON), +/// the stream yields `Err` for that item but **continues streaming** subsequent events. +/// The stream only ends when: +/// - A terminator is received (if configured) +/// - The server closes the connection +/// - A fatal network error occurs +/// +/// This allows the client to handle per-event errors gracefully without losing +/// the entire stream. Compare this to other error handling strategies where a single +/// bad event might terminate the stream. +/// +/// # Terminator Support +/// +/// When a terminator string is specified (e.g., `[DONE]`), the stream automatically +/// ends when an SSE event with that exact data is received. The terminator event +/// itself is not yielded to the consumer. +#[pin_project] +pub struct SseStream { + #[pin] + inner: Pin> + Send>>, + terminator: Option, + _phantom: PhantomData, +} + +impl SseStream +where + T: DeserializeOwned, +{ + /// Create a new SSE stream from a Response + /// + /// # Arguments + /// * `response` - The HTTP response to parse as SSE + /// * `terminator` - Optional terminator string (e.g., `"[DONE]"`) that signals end of stream + /// + /// # Errors + /// Returns `ApiError::SseParseError` if: + /// - Response Content-Type is not `text/event-stream` + /// - SSE stream cannot be created from response + pub(crate) async fn new( + response: Response, + terminator: Option, + ) -> Result { + // Validate Content-Type header (case-insensitive, handles parameters) + let content_type = response + .headers() + .get(CONTENT_TYPE) + .and_then(|v| v.to_str().ok()) + .unwrap_or(""); + + // Extract main content type (before ';' parameter separator) and compare case-insensitively + let content_type_main = content_type.split(';').next().unwrap_or("").trim(); + + if !content_type_main.eq_ignore_ascii_case("text/event-stream") { + return Err(ApiError::SseParseError(format!( + "Expected Content-Type to be 'text/event-stream', got '{}'", + content_type + ))); + } + + // Use reqwest-sse's EventSource trait to get SSE stream + let events = response + .events() + .await + .map_err(|e| ApiError::SseParseError(e.to_string()))?; + + Ok(Self { + inner: Box::pin(events), + terminator, + _phantom: PhantomData, + }) + } +} + +impl SseStream +where + T: DeserializeOwned, +{ + /// Convert this stream into one that yields events with metadata + /// + /// This consumes the stream and returns a new stream that yields `SseEvent` + /// instead of just `T`, providing access to SSE metadata (event type, id, retry). + /// + /// # Example + /// + /// ```no_run + /// use futures::StreamExt; + /// + /// let stream = client.stream_completions(request).await?; + /// let mut stream_with_metadata = stream.with_metadata(); + /// let mut stream_with_metadata = std::pin::pin!(stream_with_metadata); + /// + /// while let Some(result) = stream_with_metadata.next().await { + /// match result { + /// Ok(event) => { + /// println!("Data: {:?}", event.data); + /// println!("Event type: {}", event.metadata.event); + /// if let Some(id) = &event.metadata.id { + /// println!("Event ID: {}", id); + /// } + /// } + /// Err(e) => eprintln!("Error: {}", e), + /// } + /// } + /// ``` + pub fn with_metadata(self) -> SseStreamWithMetadata { + SseStreamWithMetadata { inner: self } + } +} + +impl Stream for SseStream +where + T: DeserializeOwned, +{ + type Item = Result; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + match this.inner.poll_next(cx) { + Poll::Ready(Some(Ok(event))) => { + // Check for terminator before parsing + if let Some(ref terminator) = this.terminator { + if event.data == *terminator { + // Terminator found - end stream cleanly + return Poll::Ready(None); + } + } + + // Deserialize JSON data to typed struct + match serde_json::from_str(&event.data) { + Ok(value) => Poll::Ready(Some(Ok(value))), + Err(e) => Poll::Ready(Some(Err(ApiError::Serialization(e)))), + } + } + Poll::Ready(Some(Err(e))) => { + Poll::Ready(Some(Err(ApiError::SseParseError(e.to_string())))) + } + Poll::Ready(None) => Poll::Ready(None), + Poll::Pending => Poll::Pending, + } + } +} + +/// Stream wrapper that yields events with metadata +/// +/// Created by calling [`SseStream::with_metadata()`]. This stream yields `SseEvent` +/// which includes both the deserialized data and SSE protocol metadata. +#[pin_project] +pub struct SseStreamWithMetadata { + #[pin] + inner: SseStream, +} + +impl Stream for SseStreamWithMetadata +where + T: DeserializeOwned, +{ + type Item = Result, ApiError>; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + + // Access the inner stream's fields through pin projection + let inner_pin = this.inner.project(); + + match inner_pin.inner.poll_next(cx) { + Poll::Ready(Some(Ok(event))) => { + // Check for terminator + if let Some(ref terminator) = inner_pin.terminator { + if event.data == *terminator { + return Poll::Ready(None); + } + } + + // Extract metadata + let metadata = SseMetadata { + // Default to "message" if event type is empty (per SSE spec) + event: if event.event_type.is_empty() { + "message".to_string() + } else { + event.event_type.clone() + }, + id: event.last_event_id.clone(), + retry: event.retry.map(|d| d.as_millis() as u64), + }; + + // Deserialize JSON data + match serde_json::from_str(&event.data) { + Ok(data) => Poll::Ready(Some(Ok(SseEvent { data, metadata }))), + Err(e) => Poll::Ready(Some(Err(ApiError::Serialization(e)))), + } + } + Poll::Ready(Some(Err(e))) => { + Poll::Ready(Some(Err(ApiError::SseParseError(e.to_string())))) + } + Poll::Ready(None) => Poll::Ready(None), + Poll::Pending => Poll::Pending, + } + } +} diff --git a/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/core/utils.rs b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/core/utils.rs new file mode 100644 index 000000000000..808056dc34a2 --- /dev/null +++ b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/core/utils.rs @@ -0,0 +1,19 @@ +/// URL building utilities +/// Safely join a base URL with a path, handling slashes properly +/// +/// # Examples +/// ``` +/// use example_api::utils::url::join_url; +/// +/// assert_eq!(join_url("https://api.example.com", "users"), "https://api.example.com/users"); +/// assert_eq!(join_url("https://api.example.com/", "users"), "https://api.example.com/users"); +/// assert_eq!(join_url("https://api.example.com", "/users"), "https://api.example.com/users"); +/// assert_eq!(join_url("https://api.example.com/", "/users"), "https://api.example.com/users"); +/// ``` +pub fn join_url(base_url: &str, path: &str) -> String { + format!( + "{}/{}", + base_url.trim_end_matches('/'), + path.trim_start_matches('/') + ) +} diff --git a/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/error.rs b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/error.rs new file mode 100644 index 000000000000..7feca158a561 --- /dev/null +++ b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/error.rs @@ -0,0 +1,32 @@ +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum ApiError { + #[error("HTTP error {status}: {message}")] + Http { status: u16, message: String }, + #[error("Network error: {0}")] + Network(reqwest::Error), + #[error("Serialization error: {0}")] + Serialization(serde_json::Error), + #[error("Configuration error: {0}")] + Configuration(String), + #[error("Invalid header value")] + InvalidHeader, + #[error("Could not clone request for retry")] + RequestClone, + #[error("SSE stream terminated")] + StreamTerminated, + #[error("SSE parse error: {0}")] + SseParseError(String), +} + +impl ApiError { + pub fn from_response(status_code: u16, body: Option<&str>) -> Self { + match status_code { + _ => Self::Http { + status: status_code, + message: body.unwrap_or("Unknown error").to_string(), + }, + } + } +} diff --git a/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/lib.rs b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/lib.rs new file mode 100644 index 000000000000..6ff23b2c0ea0 --- /dev/null +++ b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/lib.rs @@ -0,0 +1,53 @@ +//! # OauthClientCredentialsMandatoryAuth SDK +//! +//! The official Rust SDK for the OauthClientCredentialsMandatoryAuth. +//! +//! ## Getting Started +//! +//! ```rust +//! use seed_oauth_client_credentials_mandatory_auth::prelude::*; +//! +//! #[tokio::main] +//! async fn main() { +//! let config = ClientConfig { +//! ..Default::default() +//! }; +//! let client = +//! OauthClientCredentialsMandatoryAuthClient::new(config).expect("Failed to build client"); +//! client +//! .auth +//! .get_token_with_client_credentials( +//! &GetTokenRequest { +//! client_id: "my_oauth_app_123".to_string(), +//! client_secret: "sk_live_abcdef123456789".to_string(), +//! audience: "https://api.example.com".to_string(), +//! grant_type: "client_credentials".to_string(), +//! scope: Some("read:users".to_string()), +//! }, +//! None, +//! ) +//! .await; +//! } +//! ``` +//! +//! ## Modules +//! +//! - [`api`] - Core API types and models +//! - [`client`] - Client implementations +//! - [`config`] - Configuration options +//! - [`core`] - Core utilities and infrastructure +//! - [`error`] - Error types and handling +//! - [`prelude`] - Common imports for convenience + +pub mod api; +pub mod client; +pub mod config; +pub mod core; +pub mod error; +pub mod prelude; + +pub use api::*; +pub use client::*; +pub use config::*; +pub use core::*; +pub use error::ApiError; diff --git a/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/prelude.rs b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/prelude.rs new file mode 100644 index 000000000000..f678b9706a2e --- /dev/null +++ b/seed/rust-sdk/oauth-client-credentials-mandatory-auth/src/prelude.rs @@ -0,0 +1,19 @@ +//! Prelude module for convenient imports +//! +//! This module re-exports the most commonly used types and traits. +//! Import it with: `use seed_oauth_client_credentials_mandatory_auth::prelude::*;` + +// Client and configuration +pub use crate::config::ClientConfig; +pub use crate::core::{HttpClient, RequestOptions}; +pub use crate::error::ApiError; + +// Main client and resource clients +pub use crate::api::*; + +// Re-export commonly used external types +pub use ordered_float::OrderedFloat; +pub use serde::{Deserialize, Serialize}; +pub use serde_json::{json, Value}; +pub use std::collections::{HashMap, HashSet}; +pub use std::fmt; diff --git a/seed/rust-sdk/pagination/dynamic-snippets/example24.rs b/seed/rust-sdk/pagination/dynamic-snippets/example24.rs index 43cb70ee1cf4..64e2d9c4a8f6 100644 --- a/seed/rust-sdk/pagination/dynamic-snippets/example24.rs +++ b/seed/rust-sdk/pagination/dynamic-snippets/example24.rs @@ -10,6 +10,11 @@ async fn main() { let client = PaginationClient::new(config).expect("Failed to build client"); client .users - .list_with_global_config(&ListWithGlobalConfigQueryRequest { offset: Some(1) }, None) + .list_with_cursor_pagination( + &ListWithCursorPaginationQueryRequest { + starting_after: Some("starting_after".to_string()), + }, + None, + ) .await; } diff --git a/seed/rust-sdk/pagination/dynamic-snippets/example25.rs b/seed/rust-sdk/pagination/dynamic-snippets/example25.rs new file mode 100644 index 000000000000..43cb70ee1cf4 --- /dev/null +++ b/seed/rust-sdk/pagination/dynamic-snippets/example25.rs @@ -0,0 +1,15 @@ +use seed_pagination::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + base_url: "https://api.fern.com".to_string(), + token: Some("".to_string()), + ..Default::default() + }; + let client = PaginationClient::new(config).expect("Failed to build client"); + client + .users + .list_with_global_config(&ListWithGlobalConfigQueryRequest { offset: Some(1) }, None) + .await; +} diff --git a/seed/rust-sdk/pagination/dynamic-snippets/example26.rs b/seed/rust-sdk/pagination/dynamic-snippets/example26.rs new file mode 100644 index 000000000000..5da8f67befc8 --- /dev/null +++ b/seed/rust-sdk/pagination/dynamic-snippets/example26.rs @@ -0,0 +1,15 @@ +use seed_pagination::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + base_url: "https://api.fern.com".to_string(), + token: Some("".to_string()), + ..Default::default() + }; + let client = PaginationClient::new(config).expect("Failed to build client"); + client + .users + .list_with_optional_data(&ListWithOptionalDataQueryRequest { page: Some(1) }, None) + .await; +} diff --git a/seed/rust-sdk/pagination/dynamic-snippets/example27.rs b/seed/rust-sdk/pagination/dynamic-snippets/example27.rs new file mode 100644 index 000000000000..5da8f67befc8 --- /dev/null +++ b/seed/rust-sdk/pagination/dynamic-snippets/example27.rs @@ -0,0 +1,15 @@ +use seed_pagination::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + base_url: "https://api.fern.com".to_string(), + token: Some("".to_string()), + ..Default::default() + }; + let client = PaginationClient::new(config).expect("Failed to build client"); + client + .users + .list_with_optional_data(&ListWithOptionalDataQueryRequest { page: Some(1) }, None) + .await; +} diff --git a/seed/rust-sdk/pagination/dynamic-snippets/example28.rs b/seed/rust-sdk/pagination/dynamic-snippets/example28.rs new file mode 100644 index 000000000000..5da8f67befc8 --- /dev/null +++ b/seed/rust-sdk/pagination/dynamic-snippets/example28.rs @@ -0,0 +1,15 @@ +use seed_pagination::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + base_url: "https://api.fern.com".to_string(), + token: Some("".to_string()), + ..Default::default() + }; + let client = PaginationClient::new(config).expect("Failed to build client"); + client + .users + .list_with_optional_data(&ListWithOptionalDataQueryRequest { page: Some(1) }, None) + .await; +} diff --git a/seed/rust-sdk/pagination/reference.md b/seed/rust-sdk/pagination/reference.md index e530047a7b32..eac8203ef451 100644 --- a/seed/rust-sdk/pagination/reference.md +++ b/seed/rust-sdk/pagination/reference.md @@ -1684,6 +1684,67 @@ the next page of results. + + + + +
client.users.list_usernames_with_optional_response(starting_after: Option>) -> Result, ApiError> +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```rust +use seed_pagination::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + token: Some("".to_string()), + ..Default::default() + }; + let client = PaginationClient::new(config).expect("Failed to build client"); + client + .users + .list_with_cursor_pagination( + &ListWithCursorPaginationQueryRequest { + starting_after: Some("starting_after".to_string()), + }, + None, + ) + .await; +} +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**starting_after:** `Option` + +The cursor used for pagination in order to fetch +the next page of results. + +
+
+
+
+ +
@@ -1737,6 +1798,59 @@ async fn main() { + + + + +
client.users.list_with_optional_data(page: Option>) -> Result +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```rust +use seed_pagination::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + token: Some("".to_string()), + ..Default::default() + }; + let client = PaginationClient::new(config).expect("Failed to build client"); + client + .users + .list_with_optional_data(&ListWithOptionalDataQueryRequest { page: Some(1) }, None) + .await; +} +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**page:** `Option` — Defaults to first page + +
+
+
+
+ +
diff --git a/seed/rust-sdk/pagination/src/api/resources/users/users.rs b/seed/rust-sdk/pagination/src/api/resources/users/users.rs index ac8b3407ee56..857a6f20dded 100644 --- a/seed/rust-sdk/pagination/src/api/resources/users/users.rs +++ b/seed/rust-sdk/pagination/src/api/resources/users/users.rs @@ -220,6 +220,24 @@ impl UsersClient { .await } + pub async fn list_usernames_with_optional_response( + &self, + request: &ListUsernamesWithOptionalResponseQueryRequest, + options: Option, + ) -> Result, ApiError> { + self.http_client + .execute_request( + Method::GET, + "/users", + None, + QueryBuilder::new() + .string("starting_after", request.starting_after.clone()) + .build(), + options, + ) + .await + } + pub async fn list_with_global_config( &self, request: &UsersListWithGlobalConfigQueryRequest, @@ -237,4 +255,22 @@ impl UsersClient { ) .await } + + pub async fn list_with_optional_data( + &self, + request: &ListWithOptionalDataQueryRequest, + options: Option, + ) -> Result { + self.http_client + .execute_request( + Method::GET, + "/users/optional-data", + None, + QueryBuilder::new() + .int("page", request.page.clone()) + .build(), + options, + ) + .await + } } diff --git a/seed/rust-sdk/pagination/src/api/types/list_usernames_with_optional_response_query_request.rs b/seed/rust-sdk/pagination/src/api/types/list_usernames_with_optional_response_query_request.rs new file mode 100644 index 000000000000..2758acc538e8 --- /dev/null +++ b/seed/rust-sdk/pagination/src/api/types/list_usernames_with_optional_response_query_request.rs @@ -0,0 +1,12 @@ +pub use crate::prelude::*; + +/// Query parameters for listUsernamesWithOptionalResponse +/// +/// Request type for the ListUsernamesWithOptionalResponseQueryRequest operation. +#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq, Hash)] +pub struct ListUsernamesWithOptionalResponseQueryRequest { + /// The cursor used for pagination in order to fetch + /// the next page of results. + #[serde(skip_serializing_if = "Option::is_none")] + pub starting_after: Option, +} diff --git a/seed/rust-sdk/pagination/src/api/types/list_with_optional_data_query_request.rs b/seed/rust-sdk/pagination/src/api/types/list_with_optional_data_query_request.rs new file mode 100644 index 000000000000..5752cf7119b9 --- /dev/null +++ b/seed/rust-sdk/pagination/src/api/types/list_with_optional_data_query_request.rs @@ -0,0 +1,11 @@ +pub use crate::prelude::*; + +/// Query parameters for listWithOptionalData +/// +/// Request type for the ListWithOptionalDataQueryRequest operation. +#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq, Hash)] +pub struct ListWithOptionalDataQueryRequest { + /// Defaults to first page + #[serde(skip_serializing_if = "Option::is_none")] + pub page: Option, +} diff --git a/seed/rust-sdk/pagination/src/api/types/mod.rs b/seed/rust-sdk/pagination/src/api/types/mod.rs index 53a0e4ace3c8..819f660468ea 100644 --- a/seed/rust-sdk/pagination/src/api/types/mod.rs +++ b/seed/rust-sdk/pagination/src/api/types/mod.rs @@ -35,16 +35,19 @@ pub mod inline_users_inline_users_username_container; pub mod inline_users_inline_users_users; pub mod inline_users_inline_users_with_cursor; pub mod inline_users_inline_users_with_page; +pub mod list_usernames_with_optional_response_query_request; pub mod list_users_body_cursor_pagination_request; pub mod list_users_body_cursor_pagination_request_request; pub mod list_users_body_offset_pagination_request; pub mod list_users_body_offset_pagination_request_request; +pub mod list_with_optional_data_query_request; pub mod username_cursor; pub mod username_page; pub mod users_list_usernames_query_request; pub mod users_list_users_extended_optional_list_response; pub mod users_list_users_extended_response; pub mod users_list_users_mixed_type_pagination_response; +pub mod users_list_users_optional_data_pagination_response; pub mod users_list_users_pagination_response; pub mod users_list_with_cursor_pagination_query_request; pub mod users_list_with_double_offset_pagination_query_request; @@ -104,16 +107,19 @@ pub use inline_users_inline_users_username_container::UsernameContainer; pub use inline_users_inline_users_users::Users; pub use inline_users_inline_users_with_cursor::WithCursor; pub use inline_users_inline_users_with_page::WithPage; +pub use list_usernames_with_optional_response_query_request::ListUsernamesWithOptionalResponseQueryRequest; pub use list_users_body_cursor_pagination_request::ListUsersBodyCursorPaginationRequest; pub use list_users_body_cursor_pagination_request_request::ListUsersBodyCursorPaginationRequest2; pub use list_users_body_offset_pagination_request::ListUsersBodyOffsetPaginationRequest; pub use list_users_body_offset_pagination_request_request::ListUsersBodyOffsetPaginationRequest2; +pub use list_with_optional_data_query_request::ListWithOptionalDataQueryRequest; pub use username_cursor::UsernameCursor; pub use username_page::UsernamePage; pub use users_list_usernames_query_request::UsersListUsernamesQueryRequest; pub use users_list_users_extended_optional_list_response::ListUsersExtendedOptionalListResponse2; pub use users_list_users_extended_response::ListUsersExtendedResponse2; pub use users_list_users_mixed_type_pagination_response::ListUsersMixedTypePaginationResponse2; +pub use users_list_users_optional_data_pagination_response::ListUsersOptionalDataPaginationResponse; pub use users_list_users_pagination_response::ListUsersPaginationResponse2; pub use users_list_with_cursor_pagination_query_request::UsersListWithCursorPaginationQueryRequest; pub use users_list_with_double_offset_pagination_query_request::UsersListWithDoubleOffsetPaginationQueryRequest; diff --git a/seed/rust-sdk/pagination/src/api/types/users_list_users_optional_data_pagination_response.rs b/seed/rust-sdk/pagination/src/api/types/users_list_users_optional_data_pagination_response.rs new file mode 100644 index 000000000000..8ea029a87c70 --- /dev/null +++ b/seed/rust-sdk/pagination/src/api/types/users_list_users_optional_data_pagination_response.rs @@ -0,0 +1,14 @@ +pub use crate::prelude::*; + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub struct ListUsersOptionalDataPaginationResponse { + #[serde(rename = "hasNextPage")] + #[serde(skip_serializing_if = "Option::is_none")] + pub has_next_page: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub page: Option, + /// The totall number of /users + pub total_count: i64, + #[serde(skip_serializing_if = "Option::is_none")] + pub data: Option>, +} diff --git a/seed/swift-sdk/pagination/custom-pager-with-exception-handler/Snippets/Example26.swift b/seed/swift-sdk/pagination/custom-pager-with-exception-handler/Snippets/Example26.swift new file mode 100644 index 000000000000..b81b0ded0bc8 --- /dev/null +++ b/seed/swift-sdk/pagination/custom-pager-with-exception-handler/Snippets/Example26.swift @@ -0,0 +1,13 @@ +import Foundation +import Pagination + +private func main() async throws { + let client = PaginationClient( + baseURL: "https://api.fern.com", + token: "" + ) + + _ = try await client.users.listWithOptionalData(page: 1) +} + +try await main() diff --git a/seed/swift-sdk/pagination/custom-pager-with-exception-handler/Snippets/Example27.swift b/seed/swift-sdk/pagination/custom-pager-with-exception-handler/Snippets/Example27.swift new file mode 100644 index 000000000000..b81b0ded0bc8 --- /dev/null +++ b/seed/swift-sdk/pagination/custom-pager-with-exception-handler/Snippets/Example27.swift @@ -0,0 +1,13 @@ +import Foundation +import Pagination + +private func main() async throws { + let client = PaginationClient( + baseURL: "https://api.fern.com", + token: "" + ) + + _ = try await client.users.listWithOptionalData(page: 1) +} + +try await main() diff --git a/seed/swift-sdk/pagination/custom-pager-with-exception-handler/Snippets/Example28.swift b/seed/swift-sdk/pagination/custom-pager-with-exception-handler/Snippets/Example28.swift new file mode 100644 index 000000000000..b81b0ded0bc8 --- /dev/null +++ b/seed/swift-sdk/pagination/custom-pager-with-exception-handler/Snippets/Example28.swift @@ -0,0 +1,13 @@ +import Foundation +import Pagination + +private func main() async throws { + let client = PaginationClient( + baseURL: "https://api.fern.com", + token: "" + ) + + _ = try await client.users.listWithOptionalData(page: 1) +} + +try await main() diff --git a/seed/swift-sdk/pagination/custom-pager-with-exception-handler/Sources/Resources/Users/UsersClient.swift b/seed/swift-sdk/pagination/custom-pager-with-exception-handler/Sources/Resources/Users/UsersClient.swift index a7b7c8f401a4..1458de7d7672 100644 --- a/seed/swift-sdk/pagination/custom-pager-with-exception-handler/Sources/Resources/Users/UsersClient.swift +++ b/seed/swift-sdk/pagination/custom-pager-with-exception-handler/Sources/Resources/Users/UsersClient.swift @@ -171,4 +171,16 @@ public final class UsersClient: Sendable { responseType: UsernameContainerType.self ) } + + public func listWithOptionalData(page: Int? = nil, requestOptions: RequestOptions? = nil) async throws -> ListUsersOptionalDataPaginationResponse { + return try await httpClient.performRequest( + method: .get, + path: "/users/optional-data", + queryParams: [ + "page": page.map { .int($0) } + ], + requestOptions: requestOptions, + responseType: ListUsersOptionalDataPaginationResponse.self + ) + } } \ No newline at end of file diff --git a/seed/swift-sdk/pagination/custom-pager-with-exception-handler/Sources/Schemas/ListUsersOptionalDataPaginationResponse.swift b/seed/swift-sdk/pagination/custom-pager-with-exception-handler/Sources/Schemas/ListUsersOptionalDataPaginationResponse.swift new file mode 100644 index 000000000000..e989c9895a6e --- /dev/null +++ b/seed/swift-sdk/pagination/custom-pager-with-exception-handler/Sources/Schemas/ListUsersOptionalDataPaginationResponse.swift @@ -0,0 +1,51 @@ +import Foundation + +public struct ListUsersOptionalDataPaginationResponse: Codable, Hashable, Sendable { + public let hasNextPage: Bool? + public let page: PageType? + /// The totall number of /users + public let totalCount: Int + public let data: [UserType]? + /// Additional properties that are not explicitly defined in the schema + public let additionalProperties: [String: JSONValue] + + public init( + hasNextPage: Bool? = nil, + page: PageType? = nil, + totalCount: Int, + data: [UserType]? = nil, + additionalProperties: [String: JSONValue] = .init() + ) { + self.hasNextPage = hasNextPage + self.page = page + self.totalCount = totalCount + self.data = data + self.additionalProperties = additionalProperties + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.hasNextPage = try container.decodeIfPresent(Bool.self, forKey: .hasNextPage) + self.page = try container.decodeIfPresent(PageType.self, forKey: .page) + self.totalCount = try container.decode(Int.self, forKey: .totalCount) + self.data = try container.decodeIfPresent([UserType].self, forKey: .data) + self.additionalProperties = try decoder.decodeAdditionalProperties(using: CodingKeys.self) + } + + public func encode(to encoder: Encoder) throws -> Void { + var container = encoder.container(keyedBy: CodingKeys.self) + try encoder.encodeAdditionalProperties(self.additionalProperties) + try container.encodeIfPresent(self.hasNextPage, forKey: .hasNextPage) + try container.encodeIfPresent(self.page, forKey: .page) + try container.encode(self.totalCount, forKey: .totalCount) + try container.encodeIfPresent(self.data, forKey: .data) + } + + /// Keys for encoding/decoding struct properties. + enum CodingKeys: String, CodingKey, CaseIterable { + case hasNextPage + case page + case totalCount = "total_count" + case data + } +} \ No newline at end of file diff --git a/seed/swift-sdk/pagination/custom-pager-with-exception-handler/Tests/Wire/Resources/Users/UsersClientWireTests.swift b/seed/swift-sdk/pagination/custom-pager-with-exception-handler/Tests/Wire/Resources/Users/UsersClientWireTests.swift index 2e4ae61e040f..36808d2a8408 100644 --- a/seed/swift-sdk/pagination/custom-pager-with-exception-handler/Tests/Wire/Resources/Users/UsersClientWireTests.swift +++ b/seed/swift-sdk/pagination/custom-pager-with-exception-handler/Tests/Wire/Resources/Users/UsersClientWireTests.swift @@ -739,4 +739,182 @@ import Pagination ) try #require(response == expectedResponse) } + + @Test func listWithOptionalData1() async throws -> Void { + let stub = HTTPStub() + stub.setResponse( + body: Data( + """ + { + "hasNextPage": true, + "page": { + "page": 1, + "next": { + "page": 2, + "starting_after": "next_cursor" + }, + "per_page": 10, + "total_page": 5 + }, + "total_count": 50, + "data": [ + { + "name": "Alice", + "id": 1 + }, + { + "name": "Bob", + "id": 2 + } + ] + } + """.utf8 + ) + ) + let client = PaginationClient( + baseURL: "https://api.fern.com", + token: "", + urlSession: stub.urlSession + ) + let expectedResponse = ListUsersOptionalDataPaginationResponse( + hasNextPage: Optional(true), + page: Optional(PageType( + page: 1, + next: Optional(NextPageType( + page: 2, + startingAfter: "next_cursor" + )), + perPage: 10, + totalPage: 5 + )), + totalCount: 50, + data: Optional([ + UserType( + name: "Alice", + id: 1 + ), + UserType( + name: "Bob", + id: 2 + ) + ]) + ) + let response = try await client.users.listWithOptionalData( + page: 1, + requestOptions: RequestOptions(additionalHeaders: stub.headers) + ) + try #require(response == expectedResponse) + } + + @Test func listWithOptionalData2() async throws -> Void { + let stub = HTTPStub() + stub.setResponse( + body: Data( + """ + { + "hasNextPage": false, + "page": { + "page": 1, + "next": { + "page": 2, + "starting_after": "next_cursor" + }, + "per_page": 10, + "total_page": 1 + }, + "total_count": 0 + } + """.utf8 + ) + ) + let client = PaginationClient( + baseURL: "https://api.fern.com", + token: "", + urlSession: stub.urlSession + ) + let expectedResponse = ListUsersOptionalDataPaginationResponse( + hasNextPage: Optional(false), + page: Optional(PageType( + page: 1, + next: Optional(NextPageType( + page: 2, + startingAfter: "next_cursor" + )), + perPage: 10, + totalPage: 1 + )), + totalCount: 0 + ) + let response = try await client.users.listWithOptionalData( + page: 1, + requestOptions: RequestOptions(additionalHeaders: stub.headers) + ) + try #require(response == expectedResponse) + } + + @Test func listWithOptionalData3() async throws -> Void { + let stub = HTTPStub() + stub.setResponse( + body: Data( + """ + { + "hasNextPage": true, + "page": { + "page": 1, + "next": { + "page": 1, + "starting_after": "starting_after" + }, + "per_page": 1, + "total_page": 1 + }, + "total_count": 1, + "data": [ + { + "name": "name", + "id": 1 + }, + { + "name": "name", + "id": 1 + } + ] + } + """.utf8 + ) + ) + let client = PaginationClient( + baseURL: "https://api.fern.com", + token: "", + urlSession: stub.urlSession + ) + let expectedResponse = ListUsersOptionalDataPaginationResponse( + hasNextPage: Optional(true), + page: Optional(PageType( + page: 1, + next: Optional(NextPageType( + page: 1, + startingAfter: "starting_after" + )), + perPage: 1, + totalPage: 1 + )), + totalCount: 1, + data: Optional([ + UserType( + name: "name", + id: 1 + ), + UserType( + name: "name", + id: 1 + ) + ]) + ) + let response = try await client.users.listWithOptionalData( + page: 1, + requestOptions: RequestOptions(additionalHeaders: stub.headers) + ) + try #require(response == expectedResponse) + } } \ No newline at end of file diff --git a/seed/swift-sdk/pagination/custom-pager-with-exception-handler/reference.md b/seed/swift-sdk/pagination/custom-pager-with-exception-handler/reference.md index 3b5d2a1723e9..ddf0adad9209 100644 --- a/seed/swift-sdk/pagination/custom-pager-with-exception-handler/reference.md +++ b/seed/swift-sdk/pagination/custom-pager-with-exception-handler/reference.md @@ -1800,3 +1800,60 @@ try await main() +
client.users.listWithOptionalData(page: Int?, requestOptions: RequestOptions?) -> ListUsersOptionalDataPaginationResponse +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```swift +import Foundation +import Pagination + +private func main() async throws { + let client = PaginationClient(token: "") + + _ = try await client.users.listWithOptionalData(page: 1) +} + +try await main() +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**page:** `Int?` — Defaults to first page + +
+
+ +
+
+ +**requestOptions:** `RequestOptions?` — Additional options for configuring the request, such as custom headers or timeout settings. + +
+
+
+
+ + +
+
+
+ diff --git a/seed/swift-sdk/pagination/custom-pager/Snippets/Example26.swift b/seed/swift-sdk/pagination/custom-pager/Snippets/Example26.swift new file mode 100644 index 000000000000..b81b0ded0bc8 --- /dev/null +++ b/seed/swift-sdk/pagination/custom-pager/Snippets/Example26.swift @@ -0,0 +1,13 @@ +import Foundation +import Pagination + +private func main() async throws { + let client = PaginationClient( + baseURL: "https://api.fern.com", + token: "" + ) + + _ = try await client.users.listWithOptionalData(page: 1) +} + +try await main() diff --git a/seed/swift-sdk/pagination/custom-pager/Snippets/Example27.swift b/seed/swift-sdk/pagination/custom-pager/Snippets/Example27.swift new file mode 100644 index 000000000000..b81b0ded0bc8 --- /dev/null +++ b/seed/swift-sdk/pagination/custom-pager/Snippets/Example27.swift @@ -0,0 +1,13 @@ +import Foundation +import Pagination + +private func main() async throws { + let client = PaginationClient( + baseURL: "https://api.fern.com", + token: "" + ) + + _ = try await client.users.listWithOptionalData(page: 1) +} + +try await main() diff --git a/seed/swift-sdk/pagination/custom-pager/Snippets/Example28.swift b/seed/swift-sdk/pagination/custom-pager/Snippets/Example28.swift new file mode 100644 index 000000000000..b81b0ded0bc8 --- /dev/null +++ b/seed/swift-sdk/pagination/custom-pager/Snippets/Example28.swift @@ -0,0 +1,13 @@ +import Foundation +import Pagination + +private func main() async throws { + let client = PaginationClient( + baseURL: "https://api.fern.com", + token: "" + ) + + _ = try await client.users.listWithOptionalData(page: 1) +} + +try await main() diff --git a/seed/swift-sdk/pagination/custom-pager/Sources/Resources/Users/UsersClient.swift b/seed/swift-sdk/pagination/custom-pager/Sources/Resources/Users/UsersClient.swift index a7b7c8f401a4..1458de7d7672 100644 --- a/seed/swift-sdk/pagination/custom-pager/Sources/Resources/Users/UsersClient.swift +++ b/seed/swift-sdk/pagination/custom-pager/Sources/Resources/Users/UsersClient.swift @@ -171,4 +171,16 @@ public final class UsersClient: Sendable { responseType: UsernameContainerType.self ) } + + public func listWithOptionalData(page: Int? = nil, requestOptions: RequestOptions? = nil) async throws -> ListUsersOptionalDataPaginationResponse { + return try await httpClient.performRequest( + method: .get, + path: "/users/optional-data", + queryParams: [ + "page": page.map { .int($0) } + ], + requestOptions: requestOptions, + responseType: ListUsersOptionalDataPaginationResponse.self + ) + } } \ No newline at end of file diff --git a/seed/swift-sdk/pagination/custom-pager/Sources/Schemas/ListUsersOptionalDataPaginationResponse.swift b/seed/swift-sdk/pagination/custom-pager/Sources/Schemas/ListUsersOptionalDataPaginationResponse.swift new file mode 100644 index 000000000000..e989c9895a6e --- /dev/null +++ b/seed/swift-sdk/pagination/custom-pager/Sources/Schemas/ListUsersOptionalDataPaginationResponse.swift @@ -0,0 +1,51 @@ +import Foundation + +public struct ListUsersOptionalDataPaginationResponse: Codable, Hashable, Sendable { + public let hasNextPage: Bool? + public let page: PageType? + /// The totall number of /users + public let totalCount: Int + public let data: [UserType]? + /// Additional properties that are not explicitly defined in the schema + public let additionalProperties: [String: JSONValue] + + public init( + hasNextPage: Bool? = nil, + page: PageType? = nil, + totalCount: Int, + data: [UserType]? = nil, + additionalProperties: [String: JSONValue] = .init() + ) { + self.hasNextPage = hasNextPage + self.page = page + self.totalCount = totalCount + self.data = data + self.additionalProperties = additionalProperties + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.hasNextPage = try container.decodeIfPresent(Bool.self, forKey: .hasNextPage) + self.page = try container.decodeIfPresent(PageType.self, forKey: .page) + self.totalCount = try container.decode(Int.self, forKey: .totalCount) + self.data = try container.decodeIfPresent([UserType].self, forKey: .data) + self.additionalProperties = try decoder.decodeAdditionalProperties(using: CodingKeys.self) + } + + public func encode(to encoder: Encoder) throws -> Void { + var container = encoder.container(keyedBy: CodingKeys.self) + try encoder.encodeAdditionalProperties(self.additionalProperties) + try container.encodeIfPresent(self.hasNextPage, forKey: .hasNextPage) + try container.encodeIfPresent(self.page, forKey: .page) + try container.encode(self.totalCount, forKey: .totalCount) + try container.encodeIfPresent(self.data, forKey: .data) + } + + /// Keys for encoding/decoding struct properties. + enum CodingKeys: String, CodingKey, CaseIterable { + case hasNextPage + case page + case totalCount = "total_count" + case data + } +} \ No newline at end of file diff --git a/seed/swift-sdk/pagination/custom-pager/Tests/Wire/Resources/Users/UsersClientWireTests.swift b/seed/swift-sdk/pagination/custom-pager/Tests/Wire/Resources/Users/UsersClientWireTests.swift index 2e4ae61e040f..36808d2a8408 100644 --- a/seed/swift-sdk/pagination/custom-pager/Tests/Wire/Resources/Users/UsersClientWireTests.swift +++ b/seed/swift-sdk/pagination/custom-pager/Tests/Wire/Resources/Users/UsersClientWireTests.swift @@ -739,4 +739,182 @@ import Pagination ) try #require(response == expectedResponse) } + + @Test func listWithOptionalData1() async throws -> Void { + let stub = HTTPStub() + stub.setResponse( + body: Data( + """ + { + "hasNextPage": true, + "page": { + "page": 1, + "next": { + "page": 2, + "starting_after": "next_cursor" + }, + "per_page": 10, + "total_page": 5 + }, + "total_count": 50, + "data": [ + { + "name": "Alice", + "id": 1 + }, + { + "name": "Bob", + "id": 2 + } + ] + } + """.utf8 + ) + ) + let client = PaginationClient( + baseURL: "https://api.fern.com", + token: "", + urlSession: stub.urlSession + ) + let expectedResponse = ListUsersOptionalDataPaginationResponse( + hasNextPage: Optional(true), + page: Optional(PageType( + page: 1, + next: Optional(NextPageType( + page: 2, + startingAfter: "next_cursor" + )), + perPage: 10, + totalPage: 5 + )), + totalCount: 50, + data: Optional([ + UserType( + name: "Alice", + id: 1 + ), + UserType( + name: "Bob", + id: 2 + ) + ]) + ) + let response = try await client.users.listWithOptionalData( + page: 1, + requestOptions: RequestOptions(additionalHeaders: stub.headers) + ) + try #require(response == expectedResponse) + } + + @Test func listWithOptionalData2() async throws -> Void { + let stub = HTTPStub() + stub.setResponse( + body: Data( + """ + { + "hasNextPage": false, + "page": { + "page": 1, + "next": { + "page": 2, + "starting_after": "next_cursor" + }, + "per_page": 10, + "total_page": 1 + }, + "total_count": 0 + } + """.utf8 + ) + ) + let client = PaginationClient( + baseURL: "https://api.fern.com", + token: "", + urlSession: stub.urlSession + ) + let expectedResponse = ListUsersOptionalDataPaginationResponse( + hasNextPage: Optional(false), + page: Optional(PageType( + page: 1, + next: Optional(NextPageType( + page: 2, + startingAfter: "next_cursor" + )), + perPage: 10, + totalPage: 1 + )), + totalCount: 0 + ) + let response = try await client.users.listWithOptionalData( + page: 1, + requestOptions: RequestOptions(additionalHeaders: stub.headers) + ) + try #require(response == expectedResponse) + } + + @Test func listWithOptionalData3() async throws -> Void { + let stub = HTTPStub() + stub.setResponse( + body: Data( + """ + { + "hasNextPage": true, + "page": { + "page": 1, + "next": { + "page": 1, + "starting_after": "starting_after" + }, + "per_page": 1, + "total_page": 1 + }, + "total_count": 1, + "data": [ + { + "name": "name", + "id": 1 + }, + { + "name": "name", + "id": 1 + } + ] + } + """.utf8 + ) + ) + let client = PaginationClient( + baseURL: "https://api.fern.com", + token: "", + urlSession: stub.urlSession + ) + let expectedResponse = ListUsersOptionalDataPaginationResponse( + hasNextPage: Optional(true), + page: Optional(PageType( + page: 1, + next: Optional(NextPageType( + page: 1, + startingAfter: "starting_after" + )), + perPage: 1, + totalPage: 1 + )), + totalCount: 1, + data: Optional([ + UserType( + name: "name", + id: 1 + ), + UserType( + name: "name", + id: 1 + ) + ]) + ) + let response = try await client.users.listWithOptionalData( + page: 1, + requestOptions: RequestOptions(additionalHeaders: stub.headers) + ) + try #require(response == expectedResponse) + } } \ No newline at end of file diff --git a/seed/swift-sdk/pagination/custom-pager/reference.md b/seed/swift-sdk/pagination/custom-pager/reference.md index 3b5d2a1723e9..ddf0adad9209 100644 --- a/seed/swift-sdk/pagination/custom-pager/reference.md +++ b/seed/swift-sdk/pagination/custom-pager/reference.md @@ -1800,3 +1800,60 @@ try await main() +
client.users.listWithOptionalData(page: Int?, requestOptions: RequestOptions?) -> ListUsersOptionalDataPaginationResponse +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```swift +import Foundation +import Pagination + +private func main() async throws { + let client = PaginationClient(token: "") + + _ = try await client.users.listWithOptionalData(page: 1) +} + +try await main() +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**page:** `Int?` — Defaults to first page + +
+
+ +
+
+ +**requestOptions:** `RequestOptions?` — Additional options for configuring the request, such as custom headers or timeout settings. + +
+
+
+
+ + +
+
+
+ diff --git a/seed/swift-sdk/pagination/no-custom-config/Snippets/Example26.swift b/seed/swift-sdk/pagination/no-custom-config/Snippets/Example26.swift new file mode 100644 index 000000000000..b81b0ded0bc8 --- /dev/null +++ b/seed/swift-sdk/pagination/no-custom-config/Snippets/Example26.swift @@ -0,0 +1,13 @@ +import Foundation +import Pagination + +private func main() async throws { + let client = PaginationClient( + baseURL: "https://api.fern.com", + token: "" + ) + + _ = try await client.users.listWithOptionalData(page: 1) +} + +try await main() diff --git a/seed/swift-sdk/pagination/no-custom-config/Snippets/Example27.swift b/seed/swift-sdk/pagination/no-custom-config/Snippets/Example27.swift new file mode 100644 index 000000000000..b81b0ded0bc8 --- /dev/null +++ b/seed/swift-sdk/pagination/no-custom-config/Snippets/Example27.swift @@ -0,0 +1,13 @@ +import Foundation +import Pagination + +private func main() async throws { + let client = PaginationClient( + baseURL: "https://api.fern.com", + token: "" + ) + + _ = try await client.users.listWithOptionalData(page: 1) +} + +try await main() diff --git a/seed/swift-sdk/pagination/no-custom-config/Snippets/Example28.swift b/seed/swift-sdk/pagination/no-custom-config/Snippets/Example28.swift new file mode 100644 index 000000000000..b81b0ded0bc8 --- /dev/null +++ b/seed/swift-sdk/pagination/no-custom-config/Snippets/Example28.swift @@ -0,0 +1,13 @@ +import Foundation +import Pagination + +private func main() async throws { + let client = PaginationClient( + baseURL: "https://api.fern.com", + token: "" + ) + + _ = try await client.users.listWithOptionalData(page: 1) +} + +try await main() diff --git a/seed/swift-sdk/pagination/no-custom-config/Sources/Resources/Users/UsersClient.swift b/seed/swift-sdk/pagination/no-custom-config/Sources/Resources/Users/UsersClient.swift index a7b7c8f401a4..1458de7d7672 100644 --- a/seed/swift-sdk/pagination/no-custom-config/Sources/Resources/Users/UsersClient.swift +++ b/seed/swift-sdk/pagination/no-custom-config/Sources/Resources/Users/UsersClient.swift @@ -171,4 +171,16 @@ public final class UsersClient: Sendable { responseType: UsernameContainerType.self ) } + + public func listWithOptionalData(page: Int? = nil, requestOptions: RequestOptions? = nil) async throws -> ListUsersOptionalDataPaginationResponse { + return try await httpClient.performRequest( + method: .get, + path: "/users/optional-data", + queryParams: [ + "page": page.map { .int($0) } + ], + requestOptions: requestOptions, + responseType: ListUsersOptionalDataPaginationResponse.self + ) + } } \ No newline at end of file diff --git a/seed/swift-sdk/pagination/no-custom-config/Sources/Schemas/ListUsersOptionalDataPaginationResponse.swift b/seed/swift-sdk/pagination/no-custom-config/Sources/Schemas/ListUsersOptionalDataPaginationResponse.swift new file mode 100644 index 000000000000..e989c9895a6e --- /dev/null +++ b/seed/swift-sdk/pagination/no-custom-config/Sources/Schemas/ListUsersOptionalDataPaginationResponse.swift @@ -0,0 +1,51 @@ +import Foundation + +public struct ListUsersOptionalDataPaginationResponse: Codable, Hashable, Sendable { + public let hasNextPage: Bool? + public let page: PageType? + /// The totall number of /users + public let totalCount: Int + public let data: [UserType]? + /// Additional properties that are not explicitly defined in the schema + public let additionalProperties: [String: JSONValue] + + public init( + hasNextPage: Bool? = nil, + page: PageType? = nil, + totalCount: Int, + data: [UserType]? = nil, + additionalProperties: [String: JSONValue] = .init() + ) { + self.hasNextPage = hasNextPage + self.page = page + self.totalCount = totalCount + self.data = data + self.additionalProperties = additionalProperties + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.hasNextPage = try container.decodeIfPresent(Bool.self, forKey: .hasNextPage) + self.page = try container.decodeIfPresent(PageType.self, forKey: .page) + self.totalCount = try container.decode(Int.self, forKey: .totalCount) + self.data = try container.decodeIfPresent([UserType].self, forKey: .data) + self.additionalProperties = try decoder.decodeAdditionalProperties(using: CodingKeys.self) + } + + public func encode(to encoder: Encoder) throws -> Void { + var container = encoder.container(keyedBy: CodingKeys.self) + try encoder.encodeAdditionalProperties(self.additionalProperties) + try container.encodeIfPresent(self.hasNextPage, forKey: .hasNextPage) + try container.encodeIfPresent(self.page, forKey: .page) + try container.encode(self.totalCount, forKey: .totalCount) + try container.encodeIfPresent(self.data, forKey: .data) + } + + /// Keys for encoding/decoding struct properties. + enum CodingKeys: String, CodingKey, CaseIterable { + case hasNextPage + case page + case totalCount = "total_count" + case data + } +} \ No newline at end of file diff --git a/seed/swift-sdk/pagination/no-custom-config/Tests/Wire/Resources/Users/UsersClientWireTests.swift b/seed/swift-sdk/pagination/no-custom-config/Tests/Wire/Resources/Users/UsersClientWireTests.swift index 2e4ae61e040f..36808d2a8408 100644 --- a/seed/swift-sdk/pagination/no-custom-config/Tests/Wire/Resources/Users/UsersClientWireTests.swift +++ b/seed/swift-sdk/pagination/no-custom-config/Tests/Wire/Resources/Users/UsersClientWireTests.swift @@ -739,4 +739,182 @@ import Pagination ) try #require(response == expectedResponse) } + + @Test func listWithOptionalData1() async throws -> Void { + let stub = HTTPStub() + stub.setResponse( + body: Data( + """ + { + "hasNextPage": true, + "page": { + "page": 1, + "next": { + "page": 2, + "starting_after": "next_cursor" + }, + "per_page": 10, + "total_page": 5 + }, + "total_count": 50, + "data": [ + { + "name": "Alice", + "id": 1 + }, + { + "name": "Bob", + "id": 2 + } + ] + } + """.utf8 + ) + ) + let client = PaginationClient( + baseURL: "https://api.fern.com", + token: "", + urlSession: stub.urlSession + ) + let expectedResponse = ListUsersOptionalDataPaginationResponse( + hasNextPage: Optional(true), + page: Optional(PageType( + page: 1, + next: Optional(NextPageType( + page: 2, + startingAfter: "next_cursor" + )), + perPage: 10, + totalPage: 5 + )), + totalCount: 50, + data: Optional([ + UserType( + name: "Alice", + id: 1 + ), + UserType( + name: "Bob", + id: 2 + ) + ]) + ) + let response = try await client.users.listWithOptionalData( + page: 1, + requestOptions: RequestOptions(additionalHeaders: stub.headers) + ) + try #require(response == expectedResponse) + } + + @Test func listWithOptionalData2() async throws -> Void { + let stub = HTTPStub() + stub.setResponse( + body: Data( + """ + { + "hasNextPage": false, + "page": { + "page": 1, + "next": { + "page": 2, + "starting_after": "next_cursor" + }, + "per_page": 10, + "total_page": 1 + }, + "total_count": 0 + } + """.utf8 + ) + ) + let client = PaginationClient( + baseURL: "https://api.fern.com", + token: "", + urlSession: stub.urlSession + ) + let expectedResponse = ListUsersOptionalDataPaginationResponse( + hasNextPage: Optional(false), + page: Optional(PageType( + page: 1, + next: Optional(NextPageType( + page: 2, + startingAfter: "next_cursor" + )), + perPage: 10, + totalPage: 1 + )), + totalCount: 0 + ) + let response = try await client.users.listWithOptionalData( + page: 1, + requestOptions: RequestOptions(additionalHeaders: stub.headers) + ) + try #require(response == expectedResponse) + } + + @Test func listWithOptionalData3() async throws -> Void { + let stub = HTTPStub() + stub.setResponse( + body: Data( + """ + { + "hasNextPage": true, + "page": { + "page": 1, + "next": { + "page": 1, + "starting_after": "starting_after" + }, + "per_page": 1, + "total_page": 1 + }, + "total_count": 1, + "data": [ + { + "name": "name", + "id": 1 + }, + { + "name": "name", + "id": 1 + } + ] + } + """.utf8 + ) + ) + let client = PaginationClient( + baseURL: "https://api.fern.com", + token: "", + urlSession: stub.urlSession + ) + let expectedResponse = ListUsersOptionalDataPaginationResponse( + hasNextPage: Optional(true), + page: Optional(PageType( + page: 1, + next: Optional(NextPageType( + page: 1, + startingAfter: "starting_after" + )), + perPage: 1, + totalPage: 1 + )), + totalCount: 1, + data: Optional([ + UserType( + name: "name", + id: 1 + ), + UserType( + name: "name", + id: 1 + ) + ]) + ) + let response = try await client.users.listWithOptionalData( + page: 1, + requestOptions: RequestOptions(additionalHeaders: stub.headers) + ) + try #require(response == expectedResponse) + } } \ No newline at end of file diff --git a/seed/swift-sdk/pagination/no-custom-config/reference.md b/seed/swift-sdk/pagination/no-custom-config/reference.md index 3b5d2a1723e9..ddf0adad9209 100644 --- a/seed/swift-sdk/pagination/no-custom-config/reference.md +++ b/seed/swift-sdk/pagination/no-custom-config/reference.md @@ -1800,3 +1800,60 @@ try await main() +
client.users.listWithOptionalData(page: Int?, requestOptions: RequestOptions?) -> ListUsersOptionalDataPaginationResponse +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```swift +import Foundation +import Pagination + +private func main() async throws { + let client = PaginationClient(token: "") + + _ = try await client.users.listWithOptionalData(page: 1) +} + +try await main() +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**page:** `Int?` — Defaults to first page + +
+
+ +
+
+ +**requestOptions:** `RequestOptions?` — Additional options for configuring the request, such as custom headers or timeout settings. + +
+
+
+
+ + +
+
+
+ diff --git a/seed/ts-express/inferred-auth-implicit-api-key/api/index.ts b/seed/ts-express/inferred-auth-implicit-api-key/api/index.ts new file mode 100644 index 000000000000..3e5335fe4215 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/api/index.ts @@ -0,0 +1 @@ +export * from "./resources"; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/api/resources/auth/index.ts b/seed/ts-express/inferred-auth-implicit-api-key/api/resources/auth/index.ts new file mode 100644 index 000000000000..4593a61f8b42 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/api/resources/auth/index.ts @@ -0,0 +1,2 @@ +export * from "./service"; +export * from "./types"; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/api/resources/auth/service/AuthService.ts b/seed/ts-express/inferred-auth-implicit-api-key/api/resources/auth/service/AuthService.ts new file mode 100644 index 000000000000..08fcf2be026c --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/api/resources/auth/service/AuthService.ts @@ -0,0 +1,75 @@ +// This file was auto-generated by Fern from our API Definition. + +import express from "express"; +import * as errors from "../../../../errors/index"; +import * as serializers from "../../../../serialization/index"; +import type * as SeedInferredAuthImplicitApiKey from "../../../index"; + +export interface AuthServiceMethods { + getToken( + req: express.Request, + res: { + send: (responseBody: SeedInferredAuthImplicitApiKey.TokenResponse) => Promise; + cookie: (cookie: string, value: string, options?: express.CookieOptions) => void; + locals: any; + }, + next: express.NextFunction, + ): void | Promise; +} + +export class AuthService { + private router; + + constructor( + private readonly methods: AuthServiceMethods, + middleware: express.RequestHandler[] = [], + ) { + this.router = express.Router({ mergeParams: true }).use( + express.json({ + strict: false, + }), + ...middleware, + ); + } + + public addMiddleware(handler: express.RequestHandler): this { + this.router.use(handler); + return this; + } + + public toRouter(): express.Router { + this.router.post("/token", async (req, res, next) => { + try { + await this.methods.getToken( + req as any, + { + send: async (responseBody) => { + res.json( + serializers.TokenResponse.jsonOrThrow(responseBody, { + unrecognizedObjectKeys: "strip", + }), + ); + }, + cookie: res.cookie.bind(res), + locals: res.locals, + }, + next, + ); + if (!res.writableEnded) { + next(); + } + } catch (error) { + if (error instanceof errors.SeedInferredAuthImplicitApiKeyError) { + console.warn( + `Endpoint 'getToken' unexpectedly threw ${error.constructor.name}. If this was intentional, please add ${error.constructor.name} to the endpoint's errors list in your Fern Definition.`, + ); + await error.send(res); + } else { + res.status(500).json("Internal Server Error"); + } + next(error); + } + }); + return this.router; + } +} diff --git a/seed/ts-express/inferred-auth-implicit-api-key/api/resources/auth/service/index.ts b/seed/ts-express/inferred-auth-implicit-api-key/api/resources/auth/service/index.ts new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/api/resources/auth/service/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/api/resources/auth/types/TokenResponse.ts b/seed/ts-express/inferred-auth-implicit-api-key/api/resources/auth/types/TokenResponse.ts new file mode 100644 index 000000000000..bcd5874c6941 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/api/resources/auth/types/TokenResponse.ts @@ -0,0 +1,11 @@ +// This file was auto-generated by Fern from our API Definition. + +/** + * An auth token response. + */ +export interface TokenResponse { + accessToken: string; + tokenType: string; + expiresIn: number; + scope?: string; +} diff --git a/seed/ts-express/inferred-auth-implicit-api-key/api/resources/auth/types/index.ts b/seed/ts-express/inferred-auth-implicit-api-key/api/resources/auth/types/index.ts new file mode 100644 index 000000000000..6848a730c911 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/api/resources/auth/types/index.ts @@ -0,0 +1 @@ +export * from "./TokenResponse"; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/api/resources/index.ts b/seed/ts-express/inferred-auth-implicit-api-key/api/resources/index.ts new file mode 100644 index 000000000000..df3df8f7278b --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/api/resources/index.ts @@ -0,0 +1,5 @@ +export * as auth from "./auth"; +export * from "./auth/types"; +export * as nested from "./nested"; +export * as nestedNoAuth from "./nestedNoAuth"; +export * as simple from "./simple"; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/api/resources/nested/index.ts b/seed/ts-express/inferred-auth-implicit-api-key/api/resources/nested/index.ts new file mode 100644 index 000000000000..3e5335fe4215 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/api/resources/nested/index.ts @@ -0,0 +1 @@ +export * from "./resources"; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/api/resources/nested/resources/api/index.ts b/seed/ts-express/inferred-auth-implicit-api-key/api/resources/nested/resources/api/index.ts new file mode 100644 index 000000000000..6261f8963643 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/api/resources/nested/resources/api/index.ts @@ -0,0 +1 @@ +export * from "./service"; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/api/resources/nested/resources/api/service/ApiService.ts b/seed/ts-express/inferred-auth-implicit-api-key/api/resources/nested/resources/api/service/ApiService.ts new file mode 100644 index 000000000000..3662ecfd2816 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/api/resources/nested/resources/api/service/ApiService.ts @@ -0,0 +1,69 @@ +// This file was auto-generated by Fern from our API Definition. + +import express from "express"; +import * as errors from "../../../../../../errors/index"; + +export interface ApiServiceMethods { + getSomething( + req: express.Request, + res: { + send: () => Promise; + cookie: (cookie: string, value: string, options?: express.CookieOptions) => void; + locals: any; + }, + next: express.NextFunction, + ): void | Promise; +} + +export class ApiService { + private router; + + constructor( + private readonly methods: ApiServiceMethods, + middleware: express.RequestHandler[] = [], + ) { + this.router = express.Router({ mergeParams: true }).use( + express.json({ + strict: false, + }), + ...middleware, + ); + } + + public addMiddleware(handler: express.RequestHandler): this { + this.router.use(handler); + return this; + } + + public toRouter(): express.Router { + this.router.get("/get-something", async (req, res, next) => { + try { + await this.methods.getSomething( + req as any, + { + send: async () => { + res.sendStatus(204); + }, + cookie: res.cookie.bind(res), + locals: res.locals, + }, + next, + ); + if (!res.writableEnded) { + next(); + } + } catch (error) { + if (error instanceof errors.SeedInferredAuthImplicitApiKeyError) { + console.warn( + `Endpoint 'getSomething' unexpectedly threw ${error.constructor.name}. If this was intentional, please add ${error.constructor.name} to the endpoint's errors list in your Fern Definition.`, + ); + await error.send(res); + } else { + res.status(500).json("Internal Server Error"); + } + next(error); + } + }); + return this.router; + } +} diff --git a/seed/ts-express/inferred-auth-implicit-api-key/api/resources/nested/resources/api/service/index.ts b/seed/ts-express/inferred-auth-implicit-api-key/api/resources/nested/resources/api/service/index.ts new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/api/resources/nested/resources/api/service/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/api/resources/nested/resources/index.ts b/seed/ts-express/inferred-auth-implicit-api-key/api/resources/nested/resources/index.ts new file mode 100644 index 000000000000..d7f360220400 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/api/resources/nested/resources/index.ts @@ -0,0 +1 @@ +export * as api from "./api"; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/api/resources/nestedNoAuth/index.ts b/seed/ts-express/inferred-auth-implicit-api-key/api/resources/nestedNoAuth/index.ts new file mode 100644 index 000000000000..3e5335fe4215 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/api/resources/nestedNoAuth/index.ts @@ -0,0 +1 @@ +export * from "./resources"; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/api/resources/nestedNoAuth/resources/api/index.ts b/seed/ts-express/inferred-auth-implicit-api-key/api/resources/nestedNoAuth/resources/api/index.ts new file mode 100644 index 000000000000..6261f8963643 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/api/resources/nestedNoAuth/resources/api/index.ts @@ -0,0 +1 @@ +export * from "./service"; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/api/resources/nestedNoAuth/resources/api/service/ApiService.ts b/seed/ts-express/inferred-auth-implicit-api-key/api/resources/nestedNoAuth/resources/api/service/ApiService.ts new file mode 100644 index 000000000000..3662ecfd2816 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/api/resources/nestedNoAuth/resources/api/service/ApiService.ts @@ -0,0 +1,69 @@ +// This file was auto-generated by Fern from our API Definition. + +import express from "express"; +import * as errors from "../../../../../../errors/index"; + +export interface ApiServiceMethods { + getSomething( + req: express.Request, + res: { + send: () => Promise; + cookie: (cookie: string, value: string, options?: express.CookieOptions) => void; + locals: any; + }, + next: express.NextFunction, + ): void | Promise; +} + +export class ApiService { + private router; + + constructor( + private readonly methods: ApiServiceMethods, + middleware: express.RequestHandler[] = [], + ) { + this.router = express.Router({ mergeParams: true }).use( + express.json({ + strict: false, + }), + ...middleware, + ); + } + + public addMiddleware(handler: express.RequestHandler): this { + this.router.use(handler); + return this; + } + + public toRouter(): express.Router { + this.router.get("/get-something", async (req, res, next) => { + try { + await this.methods.getSomething( + req as any, + { + send: async () => { + res.sendStatus(204); + }, + cookie: res.cookie.bind(res), + locals: res.locals, + }, + next, + ); + if (!res.writableEnded) { + next(); + } + } catch (error) { + if (error instanceof errors.SeedInferredAuthImplicitApiKeyError) { + console.warn( + `Endpoint 'getSomething' unexpectedly threw ${error.constructor.name}. If this was intentional, please add ${error.constructor.name} to the endpoint's errors list in your Fern Definition.`, + ); + await error.send(res); + } else { + res.status(500).json("Internal Server Error"); + } + next(error); + } + }); + return this.router; + } +} diff --git a/seed/ts-express/inferred-auth-implicit-api-key/api/resources/nestedNoAuth/resources/api/service/index.ts b/seed/ts-express/inferred-auth-implicit-api-key/api/resources/nestedNoAuth/resources/api/service/index.ts new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/api/resources/nestedNoAuth/resources/api/service/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/api/resources/nestedNoAuth/resources/index.ts b/seed/ts-express/inferred-auth-implicit-api-key/api/resources/nestedNoAuth/resources/index.ts new file mode 100644 index 000000000000..d7f360220400 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/api/resources/nestedNoAuth/resources/index.ts @@ -0,0 +1 @@ +export * as api from "./api"; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/api/resources/simple/index.ts b/seed/ts-express/inferred-auth-implicit-api-key/api/resources/simple/index.ts new file mode 100644 index 000000000000..6261f8963643 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/api/resources/simple/index.ts @@ -0,0 +1 @@ +export * from "./service"; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/api/resources/simple/service/SimpleService.ts b/seed/ts-express/inferred-auth-implicit-api-key/api/resources/simple/service/SimpleService.ts new file mode 100644 index 000000000000..676e46d4db7d --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/api/resources/simple/service/SimpleService.ts @@ -0,0 +1,69 @@ +// This file was auto-generated by Fern from our API Definition. + +import express from "express"; +import * as errors from "../../../../errors/index"; + +export interface SimpleServiceMethods { + getSomething( + req: express.Request, + res: { + send: () => Promise; + cookie: (cookie: string, value: string, options?: express.CookieOptions) => void; + locals: any; + }, + next: express.NextFunction, + ): void | Promise; +} + +export class SimpleService { + private router; + + constructor( + private readonly methods: SimpleServiceMethods, + middleware: express.RequestHandler[] = [], + ) { + this.router = express.Router({ mergeParams: true }).use( + express.json({ + strict: false, + }), + ...middleware, + ); + } + + public addMiddleware(handler: express.RequestHandler): this { + this.router.use(handler); + return this; + } + + public toRouter(): express.Router { + this.router.get("/get-something", async (req, res, next) => { + try { + await this.methods.getSomething( + req as any, + { + send: async () => { + res.sendStatus(204); + }, + cookie: res.cookie.bind(res), + locals: res.locals, + }, + next, + ); + if (!res.writableEnded) { + next(); + } + } catch (error) { + if (error instanceof errors.SeedInferredAuthImplicitApiKeyError) { + console.warn( + `Endpoint 'getSomething' unexpectedly threw ${error.constructor.name}. If this was intentional, please add ${error.constructor.name} to the endpoint's errors list in your Fern Definition.`, + ); + await error.send(res); + } else { + res.status(500).json("Internal Server Error"); + } + next(error); + } + }); + return this.router; + } +} diff --git a/seed/ts-express/inferred-auth-implicit-api-key/api/resources/simple/service/index.ts b/seed/ts-express/inferred-auth-implicit-api-key/api/resources/simple/service/index.ts new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/api/resources/simple/service/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/index.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/index.ts new file mode 100644 index 000000000000..3ae53c06d387 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/index.ts @@ -0,0 +1 @@ +export * as serialization from "./schemas"; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/Schema.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/Schema.ts new file mode 100644 index 000000000000..cce732a2ee12 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/Schema.ts @@ -0,0 +1,103 @@ +import type { SchemaUtils } from "./builders/index"; + +export type Schema = BaseSchema & SchemaUtils; + +export type inferRaw = S extends Schema ? Raw : never; +export type inferParsed = S extends Schema ? Parsed : never; + +export interface BaseSchema { + parse: (raw: unknown, opts?: SchemaOptions) => MaybeValid; + json: (parsed: unknown, opts?: SchemaOptions) => MaybeValid; + getType: () => SchemaType | SchemaType; +} + +export const SchemaType = { + BIGINT: "bigint", + DATE: "date", + ENUM: "enum", + LIST: "list", + STRING_LITERAL: "stringLiteral", + BOOLEAN_LITERAL: "booleanLiteral", + OBJECT: "object", + ANY: "any", + BOOLEAN: "boolean", + NUMBER: "number", + STRING: "string", + UNKNOWN: "unknown", + NEVER: "never", + RECORD: "record", + SET: "set", + UNION: "union", + UNDISCRIMINATED_UNION: "undiscriminatedUnion", + NULLABLE: "nullable", + OPTIONAL: "optional", + OPTIONAL_NULLABLE: "optionalNullable", +} as const; + +export type SchemaType = (typeof SchemaType)[keyof typeof SchemaType]; + +export type MaybeValid = Valid | Invalid; + +export interface Valid { + ok: true; + value: T; +} + +export interface Invalid { + ok: false; + errors: ValidationError[]; +} + +export interface ValidationError { + path: string[]; + message: string; +} + +export interface SchemaOptions { + /** + * how to handle unrecognized keys in objects + * + * @default "fail" + */ + unrecognizedObjectKeys?: "fail" | "passthrough" | "strip"; + + /** + * whether to fail when an unrecognized discriminant value is + * encountered in a union + * + * @default false + */ + allowUnrecognizedUnionMembers?: boolean; + + /** + * whether to fail when an unrecognized enum value is encountered + * + * @default false + */ + allowUnrecognizedEnumValues?: boolean; + + /** + * whether to allow data that doesn't conform to the schema. + * invalid data is passed through without transformation. + * + * when this is enabled, .parse() and .json() will always + * return `ok: true`. `.parseOrThrow()` and `.jsonOrThrow()` + * will never fail. + * + * @default false + */ + skipValidation?: boolean; + + /** + * each validation failure contains a "path" property, which is + * the breadcrumbs to the offending node in the JSON. you can supply + * a prefix that is prepended to all the errors' paths. this can be + * helpful for zurg's internal debug logging. + */ + breadcrumbsPrefix?: string[]; + + /** + * whether to send 'null' for optional properties explicitly set to 'undefined'. + */ + omitUndefined?: boolean; +} diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/bigint/bigint.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/bigint/bigint.ts new file mode 100644 index 000000000000..9d8a63d450eb --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/bigint/bigint.ts @@ -0,0 +1,55 @@ +import { type BaseSchema, type Schema, SchemaType } from "../../Schema"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; +import { maybeSkipValidation } from "../../utils/maybeSkipValidation"; +import { getSchemaUtils } from "../schema-utils/index"; + +export function bigint(): Schema { + const baseSchema: BaseSchema = { + parse: (raw, { breadcrumbsPrefix = [] } = {}) => { + if (typeof raw === "bigint") { + return { + ok: true, + value: raw, + }; + } + if (typeof raw === "number") { + return { + ok: true, + value: BigInt(raw), + }; + } + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(raw, "bigint | number"), + }, + ], + }; + }, + json: (bigint, { breadcrumbsPrefix = [] } = {}) => { + if (typeof bigint !== "bigint") { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(bigint, "bigint"), + }, + ], + }; + } + return { + ok: true, + value: bigint, + }; + }, + getType: () => SchemaType.BIGINT, + }; + + return { + ...maybeSkipValidation(baseSchema), + ...getSchemaUtils(baseSchema), + }; +} diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/bigint/index.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/bigint/index.ts new file mode 100644 index 000000000000..e5843043fcbf --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/bigint/index.ts @@ -0,0 +1 @@ +export { bigint } from "./bigint"; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/date/date.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/date/date.ts new file mode 100644 index 000000000000..bc507e08539c --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/date/date.ts @@ -0,0 +1,65 @@ +import { type BaseSchema, type Schema, SchemaType } from "../../Schema"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; +import { maybeSkipValidation } from "../../utils/maybeSkipValidation"; +import { getSchemaUtils } from "../schema-utils/index"; + +// https://stackoverflow.com/questions/12756159/regex-and-iso8601-formatted-datetime +const ISO_8601_REGEX = + /^([+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))([T\s]((([01]\d|2[0-3])((:?)[0-5]\d)?|24:?00)([.,]\d+(?!:))?)?(\17[0-5]\d([.,]\d+)?)?([zZ]|([+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?$/; + +export function date(): Schema { + const baseSchema: BaseSchema = { + parse: (raw, { breadcrumbsPrefix = [] } = {}) => { + if (typeof raw !== "string") { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(raw, "string"), + }, + ], + }; + } + if (!ISO_8601_REGEX.test(raw)) { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(raw, "ISO 8601 date string"), + }, + ], + }; + } + return { + ok: true, + value: new Date(raw), + }; + }, + json: (date, { breadcrumbsPrefix = [] } = {}) => { + if (date instanceof Date) { + return { + ok: true, + value: date.toISOString(), + }; + } else { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(date, "Date object"), + }, + ], + }; + } + }, + getType: () => SchemaType.DATE, + }; + + return { + ...maybeSkipValidation(baseSchema), + ...getSchemaUtils(baseSchema), + }; +} diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/date/index.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/date/index.ts new file mode 100644 index 000000000000..187b29040f6e --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/date/index.ts @@ -0,0 +1 @@ +export { date } from "./date"; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/enum/enum.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/enum/enum.ts new file mode 100644 index 000000000000..c2c49a323e1c --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/enum/enum.ts @@ -0,0 +1,43 @@ +import { type Schema, SchemaType } from "../../Schema"; +import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; + +export function enum_(values: E): Schema { + const validValues = new Set(values); + + const schemaCreator = createIdentitySchemaCreator( + SchemaType.ENUM, + (value, { allowUnrecognizedEnumValues, breadcrumbsPrefix = [] } = {}) => { + if (typeof value !== "string") { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(value, "string"), + }, + ], + }; + } + + if (!validValues.has(value) && !allowUnrecognizedEnumValues) { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(value, "enum"), + }, + ], + }; + } + + return { + ok: true, + value: value as U, + }; + }, + ); + + return schemaCreator(); +} diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/enum/index.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/enum/index.ts new file mode 100644 index 000000000000..fe6faed93e3f --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/enum/index.ts @@ -0,0 +1 @@ +export { enum_ } from "./enum"; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/index.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/index.ts new file mode 100644 index 000000000000..be23de25800d --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/index.ts @@ -0,0 +1,14 @@ +export * from "./bigint/index"; +export * from "./date/index"; +export * from "./enum/index"; +export * from "./lazy/index"; +export * from "./list/index"; +export * from "./literals/index"; +export * from "./object/index"; +export * from "./object-like/index"; +export * from "./primitives/index"; +export * from "./record/index"; +export * from "./schema-utils/index"; +export * from "./set/index"; +export * from "./undiscriminated-union/index"; +export * from "./union/index"; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/lazy/index.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/lazy/index.ts new file mode 100644 index 000000000000..2221abd8806b --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/lazy/index.ts @@ -0,0 +1,3 @@ +export type { SchemaGetter } from "./lazy"; +export { lazy } from "./lazy"; +export { lazyObject } from "./lazyObject"; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/lazy/lazy.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/lazy/lazy.ts new file mode 100644 index 000000000000..a72735184f38 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/lazy/lazy.ts @@ -0,0 +1,32 @@ +import type { BaseSchema, Schema } from "../../Schema"; +import { getSchemaUtils } from "../schema-utils/index"; + +export type SchemaGetter> = () => SchemaType; + +export function lazy(getter: SchemaGetter>): Schema { + const baseSchema = constructLazyBaseSchema(getter); + return { + ...baseSchema, + ...getSchemaUtils(baseSchema), + }; +} + +export function constructLazyBaseSchema( + getter: SchemaGetter>, +): BaseSchema { + return { + parse: (raw, opts) => getMemoizedSchema(getter).parse(raw, opts), + json: (parsed, opts) => getMemoizedSchema(getter).json(parsed, opts), + getType: () => getMemoizedSchema(getter).getType(), + }; +} + +type MemoizedGetter> = SchemaGetter & { __zurg_memoized?: SchemaType }; + +export function getMemoizedSchema>(getter: SchemaGetter): SchemaType { + const castedGetter = getter as MemoizedGetter; + if (castedGetter.__zurg_memoized == null) { + castedGetter.__zurg_memoized = getter(); + } + return castedGetter.__zurg_memoized; +} diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/lazy/lazyObject.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/lazy/lazyObject.ts new file mode 100644 index 000000000000..2a58a2abb203 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/lazy/lazyObject.ts @@ -0,0 +1,20 @@ +import { getObjectUtils } from "../object/index"; +import type { BaseObjectSchema, ObjectSchema } from "../object/types"; +import { getObjectLikeUtils } from "../object-like/index"; +import { getSchemaUtils } from "../schema-utils/index"; +import { constructLazyBaseSchema, getMemoizedSchema, type SchemaGetter } from "./lazy"; + +export function lazyObject(getter: SchemaGetter>): ObjectSchema { + const baseSchema: BaseObjectSchema = { + ...constructLazyBaseSchema(getter), + _getRawProperties: () => getMemoizedSchema(getter)._getRawProperties(), + _getParsedProperties: () => getMemoizedSchema(getter)._getParsedProperties(), + }; + + return { + ...baseSchema, + ...getSchemaUtils(baseSchema), + ...getObjectLikeUtils(baseSchema), + ...getObjectUtils(baseSchema), + }; +} diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/list/index.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/list/index.ts new file mode 100644 index 000000000000..25f4bcc17377 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/list/index.ts @@ -0,0 +1 @@ +export { list } from "./list"; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/list/list.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/list/list.ts new file mode 100644 index 000000000000..7c8fd6e87db9 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/list/list.ts @@ -0,0 +1,73 @@ +import { type BaseSchema, type MaybeValid, type Schema, SchemaType, type ValidationError } from "../../Schema"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; +import { maybeSkipValidation } from "../../utils/maybeSkipValidation"; +import { getSchemaUtils } from "../schema-utils/index"; + +export function list(schema: Schema): Schema { + const baseSchema: BaseSchema = { + parse: (raw, opts) => + validateAndTransformArray(raw, (item, index) => + schema.parse(item, { + ...opts, + breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), `[${index}]`], + }), + ), + json: (parsed, opts) => + validateAndTransformArray(parsed, (item, index) => + schema.json(item, { + ...opts, + breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), `[${index}]`], + }), + ), + getType: () => SchemaType.LIST, + }; + + return { + ...maybeSkipValidation(baseSchema), + ...getSchemaUtils(baseSchema), + }; +} + +function validateAndTransformArray( + value: unknown, + transformItem: (item: Raw, index: number) => MaybeValid, +): MaybeValid { + if (!Array.isArray(value)) { + return { + ok: false, + errors: [ + { + message: getErrorMessageForIncorrectType(value, "list"), + path: [], + }, + ], + }; + } + + const maybeValidItems = value.map((item, index) => transformItem(item, index)); + + return maybeValidItems.reduce>( + (acc, item) => { + if (acc.ok && item.ok) { + return { + ok: true, + value: [...acc.value, item.value], + }; + } + + const errors: ValidationError[] = []; + if (!acc.ok) { + errors.push(...acc.errors); + } + if (!item.ok) { + errors.push(...item.errors); + } + + return { + ok: false, + errors, + }; + }, + { ok: true, value: [] }, + ); +} diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/literals/booleanLiteral.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/literals/booleanLiteral.ts new file mode 100644 index 000000000000..fb06cc00f0de --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/literals/booleanLiteral.ts @@ -0,0 +1,29 @@ +import { type Schema, SchemaType } from "../../Schema"; +import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; + +export function booleanLiteral(literal: V): Schema { + const schemaCreator = createIdentitySchemaCreator( + SchemaType.BOOLEAN_LITERAL, + (value, { breadcrumbsPrefix = [] } = {}) => { + if (value === literal) { + return { + ok: true, + value: literal, + }; + } else { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(value, `${literal.toString()}`), + }, + ], + }; + } + }, + ); + + return schemaCreator(); +} diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/literals/index.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/literals/index.ts new file mode 100644 index 000000000000..ae7389065564 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/literals/index.ts @@ -0,0 +1,2 @@ +export { booleanLiteral } from "./booleanLiteral"; +export { stringLiteral } from "./stringLiteral"; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/literals/stringLiteral.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/literals/stringLiteral.ts new file mode 100644 index 000000000000..98fe06223fe4 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/literals/stringLiteral.ts @@ -0,0 +1,29 @@ +import { type Schema, SchemaType } from "../../Schema"; +import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; + +export function stringLiteral(literal: V): Schema { + const schemaCreator = createIdentitySchemaCreator( + SchemaType.STRING_LITERAL, + (value, { breadcrumbsPrefix = [] } = {}) => { + if (value === literal) { + return { + ok: true, + value: literal, + }; + } else { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(value, `"${literal}"`), + }, + ], + }; + } + }, + ); + + return schemaCreator(); +} diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/object-like/getObjectLikeUtils.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/object-like/getObjectLikeUtils.ts new file mode 100644 index 000000000000..ed96cf771cbb --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/object-like/getObjectLikeUtils.ts @@ -0,0 +1,79 @@ +import type { BaseSchema } from "../../Schema"; +import { filterObject } from "../../utils/filterObject"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; +import { isPlainObject } from "../../utils/isPlainObject"; +import { getSchemaUtils } from "../schema-utils/index"; +import type { ObjectLikeSchema, ObjectLikeUtils } from "./types"; + +export function getObjectLikeUtils(schema: BaseSchema): ObjectLikeUtils { + return { + withParsedProperties: (properties) => withParsedProperties(schema, properties), + }; +} + +/** + * object-like utils are defined in one file to resolve issues with circular imports + */ + +export function withParsedProperties( + objectLike: BaseSchema, + properties: { [K in keyof Properties]: Properties[K] | ((parsed: ParsedObjectShape) => Properties[K]) }, +): ObjectLikeSchema { + const objectSchema: BaseSchema = { + parse: (raw, opts) => { + const parsedObject = objectLike.parse(raw, opts); + if (!parsedObject.ok) { + return parsedObject; + } + + const additionalProperties = Object.entries(properties).reduce>( + (processed, [key, value]) => { + return { + ...processed, + [key]: typeof value === "function" ? value(parsedObject.value) : value, + }; + }, + {}, + ); + + return { + ok: true, + value: { + ...parsedObject.value, + ...(additionalProperties as Properties), + }, + }; + }, + + json: (parsed, opts) => { + if (!isPlainObject(parsed)) { + return { + ok: false, + errors: [ + { + path: opts?.breadcrumbsPrefix ?? [], + message: getErrorMessageForIncorrectType(parsed, "object"), + }, + ], + }; + } + + // strip out added properties + const addedPropertyKeys = new Set(Object.keys(properties)); + const parsedWithoutAddedProperties = filterObject( + parsed, + Object.keys(parsed).filter((key) => !addedPropertyKeys.has(key)), + ); + + return objectLike.json(parsedWithoutAddedProperties as ParsedObjectShape, opts); + }, + + getType: () => objectLike.getType(), + }; + + return { + ...objectSchema, + ...getSchemaUtils(objectSchema), + ...getObjectLikeUtils(objectSchema), + }; +} diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/object-like/index.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/object-like/index.ts new file mode 100644 index 000000000000..c342e72cf9d1 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/object-like/index.ts @@ -0,0 +1,2 @@ +export { getObjectLikeUtils, withParsedProperties } from "./getObjectLikeUtils"; +export type { ObjectLikeSchema, ObjectLikeUtils } from "./types"; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/object-like/types.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/object-like/types.ts new file mode 100644 index 000000000000..a8401d7cf01e --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/object-like/types.ts @@ -0,0 +1,13 @@ +import type { BaseSchema, Schema } from "../../Schema"; + +export type ObjectLikeSchema = Schema & + BaseSchema & + ObjectLikeUtils; + +export interface ObjectLikeUtils { + withParsedProperties: >( + properties: { + [K in keyof T]: T[K] | ((parsed: Parsed) => T[K]); + }, + ) => ObjectLikeSchema; +} diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/object/index.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/object/index.ts new file mode 100644 index 000000000000..58ce0b48b23c --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/object/index.ts @@ -0,0 +1,22 @@ +export { getObjectUtils, object } from "./object"; +export type { + inferObjectWithoutOptionalPropertiesSchemaFromPropertySchemas, + inferParsedObjectWithoutOptionalPropertiesFromPropertySchemas, +} from "./objectWithoutOptionalProperties"; +export { objectWithoutOptionalProperties } from "./objectWithoutOptionalProperties"; +export type { Property } from "./property"; +export { isProperty, property } from "./property"; +export type { + BaseObjectSchema, + inferObjectSchemaFromPropertySchemas, + inferParsedObject, + inferParsedObjectFromPropertySchemas, + inferParsedPropertySchema, + inferRawKey, + inferRawObject, + inferRawObjectFromPropertySchemas, + inferRawPropertySchema, + ObjectSchema, + ObjectUtils, + PropertySchemas, +} from "./types"; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/object/object.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/object/object.ts new file mode 100644 index 000000000000..bdad8076717c --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/object/object.ts @@ -0,0 +1,382 @@ +import { type MaybeValid, type Schema, SchemaType, type ValidationError } from "../../Schema"; +import { entries } from "../../utils/entries"; +import { filterObject } from "../../utils/filterObject"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; +import { isPlainObject } from "../../utils/isPlainObject"; +import { keys } from "../../utils/keys"; +import { maybeSkipValidation } from "../../utils/maybeSkipValidation"; +import { partition } from "../../utils/partition"; +import { getObjectLikeUtils } from "../object-like/index"; +import { getSchemaUtils } from "../schema-utils/index"; +import { isProperty } from "./property"; +import type { + BaseObjectSchema, + inferObjectSchemaFromPropertySchemas, + inferParsedObjectFromPropertySchemas, + inferRawObjectFromPropertySchemas, + ObjectSchema, + ObjectUtils, + PropertySchemas, +} from "./types"; + +interface ObjectPropertyWithRawKey { + rawKey: string; + parsedKey: string; + valueSchema: Schema; +} + +export function object>( + schemas: T, +): inferObjectSchemaFromPropertySchemas { + const baseSchema: BaseObjectSchema< + inferRawObjectFromPropertySchemas, + inferParsedObjectFromPropertySchemas + > = { + _getRawProperties: () => + Object.entries(schemas).map(([parsedKey, propertySchema]) => + isProperty(propertySchema) ? propertySchema.rawKey : parsedKey, + ) as unknown as (keyof inferRawObjectFromPropertySchemas)[], + _getParsedProperties: () => keys(schemas) as unknown as (keyof inferParsedObjectFromPropertySchemas)[], + + parse: (raw, opts) => { + const rawKeyToProperty: Record = {}; + const requiredKeys: string[] = []; + + for (const [parsedKey, schemaOrObjectProperty] of entries(schemas)) { + const rawKey = isProperty(schemaOrObjectProperty) ? schemaOrObjectProperty.rawKey : parsedKey; + const valueSchema: Schema = isProperty(schemaOrObjectProperty) + ? schemaOrObjectProperty.valueSchema + : schemaOrObjectProperty; + + const property: ObjectPropertyWithRawKey = { + rawKey, + parsedKey: parsedKey as string, + valueSchema, + }; + + rawKeyToProperty[rawKey] = property; + + if (isSchemaRequired(valueSchema)) { + requiredKeys.push(rawKey); + } + } + + return validateAndTransformObject({ + value: raw, + requiredKeys, + getProperty: (rawKey) => { + const property = rawKeyToProperty[rawKey]; + if (property == null) { + return undefined; + } + return { + transformedKey: property.parsedKey, + transform: (propertyValue) => + property.valueSchema.parse(propertyValue, { + ...opts, + breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), rawKey], + }), + }; + }, + unrecognizedObjectKeys: opts?.unrecognizedObjectKeys, + skipValidation: opts?.skipValidation, + breadcrumbsPrefix: opts?.breadcrumbsPrefix, + omitUndefined: opts?.omitUndefined, + }); + }, + + json: (parsed, opts) => { + const requiredKeys: string[] = []; + + for (const [parsedKey, schemaOrObjectProperty] of entries(schemas)) { + const valueSchema: Schema = isProperty(schemaOrObjectProperty) + ? schemaOrObjectProperty.valueSchema + : schemaOrObjectProperty; + + if (isSchemaRequired(valueSchema)) { + requiredKeys.push(parsedKey as string); + } + } + + return validateAndTransformObject({ + value: parsed, + requiredKeys, + getProperty: ( + parsedKey, + ): { transformedKey: string; transform: (propertyValue: object) => MaybeValid } | undefined => { + const property = schemas[parsedKey as keyof T]; + + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (property == null) { + return undefined; + } + + if (isProperty(property)) { + return { + transformedKey: property.rawKey, + transform: (propertyValue) => + property.valueSchema.json(propertyValue, { + ...opts, + breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), parsedKey], + }), + }; + } else { + return { + transformedKey: parsedKey, + transform: (propertyValue) => + property.json(propertyValue, { + ...opts, + breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), parsedKey], + }), + }; + } + }, + unrecognizedObjectKeys: opts?.unrecognizedObjectKeys, + skipValidation: opts?.skipValidation, + breadcrumbsPrefix: opts?.breadcrumbsPrefix, + omitUndefined: opts?.omitUndefined, + }); + }, + + getType: () => SchemaType.OBJECT, + }; + + return { + ...maybeSkipValidation(baseSchema), + ...getSchemaUtils(baseSchema), + ...getObjectLikeUtils(baseSchema), + ...getObjectUtils(baseSchema), + }; +} + +function validateAndTransformObject({ + value, + requiredKeys, + getProperty, + unrecognizedObjectKeys = "fail", + skipValidation = false, + breadcrumbsPrefix = [], +}: { + value: unknown; + requiredKeys: string[]; + getProperty: ( + preTransformedKey: string, + ) => { transformedKey: string; transform: (propertyValue: object) => MaybeValid } | undefined; + unrecognizedObjectKeys: "fail" | "passthrough" | "strip" | undefined; + skipValidation: boolean | undefined; + breadcrumbsPrefix: string[] | undefined; + omitUndefined: boolean | undefined; +}): MaybeValid { + if (!isPlainObject(value)) { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(value, "object"), + }, + ], + }; + } + + const missingRequiredKeys = new Set(requiredKeys); + const errors: ValidationError[] = []; + const transformed: Record = {}; + + for (const [preTransformedKey, preTransformedItemValue] of Object.entries(value)) { + const property = getProperty(preTransformedKey); + + if (property != null) { + missingRequiredKeys.delete(preTransformedKey); + + const value = property.transform(preTransformedItemValue as object); + if (value.ok) { + transformed[property.transformedKey] = value.value; + } else { + transformed[preTransformedKey] = preTransformedItemValue; + errors.push(...value.errors); + } + } else { + switch (unrecognizedObjectKeys) { + case "fail": + errors.push({ + path: [...breadcrumbsPrefix, preTransformedKey], + message: `Unexpected key "${preTransformedKey}"`, + }); + break; + case "strip": + break; + case "passthrough": + transformed[preTransformedKey] = preTransformedItemValue; + break; + } + } + } + + errors.push( + ...requiredKeys + .filter((key) => missingRequiredKeys.has(key)) + .map((key) => ({ + path: breadcrumbsPrefix, + message: `Missing required key "${key}"`, + })), + ); + + if (errors.length === 0 || skipValidation) { + return { + ok: true, + value: transformed as Transformed, + }; + } else { + return { + ok: false, + errors, + }; + } +} + +export function getObjectUtils(schema: BaseObjectSchema): ObjectUtils { + return { + extend: (extension: ObjectSchema) => { + const baseSchema: BaseObjectSchema = { + _getParsedProperties: () => [...schema._getParsedProperties(), ...extension._getParsedProperties()], + _getRawProperties: () => [...schema._getRawProperties(), ...extension._getRawProperties()], + parse: (raw, opts) => { + return validateAndTransformExtendedObject({ + extensionKeys: extension._getRawProperties(), + value: raw, + transformBase: (rawBase) => schema.parse(rawBase, opts), + transformExtension: (rawExtension) => extension.parse(rawExtension, opts), + breadcrumbsPrefix: opts?.breadcrumbsPrefix, + }); + }, + json: (parsed, opts) => { + return validateAndTransformExtendedObject({ + extensionKeys: extension._getParsedProperties(), + value: parsed, + transformBase: (parsedBase) => schema.json(parsedBase, opts), + transformExtension: (parsedExtension) => extension.json(parsedExtension, opts), + breadcrumbsPrefix: opts?.breadcrumbsPrefix, + }); + }, + getType: () => SchemaType.OBJECT, + }; + + return { + ...baseSchema, + ...getSchemaUtils(baseSchema), + ...getObjectLikeUtils(baseSchema), + ...getObjectUtils(baseSchema), + }; + }, + passthrough: () => { + const baseSchema: BaseObjectSchema = + { + _getParsedProperties: () => schema._getParsedProperties(), + _getRawProperties: () => schema._getRawProperties(), + parse: (raw, opts) => { + const transformed = schema.parse(raw, { ...opts, unrecognizedObjectKeys: "passthrough" }); + if (!transformed.ok) { + return transformed; + } + return { + ok: true, + value: { + ...(raw as any), + ...transformed.value, + }, + }; + }, + json: (parsed, opts) => { + const transformed = schema.json(parsed, { ...opts, unrecognizedObjectKeys: "passthrough" }); + if (!transformed.ok) { + return transformed; + } + return { + ok: true, + value: { + ...(parsed as any), + ...transformed.value, + }, + }; + }, + getType: () => SchemaType.OBJECT, + }; + + return { + ...baseSchema, + ...getSchemaUtils(baseSchema), + ...getObjectLikeUtils(baseSchema), + ...getObjectUtils(baseSchema), + }; + }, + }; +} + +function validateAndTransformExtendedObject({ + extensionKeys, + value, + transformBase, + transformExtension, + breadcrumbsPrefix = [], +}: { + extensionKeys: (keyof PreTransformedExtension)[]; + value: unknown; + transformBase: (value: object) => MaybeValid; + transformExtension: (value: object) => MaybeValid; + breadcrumbsPrefix?: string[]; +}): MaybeValid { + if (!isPlainObject(value)) { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(value, "object"), + }, + ], + }; + } + + const extensionPropertiesSet = new Set(extensionKeys); + const [extensionProperties, baseProperties] = partition(keys(value), (key) => + extensionPropertiesSet.has(key as keyof PreTransformedExtension), + ); + + const transformedBase = transformBase(filterObject(value, baseProperties)); + const transformedExtension = transformExtension(filterObject(value, extensionProperties)); + + if (transformedBase.ok && transformedExtension.ok) { + return { + ok: true, + value: { + ...transformedBase.value, + ...transformedExtension.value, + }, + }; + } else { + return { + ok: false, + errors: [ + ...(transformedBase.ok ? [] : transformedBase.errors), + ...(transformedExtension.ok ? [] : transformedExtension.errors), + ], + }; + } +} + +function isSchemaRequired(schema: Schema): boolean { + return !isSchemaOptional(schema); +} + +function isSchemaOptional(schema: Schema): boolean { + switch (schema.getType()) { + case SchemaType.ANY: + case SchemaType.UNKNOWN: + case SchemaType.OPTIONAL: + case SchemaType.OPTIONAL_NULLABLE: + return true; + default: + return false; + } +} diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/object/objectWithoutOptionalProperties.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/object/objectWithoutOptionalProperties.ts new file mode 100644 index 000000000000..bac2e1e1af0f --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/object/objectWithoutOptionalProperties.ts @@ -0,0 +1,23 @@ +import { object } from "./object"; +import type { + inferParsedPropertySchema, + inferRawObjectFromPropertySchemas, + ObjectSchema, + PropertySchemas, +} from "./types"; + +export function objectWithoutOptionalProperties>( + schemas: T, +): inferObjectWithoutOptionalPropertiesSchemaFromPropertySchemas { + return object(schemas) as unknown as inferObjectWithoutOptionalPropertiesSchemaFromPropertySchemas; +} + +export type inferObjectWithoutOptionalPropertiesSchemaFromPropertySchemas> = + ObjectSchema< + inferRawObjectFromPropertySchemas, + inferParsedObjectWithoutOptionalPropertiesFromPropertySchemas + >; + +export type inferParsedObjectWithoutOptionalPropertiesFromPropertySchemas> = { + [K in keyof T]: inferParsedPropertySchema; +}; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/object/property.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/object/property.ts new file mode 100644 index 000000000000..ef07c2a7952f --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/object/property.ts @@ -0,0 +1,23 @@ +import type { Schema } from "../../Schema"; + +export function property( + rawKey: RawKey, + valueSchema: Schema, +): Property { + return { + rawKey, + valueSchema, + isProperty: true, + }; +} + +export interface Property { + rawKey: RawKey; + valueSchema: Schema; + isProperty: true; +} + +export function isProperty>(maybeProperty: unknown): maybeProperty is O { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + return (maybeProperty as O).isProperty; +} diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/object/types.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/object/types.ts new file mode 100644 index 000000000000..57f87486910a --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/object/types.ts @@ -0,0 +1,73 @@ +import type { BaseSchema, inferParsed, inferRaw, Schema } from "../../Schema"; +import type { addQuestionMarksToNullableProperties } from "../../utils/addQuestionMarksToNullableProperties"; +import type { ObjectLikeUtils } from "../object-like/index"; +import type { SchemaUtils } from "../schema-utils/index"; +import type { Property } from "./property"; + +export type ObjectSchema = BaseObjectSchema & + ObjectLikeUtils & + ObjectUtils & + SchemaUtils; + +export interface BaseObjectSchema extends BaseSchema { + _getRawProperties: () => (keyof Raw)[]; + _getParsedProperties: () => (keyof Parsed)[]; +} + +export interface ObjectUtils { + extend: ( + schemas: ObjectSchema, + ) => ObjectSchema; + passthrough: () => ObjectSchema; +} + +export type inferRawObject> = O extends ObjectSchema ? Raw : never; + +export type inferParsedObject> = O extends ObjectSchema + ? Parsed + : never; + +export type inferObjectSchemaFromPropertySchemas> = ObjectSchema< + inferRawObjectFromPropertySchemas, + inferParsedObjectFromPropertySchemas +>; + +export type inferRawObjectFromPropertySchemas> = + addQuestionMarksToNullableProperties<{ + [ParsedKey in keyof T as inferRawKey]: inferRawPropertySchema; + }>; + +export type inferParsedObjectFromPropertySchemas> = + addQuestionMarksToNullableProperties<{ + [K in keyof T]: inferParsedPropertySchema; + }>; + +export type PropertySchemas = Record< + ParsedKeys, + Property | Schema +>; + +export type inferRawPropertySchema

| Schema> = P extends Property< + any, + infer Raw, + any +> + ? Raw + : P extends Schema + ? inferRaw

+ : never; + +export type inferParsedPropertySchema

| Schema> = P extends Property< + any, + any, + infer Parsed +> + ? Parsed + : P extends Schema + ? inferParsed

+ : never; + +export type inferRawKey< + ParsedKey extends string | number | symbol, + P extends Property | Schema, +> = P extends Property ? Raw : ParsedKey; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/primitives/any.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/primitives/any.ts new file mode 100644 index 000000000000..0652f8934269 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/primitives/any.ts @@ -0,0 +1,7 @@ +import { type Schema, SchemaType } from "../../Schema"; +import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator"; + +export const any: () => Schema = createIdentitySchemaCreator(SchemaType.ANY, (value) => ({ + ok: true, + value, +})); diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/primitives/boolean.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/primitives/boolean.ts new file mode 100644 index 000000000000..d90a6924f1fe --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/primitives/boolean.ts @@ -0,0 +1,25 @@ +import { type Schema, SchemaType } from "../../Schema"; +import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; + +export const boolean: () => Schema = createIdentitySchemaCreator( + SchemaType.BOOLEAN, + (value, { breadcrumbsPrefix = [] } = {}) => { + if (typeof value === "boolean") { + return { + ok: true, + value, + }; + } else { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(value, "boolean"), + }, + ], + }; + } + }, +); diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/primitives/index.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/primitives/index.ts new file mode 100644 index 000000000000..a60b5e5ad81c --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/primitives/index.ts @@ -0,0 +1,6 @@ +export { any } from "./any"; +export { boolean } from "./boolean"; +export { never } from "./never"; +export { number } from "./number"; +export { string } from "./string"; +export { unknown } from "./unknown"; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/primitives/never.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/primitives/never.ts new file mode 100644 index 000000000000..3de77c3fd7a0 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/primitives/never.ts @@ -0,0 +1,15 @@ +import { type Schema, SchemaType } from "../../Schema"; +import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator"; + +export const never: () => Schema = createIdentitySchemaCreator( + SchemaType.NEVER, + (_value, { breadcrumbsPrefix = [] } = {}) => ({ + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: "Expected never", + }, + ], + }), +); diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/primitives/number.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/primitives/number.ts new file mode 100644 index 000000000000..8b3b32cdca0c --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/primitives/number.ts @@ -0,0 +1,25 @@ +import { type Schema, SchemaType } from "../../Schema"; +import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; + +export const number: () => Schema = createIdentitySchemaCreator( + SchemaType.NUMBER, + (value, { breadcrumbsPrefix = [] } = {}) => { + if (typeof value === "number") { + return { + ok: true, + value, + }; + } else { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(value, "number"), + }, + ], + }; + } + }, +); diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/primitives/string.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/primitives/string.ts new file mode 100644 index 000000000000..f9f8e4779c1b --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/primitives/string.ts @@ -0,0 +1,25 @@ +import { type Schema, SchemaType } from "../../Schema"; +import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; + +export const string: () => Schema = createIdentitySchemaCreator( + SchemaType.STRING, + (value, { breadcrumbsPrefix = [] } = {}) => { + if (typeof value === "string") { + return { + ok: true, + value, + }; + } else { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(value, "string"), + }, + ], + }; + } + }, +); diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/primitives/unknown.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/primitives/unknown.ts new file mode 100644 index 000000000000..41c9d770b10c --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/primitives/unknown.ts @@ -0,0 +1,7 @@ +import { type Schema, SchemaType } from "../../Schema"; +import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator"; + +export const unknown: () => Schema = createIdentitySchemaCreator( + SchemaType.UNKNOWN, + (value) => ({ ok: true, value }), +); diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/record/index.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/record/index.ts new file mode 100644 index 000000000000..82e25c5c2af9 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/record/index.ts @@ -0,0 +1,2 @@ +export { record } from "./record"; +export type { BaseRecordSchema, RecordSchema } from "./types"; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/record/record.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/record/record.ts new file mode 100644 index 000000000000..ba5307a6a45c --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/record/record.ts @@ -0,0 +1,129 @@ +import { type MaybeValid, type Schema, SchemaType, type ValidationError } from "../../Schema"; +import { entries } from "../../utils/entries"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; +import { isPlainObject } from "../../utils/isPlainObject"; +import { maybeSkipValidation } from "../../utils/maybeSkipValidation"; +import { getSchemaUtils } from "../schema-utils/index"; +import type { BaseRecordSchema, RecordSchema } from "./types"; + +export function record( + keySchema: Schema, + valueSchema: Schema, +): RecordSchema { + const baseSchema: BaseRecordSchema = { + parse: (raw, opts) => { + return validateAndTransformRecord({ + value: raw, + isKeyNumeric: keySchema.getType() === SchemaType.NUMBER, + transformKey: (key) => + keySchema.parse(key, { + ...opts, + breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), `${key} (key)`], + }), + transformValue: (value, key) => + valueSchema.parse(value, { + ...opts, + breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), `${key}`], + }), + breadcrumbsPrefix: opts?.breadcrumbsPrefix, + }); + }, + json: (parsed, opts) => { + return validateAndTransformRecord({ + value: parsed, + isKeyNumeric: keySchema.getType() === SchemaType.NUMBER, + transformKey: (key) => + keySchema.json(key, { + ...opts, + breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), `${key} (key)`], + }), + transformValue: (value, key) => + valueSchema.json(value, { + ...opts, + breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), `${key}`], + }), + breadcrumbsPrefix: opts?.breadcrumbsPrefix, + }); + }, + getType: () => SchemaType.RECORD, + }; + + return { + ...maybeSkipValidation(baseSchema), + ...getSchemaUtils(baseSchema), + }; +} + +function validateAndTransformRecord({ + value, + isKeyNumeric, + transformKey, + transformValue, + breadcrumbsPrefix = [], +}: { + value: unknown; + isKeyNumeric: boolean; + transformKey: (key: string | number) => MaybeValid; + transformValue: (value: unknown, key: string | number) => MaybeValid; + breadcrumbsPrefix: string[] | undefined; +}): MaybeValid> { + if (!isPlainObject(value)) { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(value, "object"), + }, + ], + }; + } + + return entries(value).reduce>>( + (accPromise, [stringKey, value]) => { + if (value === undefined) { + return accPromise; + } + + const acc = accPromise; + + let key: string | number = stringKey; + if (isKeyNumeric) { + const numberKey = stringKey.length > 0 ? Number(stringKey) : NaN; + if (!Number.isNaN(numberKey)) { + key = numberKey; + } + } + const transformedKey = transformKey(key); + + const transformedValue = transformValue(value, key); + + if (acc.ok && transformedKey.ok && transformedValue.ok) { + return { + ok: true, + value: { + ...acc.value, + [transformedKey.value]: transformedValue.value, + }, + }; + } + + const errors: ValidationError[] = []; + if (!acc.ok) { + errors.push(...acc.errors); + } + if (!transformedKey.ok) { + errors.push(...transformedKey.errors); + } + if (!transformedValue.ok) { + errors.push(...transformedValue.errors); + } + + return { + ok: false, + errors, + }; + }, + { ok: true, value: {} as Record }, + ); +} diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/record/types.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/record/types.ts new file mode 100644 index 000000000000..b99bb9d8786b --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/record/types.ts @@ -0,0 +1,17 @@ +import type { BaseSchema } from "../../Schema"; +import type { SchemaUtils } from "../schema-utils/index"; + +export type RecordSchema< + RawKey extends string | number, + RawValue, + ParsedKey extends string | number, + ParsedValue, +> = BaseRecordSchema & + SchemaUtils, Record>; + +export type BaseRecordSchema< + RawKey extends string | number, + RawValue, + ParsedKey extends string | number, + ParsedValue, +> = BaseSchema, Record>; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/schema-utils/JsonError.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/schema-utils/JsonError.ts new file mode 100644 index 000000000000..7573c76be2aa --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/schema-utils/JsonError.ts @@ -0,0 +1,9 @@ +import type { ValidationError } from "../../Schema"; +import { stringifyValidationError } from "./stringifyValidationErrors"; + +export class JsonError extends Error { + constructor(public readonly errors: ValidationError[]) { + super(errors.map(stringifyValidationError).join("; ")); + Object.setPrototypeOf(this, JsonError.prototype); + } +} diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/schema-utils/ParseError.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/schema-utils/ParseError.ts new file mode 100644 index 000000000000..f1914b5965e6 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/schema-utils/ParseError.ts @@ -0,0 +1,9 @@ +import type { ValidationError } from "../../Schema"; +import { stringifyValidationError } from "./stringifyValidationErrors"; + +export class ParseError extends Error { + constructor(public readonly errors: ValidationError[]) { + super(errors.map(stringifyValidationError).join("; ")); + Object.setPrototypeOf(this, ParseError.prototype); + } +} diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/schema-utils/getSchemaUtils.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/schema-utils/getSchemaUtils.ts new file mode 100644 index 000000000000..91381a1d6626 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/schema-utils/getSchemaUtils.ts @@ -0,0 +1,181 @@ +import { type BaseSchema, type Schema, type SchemaOptions, SchemaType } from "../../Schema"; +import { JsonError } from "./JsonError"; +import { ParseError } from "./ParseError"; + +export interface SchemaUtils { + nullable: () => Schema; + optional: () => Schema; + optionalNullable: () => Schema; + transform: (transformer: SchemaTransformer) => Schema; + parseOrThrow: (raw: unknown, opts?: SchemaOptions) => Parsed; + jsonOrThrow: (raw: unknown, opts?: SchemaOptions) => Raw; +} + +export interface SchemaTransformer { + transform: (parsed: Parsed) => Transformed; + untransform: (transformed: any) => Parsed; +} + +export function getSchemaUtils(schema: BaseSchema): SchemaUtils { + return { + nullable: () => nullable(schema), + optional: () => optional(schema), + optionalNullable: () => optionalNullable(schema), + transform: (transformer) => transform(schema, transformer), + parseOrThrow: (raw, opts) => { + const parsed = schema.parse(raw, opts); + if (parsed.ok) { + return parsed.value; + } + throw new ParseError(parsed.errors); + }, + jsonOrThrow: (parsed, opts) => { + const raw = schema.json(parsed, opts); + if (raw.ok) { + return raw.value; + } + throw new JsonError(raw.errors); + }, + }; +} + +/** + * schema utils are defined in one file to resolve issues with circular imports + */ + +export function nullable(schema: BaseSchema): Schema { + const baseSchema: BaseSchema = { + parse: (raw, opts) => { + if (raw == null) { + return { + ok: true, + value: null, + }; + } + return schema.parse(raw, opts); + }, + json: (parsed, opts) => { + if (parsed == null) { + return { + ok: true, + value: null, + }; + } + return schema.json(parsed, opts); + }, + getType: () => SchemaType.NULLABLE, + }; + + return { + ...baseSchema, + ...getSchemaUtils(baseSchema), + }; +} + +export function optional( + schema: BaseSchema, +): Schema { + const baseSchema: BaseSchema = { + parse: (raw, opts) => { + if (raw == null) { + return { + ok: true, + value: undefined, + }; + } + return schema.parse(raw, opts); + }, + json: (parsed, opts) => { + if (opts?.omitUndefined && parsed === undefined) { + return { + ok: true, + value: undefined, + }; + } + if (parsed == null) { + return { + ok: true, + value: null, + }; + } + return schema.json(parsed, opts); + }, + getType: () => SchemaType.OPTIONAL, + }; + + return { + ...baseSchema, + ...getSchemaUtils(baseSchema), + }; +} + +export function optionalNullable( + schema: BaseSchema, +): Schema { + const baseSchema: BaseSchema = { + parse: (raw, opts) => { + if (raw === undefined) { + return { + ok: true, + value: undefined, + }; + } + if (raw === null) { + return { + ok: true, + value: null, + }; + } + return schema.parse(raw, opts); + }, + json: (parsed, opts) => { + if (parsed === undefined) { + return { + ok: true, + value: undefined, + }; + } + if (parsed === null) { + return { + ok: true, + value: null, + }; + } + return schema.json(parsed, opts); + }, + getType: () => SchemaType.OPTIONAL_NULLABLE, + }; + + return { + ...baseSchema, + ...getSchemaUtils(baseSchema), + }; +} + +export function transform( + schema: BaseSchema, + transformer: SchemaTransformer, +): Schema { + const baseSchema: BaseSchema = { + parse: (raw, opts) => { + const parsed = schema.parse(raw, opts); + if (!parsed.ok) { + return parsed; + } + return { + ok: true, + value: transformer.transform(parsed.value), + }; + }, + json: (transformed, opts) => { + const parsed = transformer.untransform(transformed); + return schema.json(parsed, opts); + }, + getType: () => schema.getType(), + }; + + return { + ...baseSchema, + ...getSchemaUtils(baseSchema), + }; +} diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/schema-utils/index.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/schema-utils/index.ts new file mode 100644 index 000000000000..9d0eed7ae843 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/schema-utils/index.ts @@ -0,0 +1,4 @@ +export type { SchemaUtils } from "./getSchemaUtils"; +export { getSchemaUtils, optional, transform } from "./getSchemaUtils"; +export { JsonError } from "./JsonError"; +export { ParseError } from "./ParseError"; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/schema-utils/stringifyValidationErrors.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/schema-utils/stringifyValidationErrors.ts new file mode 100644 index 000000000000..1066d00384aa --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/schema-utils/stringifyValidationErrors.ts @@ -0,0 +1,8 @@ +import type { ValidationError } from "../../Schema"; + +export function stringifyValidationError(error: ValidationError): string { + if (error.path.length === 0) { + return error.message; + } + return `${error.path.join(" -> ")}: ${error.message}`; +} diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/set/index.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/set/index.ts new file mode 100644 index 000000000000..f3310e8bdade --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/set/index.ts @@ -0,0 +1 @@ +export { set } from "./set"; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/set/set.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/set/set.ts new file mode 100644 index 000000000000..bf31bc5fdb43 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/set/set.ts @@ -0,0 +1,43 @@ +import { type BaseSchema, type Schema, SchemaType } from "../../Schema"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; +import { maybeSkipValidation } from "../../utils/maybeSkipValidation"; +import { list } from "../list/index"; +import { getSchemaUtils } from "../schema-utils/index"; + +export function set(schema: Schema): Schema> { + const listSchema = list(schema); + const baseSchema: BaseSchema> = { + parse: (raw, opts) => { + const parsedList = listSchema.parse(raw, opts); + if (parsedList.ok) { + return { + ok: true, + value: new Set(parsedList.value), + }; + } else { + return parsedList; + } + }, + json: (parsed, opts) => { + if (!(parsed instanceof Set)) { + return { + ok: false, + errors: [ + { + path: opts?.breadcrumbsPrefix ?? [], + message: getErrorMessageForIncorrectType(parsed, "Set"), + }, + ], + }; + } + const jsonList = listSchema.json([...parsed], opts); + return jsonList; + }, + getType: () => SchemaType.SET, + }; + + return { + ...maybeSkipValidation(baseSchema), + ...getSchemaUtils(baseSchema), + }; +} diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/undiscriminated-union/index.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/undiscriminated-union/index.ts new file mode 100644 index 000000000000..75b71cb35656 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/undiscriminated-union/index.ts @@ -0,0 +1,6 @@ +export type { + inferParsedUnidiscriminatedUnionSchema, + inferRawUnidiscriminatedUnionSchema, + UndiscriminatedUnionSchema, +} from "./types"; +export { undiscriminatedUnion } from "./undiscriminatedUnion"; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/undiscriminated-union/types.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/undiscriminated-union/types.ts new file mode 100644 index 000000000000..3ef19e2c09e1 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/undiscriminated-union/types.ts @@ -0,0 +1,10 @@ +import type { inferParsed, inferRaw, Schema } from "../../Schema"; + +export type UndiscriminatedUnionSchema = Schema< + inferRawUnidiscriminatedUnionSchema, + inferParsedUnidiscriminatedUnionSchema +>; + +export type inferRawUnidiscriminatedUnionSchema = inferRaw; + +export type inferParsedUnidiscriminatedUnionSchema = inferParsed; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/undiscriminated-union/undiscriminatedUnion.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/undiscriminated-union/undiscriminatedUnion.ts new file mode 100644 index 000000000000..26ad5f9b9112 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/undiscriminated-union/undiscriminatedUnion.ts @@ -0,0 +1,67 @@ +import { + type BaseSchema, + type MaybeValid, + type Schema, + type SchemaOptions, + SchemaType, + type ValidationError, +} from "../../Schema"; +import { maybeSkipValidation } from "../../utils/maybeSkipValidation"; +import { getSchemaUtils } from "../schema-utils/index"; +import type { inferParsedUnidiscriminatedUnionSchema, inferRawUnidiscriminatedUnionSchema } from "./types"; + +export function undiscriminatedUnion, ...Schema[]]>( + schemas: Schemas, +): Schema, inferParsedUnidiscriminatedUnionSchema> { + const baseSchema: BaseSchema< + inferRawUnidiscriminatedUnionSchema, + inferParsedUnidiscriminatedUnionSchema + > = { + parse: (raw, opts) => { + return validateAndTransformUndiscriminatedUnion>( + (schema, opts) => schema.parse(raw, opts), + schemas, + opts, + ); + }, + json: (parsed, opts) => { + return validateAndTransformUndiscriminatedUnion>( + (schema, opts) => schema.json(parsed, opts), + schemas, + opts, + ); + }, + getType: () => SchemaType.UNDISCRIMINATED_UNION, + }; + + return { + ...maybeSkipValidation(baseSchema), + ...getSchemaUtils(baseSchema), + }; +} + +function validateAndTransformUndiscriminatedUnion( + transform: (schema: Schema, opts: SchemaOptions) => MaybeValid, + schemas: Schema[], + opts: SchemaOptions | undefined, +): MaybeValid { + const errors: ValidationError[] = []; + for (const [index, schema] of schemas.entries()) { + const transformed = transform(schema, { ...opts, skipValidation: false }); + if (transformed.ok) { + return transformed; + } else { + for (const error of transformed.errors) { + errors.push({ + path: error.path, + message: `[Variant ${index}] ${error.message}`, + }); + } + } + } + + return { + ok: false, + errors, + }; +} diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/union/discriminant.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/union/discriminant.ts new file mode 100644 index 000000000000..73cd62adeba5 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/union/discriminant.ts @@ -0,0 +1,14 @@ +export function discriminant( + parsedDiscriminant: ParsedDiscriminant, + rawDiscriminant: RawDiscriminant, +): Discriminant { + return { + parsedDiscriminant, + rawDiscriminant, + }; +} + +export interface Discriminant { + parsedDiscriminant: ParsedDiscriminant; + rawDiscriminant: RawDiscriminant; +} diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/union/index.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/union/index.ts new file mode 100644 index 000000000000..22289b66715c --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/union/index.ts @@ -0,0 +1,10 @@ +export type { Discriminant } from "./discriminant"; +export { discriminant } from "./discriminant"; +export type { + inferParsedDiscriminant, + inferParsedUnion, + inferRawDiscriminant, + inferRawUnion, + UnionSubtypes, +} from "./types"; +export { union } from "./union"; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/union/types.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/union/types.ts new file mode 100644 index 000000000000..79753436d6cf --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/union/types.ts @@ -0,0 +1,26 @@ +import type { inferParsedObject, inferRawObject, ObjectSchema } from "../object/index"; +import type { Discriminant } from "./discriminant"; + +export type UnionSubtypes = { + [K in DiscriminantValues]: ObjectSchema; +}; + +export type inferRawUnion, U extends UnionSubtypes> = { + [K in keyof U]: Record, K> & inferRawObject; +}[keyof U]; + +export type inferParsedUnion, U extends UnionSubtypes> = { + [K in keyof U]: Record, K> & inferParsedObject; +}[keyof U]; + +export type inferRawDiscriminant> = D extends string + ? D + : D extends Discriminant + ? Raw + : never; + +export type inferParsedDiscriminant> = D extends string + ? D + : D extends Discriminant + ? Parsed + : never; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/union/union.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/union/union.ts new file mode 100644 index 000000000000..7da4271a27c0 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/builders/union/union.ts @@ -0,0 +1,176 @@ +import { type BaseSchema, type MaybeValid, SchemaType } from "../../Schema"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; +import { isPlainObject } from "../../utils/isPlainObject"; +import { keys } from "../../utils/keys"; +import { maybeSkipValidation } from "../../utils/maybeSkipValidation"; +import { enum_ } from "../enum/index"; +import type { ObjectSchema } from "../object/index"; +import { getObjectLikeUtils, type ObjectLikeSchema } from "../object-like"; +import { getSchemaUtils } from "../schema-utils/index"; +import type { Discriminant } from "./discriminant"; +import type { + inferParsedDiscriminant, + inferParsedUnion, + inferRawDiscriminant, + inferRawUnion, + UnionSubtypes, +} from "./types"; + +export function union, U extends UnionSubtypes>( + discriminant: D, + union: U, +): ObjectLikeSchema, inferParsedUnion> { + const rawDiscriminant = + typeof discriminant === "string" ? discriminant : (discriminant.rawDiscriminant as inferRawDiscriminant); + const parsedDiscriminant = + typeof discriminant === "string" + ? discriminant + : (discriminant.parsedDiscriminant as inferParsedDiscriminant); + + const discriminantValueSchema = enum_(keys(union) as string[]); + + const baseSchema: BaseSchema, inferParsedUnion> = { + parse: (raw, opts) => { + return transformAndValidateUnion({ + value: raw, + discriminant: rawDiscriminant, + transformedDiscriminant: parsedDiscriminant, + transformDiscriminantValue: (discriminantValue) => + discriminantValueSchema.parse(discriminantValue, { + allowUnrecognizedEnumValues: opts?.allowUnrecognizedUnionMembers, + breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), rawDiscriminant], + }), + getAdditionalPropertiesSchema: (discriminantValue) => union[discriminantValue], + allowUnrecognizedUnionMembers: opts?.allowUnrecognizedUnionMembers, + transformAdditionalProperties: (additionalProperties, additionalPropertiesSchema) => + additionalPropertiesSchema.parse(additionalProperties, opts), + breadcrumbsPrefix: opts?.breadcrumbsPrefix, + }); + }, + json: (parsed, opts) => { + return transformAndValidateUnion({ + value: parsed, + discriminant: parsedDiscriminant, + transformedDiscriminant: rawDiscriminant, + transformDiscriminantValue: (discriminantValue) => + discriminantValueSchema.json(discriminantValue, { + allowUnrecognizedEnumValues: opts?.allowUnrecognizedUnionMembers, + breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), parsedDiscriminant], + }), + getAdditionalPropertiesSchema: (discriminantValue) => union[discriminantValue], + allowUnrecognizedUnionMembers: opts?.allowUnrecognizedUnionMembers, + transformAdditionalProperties: (additionalProperties, additionalPropertiesSchema) => + additionalPropertiesSchema.json(additionalProperties, opts), + breadcrumbsPrefix: opts?.breadcrumbsPrefix, + }); + }, + getType: () => SchemaType.UNION, + }; + + return { + ...maybeSkipValidation(baseSchema), + ...getSchemaUtils(baseSchema), + ...getObjectLikeUtils(baseSchema), + }; +} + +function transformAndValidateUnion< + TransformedDiscriminant extends string, + TransformedDiscriminantValue extends string, + TransformedAdditionalProperties, +>({ + value, + discriminant, + transformedDiscriminant, + transformDiscriminantValue, + getAdditionalPropertiesSchema, + allowUnrecognizedUnionMembers = false, + transformAdditionalProperties, + breadcrumbsPrefix = [], +}: { + value: unknown; + discriminant: string; + transformedDiscriminant: TransformedDiscriminant; + transformDiscriminantValue: (discriminantValue: unknown) => MaybeValid; + getAdditionalPropertiesSchema: (discriminantValue: string) => ObjectSchema | undefined; + allowUnrecognizedUnionMembers: boolean | undefined; + transformAdditionalProperties: ( + additionalProperties: unknown, + additionalPropertiesSchema: ObjectSchema, + ) => MaybeValid; + breadcrumbsPrefix: string[] | undefined; +}): MaybeValid & TransformedAdditionalProperties> { + if (!isPlainObject(value)) { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(value, "object"), + }, + ], + }; + } + + const { [discriminant]: discriminantValue, ...additionalProperties } = value; + + if (discriminantValue == null) { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: `Missing discriminant ("${discriminant}")`, + }, + ], + }; + } + + const transformedDiscriminantValue = transformDiscriminantValue(discriminantValue); + if (!transformedDiscriminantValue.ok) { + return { + ok: false, + errors: transformedDiscriminantValue.errors, + }; + } + + const additionalPropertiesSchema = getAdditionalPropertiesSchema(transformedDiscriminantValue.value); + + if (additionalPropertiesSchema == null) { + if (allowUnrecognizedUnionMembers) { + return { + ok: true, + value: { + [transformedDiscriminant]: transformedDiscriminantValue.value, + ...additionalProperties, + } as Record & TransformedAdditionalProperties, + }; + } else { + return { + ok: false, + errors: [ + { + path: [...breadcrumbsPrefix, discriminant], + message: "Unexpected discriminant value", + }, + ], + }; + } + } + + const transformedAdditionalProperties = transformAdditionalProperties( + additionalProperties, + additionalPropertiesSchema, + ); + if (!transformedAdditionalProperties.ok) { + return transformedAdditionalProperties; + } + + return { + ok: true, + value: { + [transformedDiscriminant]: discriminantValue, + ...transformedAdditionalProperties.value, + } as Record & TransformedAdditionalProperties, + }; +} diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/index.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/index.ts new file mode 100644 index 000000000000..60c0b5988d32 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/index.ts @@ -0,0 +1,2 @@ +export * from "./builders/index"; +export type { inferParsed, inferRaw, Schema, SchemaOptions } from "./Schema"; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/utils/MaybePromise.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/utils/MaybePromise.ts new file mode 100644 index 000000000000..9cd354b3418e --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/utils/MaybePromise.ts @@ -0,0 +1 @@ +export type MaybePromise = T | Promise; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/utils/addQuestionMarksToNullableProperties.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/utils/addQuestionMarksToNullableProperties.ts new file mode 100644 index 000000000000..59f9e658867b --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/utils/addQuestionMarksToNullableProperties.ts @@ -0,0 +1,9 @@ +export type addQuestionMarksToNullableProperties = { + [K in OptionalKeys]?: T[K]; +} & Pick>; + +export type OptionalKeys = { + [K in keyof T]-?: undefined extends T[K] ? K : never; +}[keyof T]; + +export type RequiredKeys = Exclude>; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/utils/createIdentitySchemaCreator.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/utils/createIdentitySchemaCreator.ts new file mode 100644 index 000000000000..d299ada74c11 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/utils/createIdentitySchemaCreator.ts @@ -0,0 +1,21 @@ +import { getSchemaUtils } from "../builders/schema-utils/index"; +import type { BaseSchema, MaybeValid, Schema, SchemaOptions, SchemaType } from "../Schema"; +import { maybeSkipValidation } from "./maybeSkipValidation"; + +export function createIdentitySchemaCreator( + schemaType: SchemaType, + validate: (value: unknown, opts?: SchemaOptions) => MaybeValid, +): () => Schema { + return () => { + const baseSchema: BaseSchema = { + parse: validate, + json: validate, + getType: () => schemaType, + }; + + return { + ...maybeSkipValidation(baseSchema), + ...getSchemaUtils(baseSchema), + }; + }; +} diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/utils/entries.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/utils/entries.ts new file mode 100644 index 000000000000..2d5c93d657ce --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/utils/entries.ts @@ -0,0 +1,3 @@ +export function entries(object: T): [keyof T, T[keyof T]][] { + return Object.entries(object) as [keyof T, T[keyof T]][]; +} diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/utils/filterObject.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/utils/filterObject.ts new file mode 100644 index 000000000000..70527d10013b --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/utils/filterObject.ts @@ -0,0 +1,13 @@ +export function filterObject(obj: T, keysToInclude: K[]): Pick { + const keysToIncludeSet = new Set(keysToInclude); + return Object.entries(obj).reduce( + (acc, [key, value]) => { + if (keysToIncludeSet.has(key as K)) { + acc[key as K] = value as T[K]; + } + return acc; + // eslint-disable-next-line @typescript-eslint/prefer-reduce-type-parameter + }, + {} as Pick, + ); +} diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/utils/getErrorMessageForIncorrectType.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/utils/getErrorMessageForIncorrectType.ts new file mode 100644 index 000000000000..1a5c31027ce9 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/utils/getErrorMessageForIncorrectType.ts @@ -0,0 +1,25 @@ +export function getErrorMessageForIncorrectType(value: unknown, expectedType: string): string { + return `Expected ${expectedType}. Received ${getTypeAsString(value)}.`; +} + +function getTypeAsString(value: unknown): string { + if (Array.isArray(value)) { + return "list"; + } + if (value === null) { + return "null"; + } + if (value instanceof BigInt) { + return "BigInt"; + } + switch (typeof value) { + case "string": + return `"${value}"`; + case "bigint": + case "number": + case "boolean": + case "undefined": + return `${value}`; + } + return typeof value; +} diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/utils/isPlainObject.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/utils/isPlainObject.ts new file mode 100644 index 000000000000..db82a722c35b --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/utils/isPlainObject.ts @@ -0,0 +1,17 @@ +// borrowed from https://github.com/lodash/lodash/blob/master/isPlainObject.js +export function isPlainObject(value: unknown): value is Record { + if (typeof value !== "object" || value === null) { + return false; + } + + if (Object.getPrototypeOf(value) === null) { + return true; + } + + let proto = value; + while (Object.getPrototypeOf(proto) !== null) { + proto = Object.getPrototypeOf(proto); + } + + return Object.getPrototypeOf(value) === proto; +} diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/utils/keys.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/utils/keys.ts new file mode 100644 index 000000000000..2e0930e2d70b --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/utils/keys.ts @@ -0,0 +1,3 @@ +export function keys(object: T): (keyof T)[] { + return Object.keys(object) as (keyof T)[]; +} diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/utils/maybeSkipValidation.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/utils/maybeSkipValidation.ts new file mode 100644 index 000000000000..70be6478ba43 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/utils/maybeSkipValidation.ts @@ -0,0 +1,38 @@ +import type { BaseSchema, MaybeValid, SchemaOptions } from "../Schema"; + +export function maybeSkipValidation, Raw, Parsed>(schema: S): S { + return { + ...schema, + json: transformAndMaybeSkipValidation(schema.json), + parse: transformAndMaybeSkipValidation(schema.parse), + }; +} + +function transformAndMaybeSkipValidation( + transform: (value: unknown, opts?: SchemaOptions) => MaybeValid, +): (value: unknown, opts?: SchemaOptions) => MaybeValid { + return (value, opts): MaybeValid => { + const transformed = transform(value, opts); + const { skipValidation = false } = opts ?? {}; + if (!transformed.ok && skipValidation) { + // biome-ignore lint/suspicious/noConsole: allow console + console.warn( + [ + "Failed to validate.", + ...transformed.errors.map( + (error) => + " - " + + (error.path.length > 0 ? `${error.path.join(".")}: ${error.message}` : error.message), + ), + ].join("\n"), + ); + + return { + ok: true, + value: value as T, + }; + } else { + return transformed; + } + }; +} diff --git a/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/utils/partition.ts b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/utils/partition.ts new file mode 100644 index 000000000000..f58d6f3d35f3 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/core/schemas/utils/partition.ts @@ -0,0 +1,12 @@ +export function partition(items: readonly T[], predicate: (item: T) => boolean): [T[], T[]] { + const trueItems: T[] = [], + falseItems: T[] = []; + for (const item of items) { + if (predicate(item)) { + trueItems.push(item); + } else { + falseItems.push(item); + } + } + return [trueItems, falseItems]; +} diff --git a/seed/ts-express/inferred-auth-implicit-api-key/errors/SeedInferredAuthImplicitApiKeyError.ts b/seed/ts-express/inferred-auth-implicit-api-key/errors/SeedInferredAuthImplicitApiKeyError.ts new file mode 100644 index 000000000000..d131ab7a37fe --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/errors/SeedInferredAuthImplicitApiKeyError.ts @@ -0,0 +1,17 @@ +// This file was auto-generated by Fern from our API Definition. + +import type express from "express"; + +export abstract class SeedInferredAuthImplicitApiKeyError extends Error { + constructor(public readonly errorName?: string) { + super(); + Object.setPrototypeOf(this, new.target.prototype); + if (Error.captureStackTrace) { + Error.captureStackTrace(this, this.constructor); + } + + this.name = this.constructor.name; + } + + public abstract send(res: express.Response): Promise; +} diff --git a/seed/ts-express/inferred-auth-implicit-api-key/errors/index.ts b/seed/ts-express/inferred-auth-implicit-api-key/errors/index.ts new file mode 100644 index 000000000000..5c13b966049a --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/errors/index.ts @@ -0,0 +1 @@ +export { SeedInferredAuthImplicitApiKeyError } from "./SeedInferredAuthImplicitApiKeyError"; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/index.ts b/seed/ts-express/inferred-auth-implicit-api-key/index.ts new file mode 100644 index 000000000000..3d200d31497f --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/index.ts @@ -0,0 +1,3 @@ +export * as SeedInferredAuthImplicitApiKey from "./api"; +export { SeedInferredAuthImplicitApiKeyError } from "./errors"; +export { register } from "./register"; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/register.ts b/seed/ts-express/inferred-auth-implicit-api-key/register.ts new file mode 100644 index 000000000000..13017b93a0ca --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/register.ts @@ -0,0 +1,26 @@ +// This file was auto-generated by Fern from our API Definition. + +import type express from "express"; +import type { AuthService } from "./api/resources/auth/service/AuthService"; +import type { ApiService as nested_ApiService } from "./api/resources/nested/resources/api/service/ApiService"; +import type { ApiService as nestedNoAuth_ApiService } from "./api/resources/nestedNoAuth/resources/api/service/ApiService"; +import type { SimpleService } from "./api/resources/simple/service/SimpleService"; + +export function register( + expressApp: express.Express | express.Router, + services: { + auth: AuthService; + simple: SimpleService; + nestedNoAuth: { + api: nestedNoAuth_ApiService; + }; + nested: { + api: nested_ApiService; + }; + }, +): void { + (expressApp as any).use("/", services.auth.toRouter()); + (expressApp as any).use("/nested-no-auth", services.nestedNoAuth.api.toRouter()); + (expressApp as any).use("/nested", services.nested.api.toRouter()); + (expressApp as any).use("/", services.simple.toRouter()); +} diff --git a/seed/ts-express/inferred-auth-implicit-api-key/serialization/index.ts b/seed/ts-express/inferred-auth-implicit-api-key/serialization/index.ts new file mode 100644 index 000000000000..3e5335fe4215 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/serialization/index.ts @@ -0,0 +1 @@ +export * from "./resources"; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/serialization/resources/auth/index.ts b/seed/ts-express/inferred-auth-implicit-api-key/serialization/resources/auth/index.ts new file mode 100644 index 000000000000..eea524d65570 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/serialization/resources/auth/index.ts @@ -0,0 +1 @@ +export * from "./types"; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/serialization/resources/auth/types/TokenResponse.ts b/seed/ts-express/inferred-auth-implicit-api-key/serialization/resources/auth/types/TokenResponse.ts new file mode 100644 index 000000000000..422ef833da87 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/serialization/resources/auth/types/TokenResponse.ts @@ -0,0 +1,24 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as SeedInferredAuthImplicitApiKey from "../../../../api/index"; +import * as core from "../../../../core"; +import type * as serializers from "../../../index"; + +export const TokenResponse: core.serialization.ObjectSchema< + serializers.TokenResponse.Raw, + SeedInferredAuthImplicitApiKey.TokenResponse +> = core.serialization.object({ + accessToken: core.serialization.property("access_token", core.serialization.string()), + tokenType: core.serialization.property("token_type", core.serialization.string()), + expiresIn: core.serialization.property("expires_in", core.serialization.number()), + scope: core.serialization.string().optional(), +}); + +export declare namespace TokenResponse { + export interface Raw { + access_token: string; + token_type: string; + expires_in: number; + scope?: string | null; + } +} diff --git a/seed/ts-express/inferred-auth-implicit-api-key/serialization/resources/auth/types/index.ts b/seed/ts-express/inferred-auth-implicit-api-key/serialization/resources/auth/types/index.ts new file mode 100644 index 000000000000..6848a730c911 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/serialization/resources/auth/types/index.ts @@ -0,0 +1 @@ +export * from "./TokenResponse"; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/serialization/resources/index.ts b/seed/ts-express/inferred-auth-implicit-api-key/serialization/resources/index.ts new file mode 100644 index 000000000000..a023a1ef6272 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-api-key/serialization/resources/index.ts @@ -0,0 +1,2 @@ +export * as auth from "./auth"; +export * from "./auth/types"; diff --git a/seed/ts-express/inferred-auth-implicit-api-key/snippet.json b/seed/ts-express/inferred-auth-implicit-api-key/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/seed/ts-express/inferred-auth-implicit-reference/api/index.ts b/seed/ts-express/inferred-auth-implicit-reference/api/index.ts new file mode 100644 index 000000000000..3e5335fe4215 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/api/index.ts @@ -0,0 +1 @@ +export * from "./resources"; diff --git a/seed/ts-express/inferred-auth-implicit-reference/api/resources/auth/index.ts b/seed/ts-express/inferred-auth-implicit-reference/api/resources/auth/index.ts new file mode 100644 index 000000000000..4593a61f8b42 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/api/resources/auth/index.ts @@ -0,0 +1,2 @@ +export * from "./service"; +export * from "./types"; diff --git a/seed/ts-express/inferred-auth-implicit-reference/api/resources/auth/service/AuthService.ts b/seed/ts-express/inferred-auth-implicit-reference/api/resources/auth/service/AuthService.ts new file mode 100644 index 000000000000..0ef5e6819cd5 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/api/resources/auth/service/AuthService.ts @@ -0,0 +1,148 @@ +// This file was auto-generated by Fern from our API Definition. + +import express from "express"; +import * as errors from "../../../../errors/index"; +import * as serializers from "../../../../serialization/index"; +import type * as SeedInferredAuthImplicit from "../../../index"; + +export interface AuthServiceMethods { + getTokenWithClientCredentials( + req: express.Request< + never, + SeedInferredAuthImplicit.TokenResponse, + SeedInferredAuthImplicit.GetTokenRequest, + never + >, + res: { + send: (responseBody: SeedInferredAuthImplicit.TokenResponse) => Promise; + cookie: (cookie: string, value: string, options?: express.CookieOptions) => void; + locals: any; + }, + next: express.NextFunction, + ): void | Promise; + refreshToken( + req: express.Request< + never, + SeedInferredAuthImplicit.TokenResponse, + SeedInferredAuthImplicit.RefreshTokenRequest, + never + >, + res: { + send: (responseBody: SeedInferredAuthImplicit.TokenResponse) => Promise; + cookie: (cookie: string, value: string, options?: express.CookieOptions) => void; + locals: any; + }, + next: express.NextFunction, + ): void | Promise; +} + +export class AuthService { + private router; + + constructor( + private readonly methods: AuthServiceMethods, + middleware: express.RequestHandler[] = [], + ) { + this.router = express.Router({ mergeParams: true }).use( + express.json({ + strict: false, + }), + ...middleware, + ); + } + + public addMiddleware(handler: express.RequestHandler): this { + this.router.use(handler); + return this; + } + + public toRouter(): express.Router { + this.router.post("/token", async (req, res, next) => { + const request = serializers.GetTokenRequest.parse(req.body); + if (request.ok) { + req.body = request.value; + try { + await this.methods.getTokenWithClientCredentials( + req as any, + { + send: async (responseBody) => { + res.json( + serializers.TokenResponse.jsonOrThrow(responseBody, { + unrecognizedObjectKeys: "strip", + }), + ); + }, + cookie: res.cookie.bind(res), + locals: res.locals, + }, + next, + ); + if (!res.writableEnded) { + next(); + } + } catch (error) { + if (error instanceof errors.SeedInferredAuthImplicitError) { + console.warn( + `Endpoint 'getTokenWithClientCredentials' unexpectedly threw ${error.constructor.name}. If this was intentional, please add ${error.constructor.name} to the endpoint's errors list in your Fern Definition.`, + ); + await error.send(res); + } else { + res.status(500).json("Internal Server Error"); + } + next(error); + } + } else { + res.status(422).json({ + errors: request.errors.map( + (error) => `${["request", ...error.path].join(" -> ")}: ${error.message}`, + ), + }); + next(request.errors); + } + }); + this.router.post("/token/refresh", async (req, res, next) => { + const request = serializers.RefreshTokenRequest.parse(req.body); + if (request.ok) { + req.body = request.value; + try { + await this.methods.refreshToken( + req as any, + { + send: async (responseBody) => { + res.json( + serializers.TokenResponse.jsonOrThrow(responseBody, { + unrecognizedObjectKeys: "strip", + }), + ); + }, + cookie: res.cookie.bind(res), + locals: res.locals, + }, + next, + ); + if (!res.writableEnded) { + next(); + } + } catch (error) { + if (error instanceof errors.SeedInferredAuthImplicitError) { + console.warn( + `Endpoint 'refreshToken' unexpectedly threw ${error.constructor.name}. If this was intentional, please add ${error.constructor.name} to the endpoint's errors list in your Fern Definition.`, + ); + await error.send(res); + } else { + res.status(500).json("Internal Server Error"); + } + next(error); + } + } else { + res.status(422).json({ + errors: request.errors.map( + (error) => `${["request", ...error.path].join(" -> ")}: ${error.message}`, + ), + }); + next(request.errors); + } + }); + return this.router; + } +} diff --git a/seed/ts-express/inferred-auth-implicit-reference/api/resources/auth/service/index.ts b/seed/ts-express/inferred-auth-implicit-reference/api/resources/auth/service/index.ts new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/api/resources/auth/service/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/seed/ts-express/inferred-auth-implicit-reference/api/resources/auth/types/GetTokenRequest.ts b/seed/ts-express/inferred-auth-implicit-reference/api/resources/auth/types/GetTokenRequest.ts new file mode 100644 index 000000000000..0fa7baab7cfd --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/api/resources/auth/types/GetTokenRequest.ts @@ -0,0 +1,12 @@ +// This file was auto-generated by Fern from our API Definition. + +/** + * A request to obtain an OAuth token. + */ +export interface GetTokenRequest { + clientId: string; + clientSecret: string; + audience: "https://api.example.com"; + grantType: "client_credentials"; + scope?: string; +} diff --git a/seed/ts-express/inferred-auth-implicit-reference/api/resources/auth/types/RefreshTokenRequest.ts b/seed/ts-express/inferred-auth-implicit-reference/api/resources/auth/types/RefreshTokenRequest.ts new file mode 100644 index 000000000000..cc66a7cc4663 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/api/resources/auth/types/RefreshTokenRequest.ts @@ -0,0 +1,13 @@ +// This file was auto-generated by Fern from our API Definition. + +/** + * A request to refresh an OAuth token. + */ +export interface RefreshTokenRequest { + clientId: string; + clientSecret: string; + refreshToken: string; + audience: "https://api.example.com"; + grantType: "refresh_token"; + scope?: string; +} diff --git a/seed/ts-express/inferred-auth-implicit-reference/api/resources/auth/types/TokenResponse.ts b/seed/ts-express/inferred-auth-implicit-reference/api/resources/auth/types/TokenResponse.ts new file mode 100644 index 000000000000..e61d1b3e971d --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/api/resources/auth/types/TokenResponse.ts @@ -0,0 +1,10 @@ +// This file was auto-generated by Fern from our API Definition. + +/** + * An OAuth token response. + */ +export interface TokenResponse { + accessToken: string; + expiresIn: number; + refreshToken?: string; +} diff --git a/seed/ts-express/inferred-auth-implicit-reference/api/resources/auth/types/index.ts b/seed/ts-express/inferred-auth-implicit-reference/api/resources/auth/types/index.ts new file mode 100644 index 000000000000..3f9dd021bed5 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/api/resources/auth/types/index.ts @@ -0,0 +1,3 @@ +export * from "./GetTokenRequest"; +export * from "./RefreshTokenRequest"; +export * from "./TokenResponse"; diff --git a/seed/ts-express/inferred-auth-implicit-reference/api/resources/index.ts b/seed/ts-express/inferred-auth-implicit-reference/api/resources/index.ts new file mode 100644 index 000000000000..df3df8f7278b --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/api/resources/index.ts @@ -0,0 +1,5 @@ +export * as auth from "./auth"; +export * from "./auth/types"; +export * as nested from "./nested"; +export * as nestedNoAuth from "./nestedNoAuth"; +export * as simple from "./simple"; diff --git a/seed/ts-express/inferred-auth-implicit-reference/api/resources/nested/index.ts b/seed/ts-express/inferred-auth-implicit-reference/api/resources/nested/index.ts new file mode 100644 index 000000000000..3e5335fe4215 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/api/resources/nested/index.ts @@ -0,0 +1 @@ +export * from "./resources"; diff --git a/seed/ts-express/inferred-auth-implicit-reference/api/resources/nested/resources/api/index.ts b/seed/ts-express/inferred-auth-implicit-reference/api/resources/nested/resources/api/index.ts new file mode 100644 index 000000000000..6261f8963643 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/api/resources/nested/resources/api/index.ts @@ -0,0 +1 @@ +export * from "./service"; diff --git a/seed/ts-express/inferred-auth-implicit-reference/api/resources/nested/resources/api/service/ApiService.ts b/seed/ts-express/inferred-auth-implicit-reference/api/resources/nested/resources/api/service/ApiService.ts new file mode 100644 index 000000000000..db7ee25ca410 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/api/resources/nested/resources/api/service/ApiService.ts @@ -0,0 +1,69 @@ +// This file was auto-generated by Fern from our API Definition. + +import express from "express"; +import * as errors from "../../../../../../errors/index"; + +export interface ApiServiceMethods { + getSomething( + req: express.Request, + res: { + send: () => Promise; + cookie: (cookie: string, value: string, options?: express.CookieOptions) => void; + locals: any; + }, + next: express.NextFunction, + ): void | Promise; +} + +export class ApiService { + private router; + + constructor( + private readonly methods: ApiServiceMethods, + middleware: express.RequestHandler[] = [], + ) { + this.router = express.Router({ mergeParams: true }).use( + express.json({ + strict: false, + }), + ...middleware, + ); + } + + public addMiddleware(handler: express.RequestHandler): this { + this.router.use(handler); + return this; + } + + public toRouter(): express.Router { + this.router.get("/get-something", async (req, res, next) => { + try { + await this.methods.getSomething( + req as any, + { + send: async () => { + res.sendStatus(204); + }, + cookie: res.cookie.bind(res), + locals: res.locals, + }, + next, + ); + if (!res.writableEnded) { + next(); + } + } catch (error) { + if (error instanceof errors.SeedInferredAuthImplicitError) { + console.warn( + `Endpoint 'getSomething' unexpectedly threw ${error.constructor.name}. If this was intentional, please add ${error.constructor.name} to the endpoint's errors list in your Fern Definition.`, + ); + await error.send(res); + } else { + res.status(500).json("Internal Server Error"); + } + next(error); + } + }); + return this.router; + } +} diff --git a/seed/ts-express/inferred-auth-implicit-reference/api/resources/nested/resources/api/service/index.ts b/seed/ts-express/inferred-auth-implicit-reference/api/resources/nested/resources/api/service/index.ts new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/api/resources/nested/resources/api/service/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/seed/ts-express/inferred-auth-implicit-reference/api/resources/nested/resources/index.ts b/seed/ts-express/inferred-auth-implicit-reference/api/resources/nested/resources/index.ts new file mode 100644 index 000000000000..d7f360220400 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/api/resources/nested/resources/index.ts @@ -0,0 +1 @@ +export * as api from "./api"; diff --git a/seed/ts-express/inferred-auth-implicit-reference/api/resources/nestedNoAuth/index.ts b/seed/ts-express/inferred-auth-implicit-reference/api/resources/nestedNoAuth/index.ts new file mode 100644 index 000000000000..3e5335fe4215 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/api/resources/nestedNoAuth/index.ts @@ -0,0 +1 @@ +export * from "./resources"; diff --git a/seed/ts-express/inferred-auth-implicit-reference/api/resources/nestedNoAuth/resources/api/index.ts b/seed/ts-express/inferred-auth-implicit-reference/api/resources/nestedNoAuth/resources/api/index.ts new file mode 100644 index 000000000000..6261f8963643 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/api/resources/nestedNoAuth/resources/api/index.ts @@ -0,0 +1 @@ +export * from "./service"; diff --git a/seed/ts-express/inferred-auth-implicit-reference/api/resources/nestedNoAuth/resources/api/service/ApiService.ts b/seed/ts-express/inferred-auth-implicit-reference/api/resources/nestedNoAuth/resources/api/service/ApiService.ts new file mode 100644 index 000000000000..db7ee25ca410 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/api/resources/nestedNoAuth/resources/api/service/ApiService.ts @@ -0,0 +1,69 @@ +// This file was auto-generated by Fern from our API Definition. + +import express from "express"; +import * as errors from "../../../../../../errors/index"; + +export interface ApiServiceMethods { + getSomething( + req: express.Request, + res: { + send: () => Promise; + cookie: (cookie: string, value: string, options?: express.CookieOptions) => void; + locals: any; + }, + next: express.NextFunction, + ): void | Promise; +} + +export class ApiService { + private router; + + constructor( + private readonly methods: ApiServiceMethods, + middleware: express.RequestHandler[] = [], + ) { + this.router = express.Router({ mergeParams: true }).use( + express.json({ + strict: false, + }), + ...middleware, + ); + } + + public addMiddleware(handler: express.RequestHandler): this { + this.router.use(handler); + return this; + } + + public toRouter(): express.Router { + this.router.get("/get-something", async (req, res, next) => { + try { + await this.methods.getSomething( + req as any, + { + send: async () => { + res.sendStatus(204); + }, + cookie: res.cookie.bind(res), + locals: res.locals, + }, + next, + ); + if (!res.writableEnded) { + next(); + } + } catch (error) { + if (error instanceof errors.SeedInferredAuthImplicitError) { + console.warn( + `Endpoint 'getSomething' unexpectedly threw ${error.constructor.name}. If this was intentional, please add ${error.constructor.name} to the endpoint's errors list in your Fern Definition.`, + ); + await error.send(res); + } else { + res.status(500).json("Internal Server Error"); + } + next(error); + } + }); + return this.router; + } +} diff --git a/seed/ts-express/inferred-auth-implicit-reference/api/resources/nestedNoAuth/resources/api/service/index.ts b/seed/ts-express/inferred-auth-implicit-reference/api/resources/nestedNoAuth/resources/api/service/index.ts new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/api/resources/nestedNoAuth/resources/api/service/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/seed/ts-express/inferred-auth-implicit-reference/api/resources/nestedNoAuth/resources/index.ts b/seed/ts-express/inferred-auth-implicit-reference/api/resources/nestedNoAuth/resources/index.ts new file mode 100644 index 000000000000..d7f360220400 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/api/resources/nestedNoAuth/resources/index.ts @@ -0,0 +1 @@ +export * as api from "./api"; diff --git a/seed/ts-express/inferred-auth-implicit-reference/api/resources/simple/index.ts b/seed/ts-express/inferred-auth-implicit-reference/api/resources/simple/index.ts new file mode 100644 index 000000000000..6261f8963643 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/api/resources/simple/index.ts @@ -0,0 +1 @@ +export * from "./service"; diff --git a/seed/ts-express/inferred-auth-implicit-reference/api/resources/simple/service/SimpleService.ts b/seed/ts-express/inferred-auth-implicit-reference/api/resources/simple/service/SimpleService.ts new file mode 100644 index 000000000000..33816d9dcaa8 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/api/resources/simple/service/SimpleService.ts @@ -0,0 +1,69 @@ +// This file was auto-generated by Fern from our API Definition. + +import express from "express"; +import * as errors from "../../../../errors/index"; + +export interface SimpleServiceMethods { + getSomething( + req: express.Request, + res: { + send: () => Promise; + cookie: (cookie: string, value: string, options?: express.CookieOptions) => void; + locals: any; + }, + next: express.NextFunction, + ): void | Promise; +} + +export class SimpleService { + private router; + + constructor( + private readonly methods: SimpleServiceMethods, + middleware: express.RequestHandler[] = [], + ) { + this.router = express.Router({ mergeParams: true }).use( + express.json({ + strict: false, + }), + ...middleware, + ); + } + + public addMiddleware(handler: express.RequestHandler): this { + this.router.use(handler); + return this; + } + + public toRouter(): express.Router { + this.router.get("/get-something", async (req, res, next) => { + try { + await this.methods.getSomething( + req as any, + { + send: async () => { + res.sendStatus(204); + }, + cookie: res.cookie.bind(res), + locals: res.locals, + }, + next, + ); + if (!res.writableEnded) { + next(); + } + } catch (error) { + if (error instanceof errors.SeedInferredAuthImplicitError) { + console.warn( + `Endpoint 'getSomething' unexpectedly threw ${error.constructor.name}. If this was intentional, please add ${error.constructor.name} to the endpoint's errors list in your Fern Definition.`, + ); + await error.send(res); + } else { + res.status(500).json("Internal Server Error"); + } + next(error); + } + }); + return this.router; + } +} diff --git a/seed/ts-express/inferred-auth-implicit-reference/api/resources/simple/service/index.ts b/seed/ts-express/inferred-auth-implicit-reference/api/resources/simple/service/index.ts new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/api/resources/simple/service/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/index.ts b/seed/ts-express/inferred-auth-implicit-reference/core/index.ts new file mode 100644 index 000000000000..3ae53c06d387 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/index.ts @@ -0,0 +1 @@ +export * as serialization from "./schemas"; diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/Schema.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/Schema.ts new file mode 100644 index 000000000000..cce732a2ee12 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/Schema.ts @@ -0,0 +1,103 @@ +import type { SchemaUtils } from "./builders/index"; + +export type Schema = BaseSchema & SchemaUtils; + +export type inferRaw = S extends Schema ? Raw : never; +export type inferParsed = S extends Schema ? Parsed : never; + +export interface BaseSchema { + parse: (raw: unknown, opts?: SchemaOptions) => MaybeValid; + json: (parsed: unknown, opts?: SchemaOptions) => MaybeValid; + getType: () => SchemaType | SchemaType; +} + +export const SchemaType = { + BIGINT: "bigint", + DATE: "date", + ENUM: "enum", + LIST: "list", + STRING_LITERAL: "stringLiteral", + BOOLEAN_LITERAL: "booleanLiteral", + OBJECT: "object", + ANY: "any", + BOOLEAN: "boolean", + NUMBER: "number", + STRING: "string", + UNKNOWN: "unknown", + NEVER: "never", + RECORD: "record", + SET: "set", + UNION: "union", + UNDISCRIMINATED_UNION: "undiscriminatedUnion", + NULLABLE: "nullable", + OPTIONAL: "optional", + OPTIONAL_NULLABLE: "optionalNullable", +} as const; + +export type SchemaType = (typeof SchemaType)[keyof typeof SchemaType]; + +export type MaybeValid = Valid | Invalid; + +export interface Valid { + ok: true; + value: T; +} + +export interface Invalid { + ok: false; + errors: ValidationError[]; +} + +export interface ValidationError { + path: string[]; + message: string; +} + +export interface SchemaOptions { + /** + * how to handle unrecognized keys in objects + * + * @default "fail" + */ + unrecognizedObjectKeys?: "fail" | "passthrough" | "strip"; + + /** + * whether to fail when an unrecognized discriminant value is + * encountered in a union + * + * @default false + */ + allowUnrecognizedUnionMembers?: boolean; + + /** + * whether to fail when an unrecognized enum value is encountered + * + * @default false + */ + allowUnrecognizedEnumValues?: boolean; + + /** + * whether to allow data that doesn't conform to the schema. + * invalid data is passed through without transformation. + * + * when this is enabled, .parse() and .json() will always + * return `ok: true`. `.parseOrThrow()` and `.jsonOrThrow()` + * will never fail. + * + * @default false + */ + skipValidation?: boolean; + + /** + * each validation failure contains a "path" property, which is + * the breadcrumbs to the offending node in the JSON. you can supply + * a prefix that is prepended to all the errors' paths. this can be + * helpful for zurg's internal debug logging. + */ + breadcrumbsPrefix?: string[]; + + /** + * whether to send 'null' for optional properties explicitly set to 'undefined'. + */ + omitUndefined?: boolean; +} diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/bigint/bigint.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/bigint/bigint.ts new file mode 100644 index 000000000000..9d8a63d450eb --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/bigint/bigint.ts @@ -0,0 +1,55 @@ +import { type BaseSchema, type Schema, SchemaType } from "../../Schema"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; +import { maybeSkipValidation } from "../../utils/maybeSkipValidation"; +import { getSchemaUtils } from "../schema-utils/index"; + +export function bigint(): Schema { + const baseSchema: BaseSchema = { + parse: (raw, { breadcrumbsPrefix = [] } = {}) => { + if (typeof raw === "bigint") { + return { + ok: true, + value: raw, + }; + } + if (typeof raw === "number") { + return { + ok: true, + value: BigInt(raw), + }; + } + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(raw, "bigint | number"), + }, + ], + }; + }, + json: (bigint, { breadcrumbsPrefix = [] } = {}) => { + if (typeof bigint !== "bigint") { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(bigint, "bigint"), + }, + ], + }; + } + return { + ok: true, + value: bigint, + }; + }, + getType: () => SchemaType.BIGINT, + }; + + return { + ...maybeSkipValidation(baseSchema), + ...getSchemaUtils(baseSchema), + }; +} diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/bigint/index.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/bigint/index.ts new file mode 100644 index 000000000000..e5843043fcbf --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/bigint/index.ts @@ -0,0 +1 @@ +export { bigint } from "./bigint"; diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/date/date.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/date/date.ts new file mode 100644 index 000000000000..bc507e08539c --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/date/date.ts @@ -0,0 +1,65 @@ +import { type BaseSchema, type Schema, SchemaType } from "../../Schema"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; +import { maybeSkipValidation } from "../../utils/maybeSkipValidation"; +import { getSchemaUtils } from "../schema-utils/index"; + +// https://stackoverflow.com/questions/12756159/regex-and-iso8601-formatted-datetime +const ISO_8601_REGEX = + /^([+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))([T\s]((([01]\d|2[0-3])((:?)[0-5]\d)?|24:?00)([.,]\d+(?!:))?)?(\17[0-5]\d([.,]\d+)?)?([zZ]|([+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?$/; + +export function date(): Schema { + const baseSchema: BaseSchema = { + parse: (raw, { breadcrumbsPrefix = [] } = {}) => { + if (typeof raw !== "string") { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(raw, "string"), + }, + ], + }; + } + if (!ISO_8601_REGEX.test(raw)) { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(raw, "ISO 8601 date string"), + }, + ], + }; + } + return { + ok: true, + value: new Date(raw), + }; + }, + json: (date, { breadcrumbsPrefix = [] } = {}) => { + if (date instanceof Date) { + return { + ok: true, + value: date.toISOString(), + }; + } else { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(date, "Date object"), + }, + ], + }; + } + }, + getType: () => SchemaType.DATE, + }; + + return { + ...maybeSkipValidation(baseSchema), + ...getSchemaUtils(baseSchema), + }; +} diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/date/index.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/date/index.ts new file mode 100644 index 000000000000..187b29040f6e --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/date/index.ts @@ -0,0 +1 @@ +export { date } from "./date"; diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/enum/enum.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/enum/enum.ts new file mode 100644 index 000000000000..c2c49a323e1c --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/enum/enum.ts @@ -0,0 +1,43 @@ +import { type Schema, SchemaType } from "../../Schema"; +import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; + +export function enum_(values: E): Schema { + const validValues = new Set(values); + + const schemaCreator = createIdentitySchemaCreator( + SchemaType.ENUM, + (value, { allowUnrecognizedEnumValues, breadcrumbsPrefix = [] } = {}) => { + if (typeof value !== "string") { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(value, "string"), + }, + ], + }; + } + + if (!validValues.has(value) && !allowUnrecognizedEnumValues) { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(value, "enum"), + }, + ], + }; + } + + return { + ok: true, + value: value as U, + }; + }, + ); + + return schemaCreator(); +} diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/enum/index.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/enum/index.ts new file mode 100644 index 000000000000..fe6faed93e3f --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/enum/index.ts @@ -0,0 +1 @@ +export { enum_ } from "./enum"; diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/index.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/index.ts new file mode 100644 index 000000000000..be23de25800d --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/index.ts @@ -0,0 +1,14 @@ +export * from "./bigint/index"; +export * from "./date/index"; +export * from "./enum/index"; +export * from "./lazy/index"; +export * from "./list/index"; +export * from "./literals/index"; +export * from "./object/index"; +export * from "./object-like/index"; +export * from "./primitives/index"; +export * from "./record/index"; +export * from "./schema-utils/index"; +export * from "./set/index"; +export * from "./undiscriminated-union/index"; +export * from "./union/index"; diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/lazy/index.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/lazy/index.ts new file mode 100644 index 000000000000..2221abd8806b --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/lazy/index.ts @@ -0,0 +1,3 @@ +export type { SchemaGetter } from "./lazy"; +export { lazy } from "./lazy"; +export { lazyObject } from "./lazyObject"; diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/lazy/lazy.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/lazy/lazy.ts new file mode 100644 index 000000000000..a72735184f38 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/lazy/lazy.ts @@ -0,0 +1,32 @@ +import type { BaseSchema, Schema } from "../../Schema"; +import { getSchemaUtils } from "../schema-utils/index"; + +export type SchemaGetter> = () => SchemaType; + +export function lazy(getter: SchemaGetter>): Schema { + const baseSchema = constructLazyBaseSchema(getter); + return { + ...baseSchema, + ...getSchemaUtils(baseSchema), + }; +} + +export function constructLazyBaseSchema( + getter: SchemaGetter>, +): BaseSchema { + return { + parse: (raw, opts) => getMemoizedSchema(getter).parse(raw, opts), + json: (parsed, opts) => getMemoizedSchema(getter).json(parsed, opts), + getType: () => getMemoizedSchema(getter).getType(), + }; +} + +type MemoizedGetter> = SchemaGetter & { __zurg_memoized?: SchemaType }; + +export function getMemoizedSchema>(getter: SchemaGetter): SchemaType { + const castedGetter = getter as MemoizedGetter; + if (castedGetter.__zurg_memoized == null) { + castedGetter.__zurg_memoized = getter(); + } + return castedGetter.__zurg_memoized; +} diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/lazy/lazyObject.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/lazy/lazyObject.ts new file mode 100644 index 000000000000..2a58a2abb203 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/lazy/lazyObject.ts @@ -0,0 +1,20 @@ +import { getObjectUtils } from "../object/index"; +import type { BaseObjectSchema, ObjectSchema } from "../object/types"; +import { getObjectLikeUtils } from "../object-like/index"; +import { getSchemaUtils } from "../schema-utils/index"; +import { constructLazyBaseSchema, getMemoizedSchema, type SchemaGetter } from "./lazy"; + +export function lazyObject(getter: SchemaGetter>): ObjectSchema { + const baseSchema: BaseObjectSchema = { + ...constructLazyBaseSchema(getter), + _getRawProperties: () => getMemoizedSchema(getter)._getRawProperties(), + _getParsedProperties: () => getMemoizedSchema(getter)._getParsedProperties(), + }; + + return { + ...baseSchema, + ...getSchemaUtils(baseSchema), + ...getObjectLikeUtils(baseSchema), + ...getObjectUtils(baseSchema), + }; +} diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/list/index.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/list/index.ts new file mode 100644 index 000000000000..25f4bcc17377 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/list/index.ts @@ -0,0 +1 @@ +export { list } from "./list"; diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/list/list.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/list/list.ts new file mode 100644 index 000000000000..7c8fd6e87db9 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/list/list.ts @@ -0,0 +1,73 @@ +import { type BaseSchema, type MaybeValid, type Schema, SchemaType, type ValidationError } from "../../Schema"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; +import { maybeSkipValidation } from "../../utils/maybeSkipValidation"; +import { getSchemaUtils } from "../schema-utils/index"; + +export function list(schema: Schema): Schema { + const baseSchema: BaseSchema = { + parse: (raw, opts) => + validateAndTransformArray(raw, (item, index) => + schema.parse(item, { + ...opts, + breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), `[${index}]`], + }), + ), + json: (parsed, opts) => + validateAndTransformArray(parsed, (item, index) => + schema.json(item, { + ...opts, + breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), `[${index}]`], + }), + ), + getType: () => SchemaType.LIST, + }; + + return { + ...maybeSkipValidation(baseSchema), + ...getSchemaUtils(baseSchema), + }; +} + +function validateAndTransformArray( + value: unknown, + transformItem: (item: Raw, index: number) => MaybeValid, +): MaybeValid { + if (!Array.isArray(value)) { + return { + ok: false, + errors: [ + { + message: getErrorMessageForIncorrectType(value, "list"), + path: [], + }, + ], + }; + } + + const maybeValidItems = value.map((item, index) => transformItem(item, index)); + + return maybeValidItems.reduce>( + (acc, item) => { + if (acc.ok && item.ok) { + return { + ok: true, + value: [...acc.value, item.value], + }; + } + + const errors: ValidationError[] = []; + if (!acc.ok) { + errors.push(...acc.errors); + } + if (!item.ok) { + errors.push(...item.errors); + } + + return { + ok: false, + errors, + }; + }, + { ok: true, value: [] }, + ); +} diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/literals/booleanLiteral.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/literals/booleanLiteral.ts new file mode 100644 index 000000000000..fb06cc00f0de --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/literals/booleanLiteral.ts @@ -0,0 +1,29 @@ +import { type Schema, SchemaType } from "../../Schema"; +import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; + +export function booleanLiteral(literal: V): Schema { + const schemaCreator = createIdentitySchemaCreator( + SchemaType.BOOLEAN_LITERAL, + (value, { breadcrumbsPrefix = [] } = {}) => { + if (value === literal) { + return { + ok: true, + value: literal, + }; + } else { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(value, `${literal.toString()}`), + }, + ], + }; + } + }, + ); + + return schemaCreator(); +} diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/literals/index.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/literals/index.ts new file mode 100644 index 000000000000..ae7389065564 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/literals/index.ts @@ -0,0 +1,2 @@ +export { booleanLiteral } from "./booleanLiteral"; +export { stringLiteral } from "./stringLiteral"; diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/literals/stringLiteral.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/literals/stringLiteral.ts new file mode 100644 index 000000000000..98fe06223fe4 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/literals/stringLiteral.ts @@ -0,0 +1,29 @@ +import { type Schema, SchemaType } from "../../Schema"; +import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; + +export function stringLiteral(literal: V): Schema { + const schemaCreator = createIdentitySchemaCreator( + SchemaType.STRING_LITERAL, + (value, { breadcrumbsPrefix = [] } = {}) => { + if (value === literal) { + return { + ok: true, + value: literal, + }; + } else { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(value, `"${literal}"`), + }, + ], + }; + } + }, + ); + + return schemaCreator(); +} diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/object-like/getObjectLikeUtils.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/object-like/getObjectLikeUtils.ts new file mode 100644 index 000000000000..ed96cf771cbb --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/object-like/getObjectLikeUtils.ts @@ -0,0 +1,79 @@ +import type { BaseSchema } from "../../Schema"; +import { filterObject } from "../../utils/filterObject"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; +import { isPlainObject } from "../../utils/isPlainObject"; +import { getSchemaUtils } from "../schema-utils/index"; +import type { ObjectLikeSchema, ObjectLikeUtils } from "./types"; + +export function getObjectLikeUtils(schema: BaseSchema): ObjectLikeUtils { + return { + withParsedProperties: (properties) => withParsedProperties(schema, properties), + }; +} + +/** + * object-like utils are defined in one file to resolve issues with circular imports + */ + +export function withParsedProperties( + objectLike: BaseSchema, + properties: { [K in keyof Properties]: Properties[K] | ((parsed: ParsedObjectShape) => Properties[K]) }, +): ObjectLikeSchema { + const objectSchema: BaseSchema = { + parse: (raw, opts) => { + const parsedObject = objectLike.parse(raw, opts); + if (!parsedObject.ok) { + return parsedObject; + } + + const additionalProperties = Object.entries(properties).reduce>( + (processed, [key, value]) => { + return { + ...processed, + [key]: typeof value === "function" ? value(parsedObject.value) : value, + }; + }, + {}, + ); + + return { + ok: true, + value: { + ...parsedObject.value, + ...(additionalProperties as Properties), + }, + }; + }, + + json: (parsed, opts) => { + if (!isPlainObject(parsed)) { + return { + ok: false, + errors: [ + { + path: opts?.breadcrumbsPrefix ?? [], + message: getErrorMessageForIncorrectType(parsed, "object"), + }, + ], + }; + } + + // strip out added properties + const addedPropertyKeys = new Set(Object.keys(properties)); + const parsedWithoutAddedProperties = filterObject( + parsed, + Object.keys(parsed).filter((key) => !addedPropertyKeys.has(key)), + ); + + return objectLike.json(parsedWithoutAddedProperties as ParsedObjectShape, opts); + }, + + getType: () => objectLike.getType(), + }; + + return { + ...objectSchema, + ...getSchemaUtils(objectSchema), + ...getObjectLikeUtils(objectSchema), + }; +} diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/object-like/index.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/object-like/index.ts new file mode 100644 index 000000000000..c342e72cf9d1 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/object-like/index.ts @@ -0,0 +1,2 @@ +export { getObjectLikeUtils, withParsedProperties } from "./getObjectLikeUtils"; +export type { ObjectLikeSchema, ObjectLikeUtils } from "./types"; diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/object-like/types.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/object-like/types.ts new file mode 100644 index 000000000000..a8401d7cf01e --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/object-like/types.ts @@ -0,0 +1,13 @@ +import type { BaseSchema, Schema } from "../../Schema"; + +export type ObjectLikeSchema = Schema & + BaseSchema & + ObjectLikeUtils; + +export interface ObjectLikeUtils { + withParsedProperties: >( + properties: { + [K in keyof T]: T[K] | ((parsed: Parsed) => T[K]); + }, + ) => ObjectLikeSchema; +} diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/object/index.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/object/index.ts new file mode 100644 index 000000000000..58ce0b48b23c --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/object/index.ts @@ -0,0 +1,22 @@ +export { getObjectUtils, object } from "./object"; +export type { + inferObjectWithoutOptionalPropertiesSchemaFromPropertySchemas, + inferParsedObjectWithoutOptionalPropertiesFromPropertySchemas, +} from "./objectWithoutOptionalProperties"; +export { objectWithoutOptionalProperties } from "./objectWithoutOptionalProperties"; +export type { Property } from "./property"; +export { isProperty, property } from "./property"; +export type { + BaseObjectSchema, + inferObjectSchemaFromPropertySchemas, + inferParsedObject, + inferParsedObjectFromPropertySchemas, + inferParsedPropertySchema, + inferRawKey, + inferRawObject, + inferRawObjectFromPropertySchemas, + inferRawPropertySchema, + ObjectSchema, + ObjectUtils, + PropertySchemas, +} from "./types"; diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/object/object.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/object/object.ts new file mode 100644 index 000000000000..bdad8076717c --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/object/object.ts @@ -0,0 +1,382 @@ +import { type MaybeValid, type Schema, SchemaType, type ValidationError } from "../../Schema"; +import { entries } from "../../utils/entries"; +import { filterObject } from "../../utils/filterObject"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; +import { isPlainObject } from "../../utils/isPlainObject"; +import { keys } from "../../utils/keys"; +import { maybeSkipValidation } from "../../utils/maybeSkipValidation"; +import { partition } from "../../utils/partition"; +import { getObjectLikeUtils } from "../object-like/index"; +import { getSchemaUtils } from "../schema-utils/index"; +import { isProperty } from "./property"; +import type { + BaseObjectSchema, + inferObjectSchemaFromPropertySchemas, + inferParsedObjectFromPropertySchemas, + inferRawObjectFromPropertySchemas, + ObjectSchema, + ObjectUtils, + PropertySchemas, +} from "./types"; + +interface ObjectPropertyWithRawKey { + rawKey: string; + parsedKey: string; + valueSchema: Schema; +} + +export function object>( + schemas: T, +): inferObjectSchemaFromPropertySchemas { + const baseSchema: BaseObjectSchema< + inferRawObjectFromPropertySchemas, + inferParsedObjectFromPropertySchemas + > = { + _getRawProperties: () => + Object.entries(schemas).map(([parsedKey, propertySchema]) => + isProperty(propertySchema) ? propertySchema.rawKey : parsedKey, + ) as unknown as (keyof inferRawObjectFromPropertySchemas)[], + _getParsedProperties: () => keys(schemas) as unknown as (keyof inferParsedObjectFromPropertySchemas)[], + + parse: (raw, opts) => { + const rawKeyToProperty: Record = {}; + const requiredKeys: string[] = []; + + for (const [parsedKey, schemaOrObjectProperty] of entries(schemas)) { + const rawKey = isProperty(schemaOrObjectProperty) ? schemaOrObjectProperty.rawKey : parsedKey; + const valueSchema: Schema = isProperty(schemaOrObjectProperty) + ? schemaOrObjectProperty.valueSchema + : schemaOrObjectProperty; + + const property: ObjectPropertyWithRawKey = { + rawKey, + parsedKey: parsedKey as string, + valueSchema, + }; + + rawKeyToProperty[rawKey] = property; + + if (isSchemaRequired(valueSchema)) { + requiredKeys.push(rawKey); + } + } + + return validateAndTransformObject({ + value: raw, + requiredKeys, + getProperty: (rawKey) => { + const property = rawKeyToProperty[rawKey]; + if (property == null) { + return undefined; + } + return { + transformedKey: property.parsedKey, + transform: (propertyValue) => + property.valueSchema.parse(propertyValue, { + ...opts, + breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), rawKey], + }), + }; + }, + unrecognizedObjectKeys: opts?.unrecognizedObjectKeys, + skipValidation: opts?.skipValidation, + breadcrumbsPrefix: opts?.breadcrumbsPrefix, + omitUndefined: opts?.omitUndefined, + }); + }, + + json: (parsed, opts) => { + const requiredKeys: string[] = []; + + for (const [parsedKey, schemaOrObjectProperty] of entries(schemas)) { + const valueSchema: Schema = isProperty(schemaOrObjectProperty) + ? schemaOrObjectProperty.valueSchema + : schemaOrObjectProperty; + + if (isSchemaRequired(valueSchema)) { + requiredKeys.push(parsedKey as string); + } + } + + return validateAndTransformObject({ + value: parsed, + requiredKeys, + getProperty: ( + parsedKey, + ): { transformedKey: string; transform: (propertyValue: object) => MaybeValid } | undefined => { + const property = schemas[parsedKey as keyof T]; + + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (property == null) { + return undefined; + } + + if (isProperty(property)) { + return { + transformedKey: property.rawKey, + transform: (propertyValue) => + property.valueSchema.json(propertyValue, { + ...opts, + breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), parsedKey], + }), + }; + } else { + return { + transformedKey: parsedKey, + transform: (propertyValue) => + property.json(propertyValue, { + ...opts, + breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), parsedKey], + }), + }; + } + }, + unrecognizedObjectKeys: opts?.unrecognizedObjectKeys, + skipValidation: opts?.skipValidation, + breadcrumbsPrefix: opts?.breadcrumbsPrefix, + omitUndefined: opts?.omitUndefined, + }); + }, + + getType: () => SchemaType.OBJECT, + }; + + return { + ...maybeSkipValidation(baseSchema), + ...getSchemaUtils(baseSchema), + ...getObjectLikeUtils(baseSchema), + ...getObjectUtils(baseSchema), + }; +} + +function validateAndTransformObject({ + value, + requiredKeys, + getProperty, + unrecognizedObjectKeys = "fail", + skipValidation = false, + breadcrumbsPrefix = [], +}: { + value: unknown; + requiredKeys: string[]; + getProperty: ( + preTransformedKey: string, + ) => { transformedKey: string; transform: (propertyValue: object) => MaybeValid } | undefined; + unrecognizedObjectKeys: "fail" | "passthrough" | "strip" | undefined; + skipValidation: boolean | undefined; + breadcrumbsPrefix: string[] | undefined; + omitUndefined: boolean | undefined; +}): MaybeValid { + if (!isPlainObject(value)) { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(value, "object"), + }, + ], + }; + } + + const missingRequiredKeys = new Set(requiredKeys); + const errors: ValidationError[] = []; + const transformed: Record = {}; + + for (const [preTransformedKey, preTransformedItemValue] of Object.entries(value)) { + const property = getProperty(preTransformedKey); + + if (property != null) { + missingRequiredKeys.delete(preTransformedKey); + + const value = property.transform(preTransformedItemValue as object); + if (value.ok) { + transformed[property.transformedKey] = value.value; + } else { + transformed[preTransformedKey] = preTransformedItemValue; + errors.push(...value.errors); + } + } else { + switch (unrecognizedObjectKeys) { + case "fail": + errors.push({ + path: [...breadcrumbsPrefix, preTransformedKey], + message: `Unexpected key "${preTransformedKey}"`, + }); + break; + case "strip": + break; + case "passthrough": + transformed[preTransformedKey] = preTransformedItemValue; + break; + } + } + } + + errors.push( + ...requiredKeys + .filter((key) => missingRequiredKeys.has(key)) + .map((key) => ({ + path: breadcrumbsPrefix, + message: `Missing required key "${key}"`, + })), + ); + + if (errors.length === 0 || skipValidation) { + return { + ok: true, + value: transformed as Transformed, + }; + } else { + return { + ok: false, + errors, + }; + } +} + +export function getObjectUtils(schema: BaseObjectSchema): ObjectUtils { + return { + extend: (extension: ObjectSchema) => { + const baseSchema: BaseObjectSchema = { + _getParsedProperties: () => [...schema._getParsedProperties(), ...extension._getParsedProperties()], + _getRawProperties: () => [...schema._getRawProperties(), ...extension._getRawProperties()], + parse: (raw, opts) => { + return validateAndTransformExtendedObject({ + extensionKeys: extension._getRawProperties(), + value: raw, + transformBase: (rawBase) => schema.parse(rawBase, opts), + transformExtension: (rawExtension) => extension.parse(rawExtension, opts), + breadcrumbsPrefix: opts?.breadcrumbsPrefix, + }); + }, + json: (parsed, opts) => { + return validateAndTransformExtendedObject({ + extensionKeys: extension._getParsedProperties(), + value: parsed, + transformBase: (parsedBase) => schema.json(parsedBase, opts), + transformExtension: (parsedExtension) => extension.json(parsedExtension, opts), + breadcrumbsPrefix: opts?.breadcrumbsPrefix, + }); + }, + getType: () => SchemaType.OBJECT, + }; + + return { + ...baseSchema, + ...getSchemaUtils(baseSchema), + ...getObjectLikeUtils(baseSchema), + ...getObjectUtils(baseSchema), + }; + }, + passthrough: () => { + const baseSchema: BaseObjectSchema = + { + _getParsedProperties: () => schema._getParsedProperties(), + _getRawProperties: () => schema._getRawProperties(), + parse: (raw, opts) => { + const transformed = schema.parse(raw, { ...opts, unrecognizedObjectKeys: "passthrough" }); + if (!transformed.ok) { + return transformed; + } + return { + ok: true, + value: { + ...(raw as any), + ...transformed.value, + }, + }; + }, + json: (parsed, opts) => { + const transformed = schema.json(parsed, { ...opts, unrecognizedObjectKeys: "passthrough" }); + if (!transformed.ok) { + return transformed; + } + return { + ok: true, + value: { + ...(parsed as any), + ...transformed.value, + }, + }; + }, + getType: () => SchemaType.OBJECT, + }; + + return { + ...baseSchema, + ...getSchemaUtils(baseSchema), + ...getObjectLikeUtils(baseSchema), + ...getObjectUtils(baseSchema), + }; + }, + }; +} + +function validateAndTransformExtendedObject({ + extensionKeys, + value, + transformBase, + transformExtension, + breadcrumbsPrefix = [], +}: { + extensionKeys: (keyof PreTransformedExtension)[]; + value: unknown; + transformBase: (value: object) => MaybeValid; + transformExtension: (value: object) => MaybeValid; + breadcrumbsPrefix?: string[]; +}): MaybeValid { + if (!isPlainObject(value)) { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(value, "object"), + }, + ], + }; + } + + const extensionPropertiesSet = new Set(extensionKeys); + const [extensionProperties, baseProperties] = partition(keys(value), (key) => + extensionPropertiesSet.has(key as keyof PreTransformedExtension), + ); + + const transformedBase = transformBase(filterObject(value, baseProperties)); + const transformedExtension = transformExtension(filterObject(value, extensionProperties)); + + if (transformedBase.ok && transformedExtension.ok) { + return { + ok: true, + value: { + ...transformedBase.value, + ...transformedExtension.value, + }, + }; + } else { + return { + ok: false, + errors: [ + ...(transformedBase.ok ? [] : transformedBase.errors), + ...(transformedExtension.ok ? [] : transformedExtension.errors), + ], + }; + } +} + +function isSchemaRequired(schema: Schema): boolean { + return !isSchemaOptional(schema); +} + +function isSchemaOptional(schema: Schema): boolean { + switch (schema.getType()) { + case SchemaType.ANY: + case SchemaType.UNKNOWN: + case SchemaType.OPTIONAL: + case SchemaType.OPTIONAL_NULLABLE: + return true; + default: + return false; + } +} diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/object/objectWithoutOptionalProperties.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/object/objectWithoutOptionalProperties.ts new file mode 100644 index 000000000000..bac2e1e1af0f --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/object/objectWithoutOptionalProperties.ts @@ -0,0 +1,23 @@ +import { object } from "./object"; +import type { + inferParsedPropertySchema, + inferRawObjectFromPropertySchemas, + ObjectSchema, + PropertySchemas, +} from "./types"; + +export function objectWithoutOptionalProperties>( + schemas: T, +): inferObjectWithoutOptionalPropertiesSchemaFromPropertySchemas { + return object(schemas) as unknown as inferObjectWithoutOptionalPropertiesSchemaFromPropertySchemas; +} + +export type inferObjectWithoutOptionalPropertiesSchemaFromPropertySchemas> = + ObjectSchema< + inferRawObjectFromPropertySchemas, + inferParsedObjectWithoutOptionalPropertiesFromPropertySchemas + >; + +export type inferParsedObjectWithoutOptionalPropertiesFromPropertySchemas> = { + [K in keyof T]: inferParsedPropertySchema; +}; diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/object/property.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/object/property.ts new file mode 100644 index 000000000000..ef07c2a7952f --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/object/property.ts @@ -0,0 +1,23 @@ +import type { Schema } from "../../Schema"; + +export function property( + rawKey: RawKey, + valueSchema: Schema, +): Property { + return { + rawKey, + valueSchema, + isProperty: true, + }; +} + +export interface Property { + rawKey: RawKey; + valueSchema: Schema; + isProperty: true; +} + +export function isProperty>(maybeProperty: unknown): maybeProperty is O { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + return (maybeProperty as O).isProperty; +} diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/object/types.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/object/types.ts new file mode 100644 index 000000000000..57f87486910a --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/object/types.ts @@ -0,0 +1,73 @@ +import type { BaseSchema, inferParsed, inferRaw, Schema } from "../../Schema"; +import type { addQuestionMarksToNullableProperties } from "../../utils/addQuestionMarksToNullableProperties"; +import type { ObjectLikeUtils } from "../object-like/index"; +import type { SchemaUtils } from "../schema-utils/index"; +import type { Property } from "./property"; + +export type ObjectSchema = BaseObjectSchema & + ObjectLikeUtils & + ObjectUtils & + SchemaUtils; + +export interface BaseObjectSchema extends BaseSchema { + _getRawProperties: () => (keyof Raw)[]; + _getParsedProperties: () => (keyof Parsed)[]; +} + +export interface ObjectUtils { + extend: ( + schemas: ObjectSchema, + ) => ObjectSchema; + passthrough: () => ObjectSchema; +} + +export type inferRawObject> = O extends ObjectSchema ? Raw : never; + +export type inferParsedObject> = O extends ObjectSchema + ? Parsed + : never; + +export type inferObjectSchemaFromPropertySchemas> = ObjectSchema< + inferRawObjectFromPropertySchemas, + inferParsedObjectFromPropertySchemas +>; + +export type inferRawObjectFromPropertySchemas> = + addQuestionMarksToNullableProperties<{ + [ParsedKey in keyof T as inferRawKey]: inferRawPropertySchema; + }>; + +export type inferParsedObjectFromPropertySchemas> = + addQuestionMarksToNullableProperties<{ + [K in keyof T]: inferParsedPropertySchema; + }>; + +export type PropertySchemas = Record< + ParsedKeys, + Property | Schema +>; + +export type inferRawPropertySchema

| Schema> = P extends Property< + any, + infer Raw, + any +> + ? Raw + : P extends Schema + ? inferRaw

+ : never; + +export type inferParsedPropertySchema

| Schema> = P extends Property< + any, + any, + infer Parsed +> + ? Parsed + : P extends Schema + ? inferParsed

+ : never; + +export type inferRawKey< + ParsedKey extends string | number | symbol, + P extends Property | Schema, +> = P extends Property ? Raw : ParsedKey; diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/primitives/any.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/primitives/any.ts new file mode 100644 index 000000000000..0652f8934269 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/primitives/any.ts @@ -0,0 +1,7 @@ +import { type Schema, SchemaType } from "../../Schema"; +import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator"; + +export const any: () => Schema = createIdentitySchemaCreator(SchemaType.ANY, (value) => ({ + ok: true, + value, +})); diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/primitives/boolean.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/primitives/boolean.ts new file mode 100644 index 000000000000..d90a6924f1fe --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/primitives/boolean.ts @@ -0,0 +1,25 @@ +import { type Schema, SchemaType } from "../../Schema"; +import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; + +export const boolean: () => Schema = createIdentitySchemaCreator( + SchemaType.BOOLEAN, + (value, { breadcrumbsPrefix = [] } = {}) => { + if (typeof value === "boolean") { + return { + ok: true, + value, + }; + } else { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(value, "boolean"), + }, + ], + }; + } + }, +); diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/primitives/index.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/primitives/index.ts new file mode 100644 index 000000000000..a60b5e5ad81c --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/primitives/index.ts @@ -0,0 +1,6 @@ +export { any } from "./any"; +export { boolean } from "./boolean"; +export { never } from "./never"; +export { number } from "./number"; +export { string } from "./string"; +export { unknown } from "./unknown"; diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/primitives/never.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/primitives/never.ts new file mode 100644 index 000000000000..3de77c3fd7a0 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/primitives/never.ts @@ -0,0 +1,15 @@ +import { type Schema, SchemaType } from "../../Schema"; +import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator"; + +export const never: () => Schema = createIdentitySchemaCreator( + SchemaType.NEVER, + (_value, { breadcrumbsPrefix = [] } = {}) => ({ + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: "Expected never", + }, + ], + }), +); diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/primitives/number.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/primitives/number.ts new file mode 100644 index 000000000000..8b3b32cdca0c --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/primitives/number.ts @@ -0,0 +1,25 @@ +import { type Schema, SchemaType } from "../../Schema"; +import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; + +export const number: () => Schema = createIdentitySchemaCreator( + SchemaType.NUMBER, + (value, { breadcrumbsPrefix = [] } = {}) => { + if (typeof value === "number") { + return { + ok: true, + value, + }; + } else { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(value, "number"), + }, + ], + }; + } + }, +); diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/primitives/string.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/primitives/string.ts new file mode 100644 index 000000000000..f9f8e4779c1b --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/primitives/string.ts @@ -0,0 +1,25 @@ +import { type Schema, SchemaType } from "../../Schema"; +import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; + +export const string: () => Schema = createIdentitySchemaCreator( + SchemaType.STRING, + (value, { breadcrumbsPrefix = [] } = {}) => { + if (typeof value === "string") { + return { + ok: true, + value, + }; + } else { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(value, "string"), + }, + ], + }; + } + }, +); diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/primitives/unknown.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/primitives/unknown.ts new file mode 100644 index 000000000000..41c9d770b10c --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/primitives/unknown.ts @@ -0,0 +1,7 @@ +import { type Schema, SchemaType } from "../../Schema"; +import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator"; + +export const unknown: () => Schema = createIdentitySchemaCreator( + SchemaType.UNKNOWN, + (value) => ({ ok: true, value }), +); diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/record/index.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/record/index.ts new file mode 100644 index 000000000000..82e25c5c2af9 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/record/index.ts @@ -0,0 +1,2 @@ +export { record } from "./record"; +export type { BaseRecordSchema, RecordSchema } from "./types"; diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/record/record.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/record/record.ts new file mode 100644 index 000000000000..ba5307a6a45c --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/record/record.ts @@ -0,0 +1,129 @@ +import { type MaybeValid, type Schema, SchemaType, type ValidationError } from "../../Schema"; +import { entries } from "../../utils/entries"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; +import { isPlainObject } from "../../utils/isPlainObject"; +import { maybeSkipValidation } from "../../utils/maybeSkipValidation"; +import { getSchemaUtils } from "../schema-utils/index"; +import type { BaseRecordSchema, RecordSchema } from "./types"; + +export function record( + keySchema: Schema, + valueSchema: Schema, +): RecordSchema { + const baseSchema: BaseRecordSchema = { + parse: (raw, opts) => { + return validateAndTransformRecord({ + value: raw, + isKeyNumeric: keySchema.getType() === SchemaType.NUMBER, + transformKey: (key) => + keySchema.parse(key, { + ...opts, + breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), `${key} (key)`], + }), + transformValue: (value, key) => + valueSchema.parse(value, { + ...opts, + breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), `${key}`], + }), + breadcrumbsPrefix: opts?.breadcrumbsPrefix, + }); + }, + json: (parsed, opts) => { + return validateAndTransformRecord({ + value: parsed, + isKeyNumeric: keySchema.getType() === SchemaType.NUMBER, + transformKey: (key) => + keySchema.json(key, { + ...opts, + breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), `${key} (key)`], + }), + transformValue: (value, key) => + valueSchema.json(value, { + ...opts, + breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), `${key}`], + }), + breadcrumbsPrefix: opts?.breadcrumbsPrefix, + }); + }, + getType: () => SchemaType.RECORD, + }; + + return { + ...maybeSkipValidation(baseSchema), + ...getSchemaUtils(baseSchema), + }; +} + +function validateAndTransformRecord({ + value, + isKeyNumeric, + transformKey, + transformValue, + breadcrumbsPrefix = [], +}: { + value: unknown; + isKeyNumeric: boolean; + transformKey: (key: string | number) => MaybeValid; + transformValue: (value: unknown, key: string | number) => MaybeValid; + breadcrumbsPrefix: string[] | undefined; +}): MaybeValid> { + if (!isPlainObject(value)) { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(value, "object"), + }, + ], + }; + } + + return entries(value).reduce>>( + (accPromise, [stringKey, value]) => { + if (value === undefined) { + return accPromise; + } + + const acc = accPromise; + + let key: string | number = stringKey; + if (isKeyNumeric) { + const numberKey = stringKey.length > 0 ? Number(stringKey) : NaN; + if (!Number.isNaN(numberKey)) { + key = numberKey; + } + } + const transformedKey = transformKey(key); + + const transformedValue = transformValue(value, key); + + if (acc.ok && transformedKey.ok && transformedValue.ok) { + return { + ok: true, + value: { + ...acc.value, + [transformedKey.value]: transformedValue.value, + }, + }; + } + + const errors: ValidationError[] = []; + if (!acc.ok) { + errors.push(...acc.errors); + } + if (!transformedKey.ok) { + errors.push(...transformedKey.errors); + } + if (!transformedValue.ok) { + errors.push(...transformedValue.errors); + } + + return { + ok: false, + errors, + }; + }, + { ok: true, value: {} as Record }, + ); +} diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/record/types.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/record/types.ts new file mode 100644 index 000000000000..b99bb9d8786b --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/record/types.ts @@ -0,0 +1,17 @@ +import type { BaseSchema } from "../../Schema"; +import type { SchemaUtils } from "../schema-utils/index"; + +export type RecordSchema< + RawKey extends string | number, + RawValue, + ParsedKey extends string | number, + ParsedValue, +> = BaseRecordSchema & + SchemaUtils, Record>; + +export type BaseRecordSchema< + RawKey extends string | number, + RawValue, + ParsedKey extends string | number, + ParsedValue, +> = BaseSchema, Record>; diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/schema-utils/JsonError.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/schema-utils/JsonError.ts new file mode 100644 index 000000000000..7573c76be2aa --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/schema-utils/JsonError.ts @@ -0,0 +1,9 @@ +import type { ValidationError } from "../../Schema"; +import { stringifyValidationError } from "./stringifyValidationErrors"; + +export class JsonError extends Error { + constructor(public readonly errors: ValidationError[]) { + super(errors.map(stringifyValidationError).join("; ")); + Object.setPrototypeOf(this, JsonError.prototype); + } +} diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/schema-utils/ParseError.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/schema-utils/ParseError.ts new file mode 100644 index 000000000000..f1914b5965e6 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/schema-utils/ParseError.ts @@ -0,0 +1,9 @@ +import type { ValidationError } from "../../Schema"; +import { stringifyValidationError } from "./stringifyValidationErrors"; + +export class ParseError extends Error { + constructor(public readonly errors: ValidationError[]) { + super(errors.map(stringifyValidationError).join("; ")); + Object.setPrototypeOf(this, ParseError.prototype); + } +} diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/schema-utils/getSchemaUtils.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/schema-utils/getSchemaUtils.ts new file mode 100644 index 000000000000..91381a1d6626 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/schema-utils/getSchemaUtils.ts @@ -0,0 +1,181 @@ +import { type BaseSchema, type Schema, type SchemaOptions, SchemaType } from "../../Schema"; +import { JsonError } from "./JsonError"; +import { ParseError } from "./ParseError"; + +export interface SchemaUtils { + nullable: () => Schema; + optional: () => Schema; + optionalNullable: () => Schema; + transform: (transformer: SchemaTransformer) => Schema; + parseOrThrow: (raw: unknown, opts?: SchemaOptions) => Parsed; + jsonOrThrow: (raw: unknown, opts?: SchemaOptions) => Raw; +} + +export interface SchemaTransformer { + transform: (parsed: Parsed) => Transformed; + untransform: (transformed: any) => Parsed; +} + +export function getSchemaUtils(schema: BaseSchema): SchemaUtils { + return { + nullable: () => nullable(schema), + optional: () => optional(schema), + optionalNullable: () => optionalNullable(schema), + transform: (transformer) => transform(schema, transformer), + parseOrThrow: (raw, opts) => { + const parsed = schema.parse(raw, opts); + if (parsed.ok) { + return parsed.value; + } + throw new ParseError(parsed.errors); + }, + jsonOrThrow: (parsed, opts) => { + const raw = schema.json(parsed, opts); + if (raw.ok) { + return raw.value; + } + throw new JsonError(raw.errors); + }, + }; +} + +/** + * schema utils are defined in one file to resolve issues with circular imports + */ + +export function nullable(schema: BaseSchema): Schema { + const baseSchema: BaseSchema = { + parse: (raw, opts) => { + if (raw == null) { + return { + ok: true, + value: null, + }; + } + return schema.parse(raw, opts); + }, + json: (parsed, opts) => { + if (parsed == null) { + return { + ok: true, + value: null, + }; + } + return schema.json(parsed, opts); + }, + getType: () => SchemaType.NULLABLE, + }; + + return { + ...baseSchema, + ...getSchemaUtils(baseSchema), + }; +} + +export function optional( + schema: BaseSchema, +): Schema { + const baseSchema: BaseSchema = { + parse: (raw, opts) => { + if (raw == null) { + return { + ok: true, + value: undefined, + }; + } + return schema.parse(raw, opts); + }, + json: (parsed, opts) => { + if (opts?.omitUndefined && parsed === undefined) { + return { + ok: true, + value: undefined, + }; + } + if (parsed == null) { + return { + ok: true, + value: null, + }; + } + return schema.json(parsed, opts); + }, + getType: () => SchemaType.OPTIONAL, + }; + + return { + ...baseSchema, + ...getSchemaUtils(baseSchema), + }; +} + +export function optionalNullable( + schema: BaseSchema, +): Schema { + const baseSchema: BaseSchema = { + parse: (raw, opts) => { + if (raw === undefined) { + return { + ok: true, + value: undefined, + }; + } + if (raw === null) { + return { + ok: true, + value: null, + }; + } + return schema.parse(raw, opts); + }, + json: (parsed, opts) => { + if (parsed === undefined) { + return { + ok: true, + value: undefined, + }; + } + if (parsed === null) { + return { + ok: true, + value: null, + }; + } + return schema.json(parsed, opts); + }, + getType: () => SchemaType.OPTIONAL_NULLABLE, + }; + + return { + ...baseSchema, + ...getSchemaUtils(baseSchema), + }; +} + +export function transform( + schema: BaseSchema, + transformer: SchemaTransformer, +): Schema { + const baseSchema: BaseSchema = { + parse: (raw, opts) => { + const parsed = schema.parse(raw, opts); + if (!parsed.ok) { + return parsed; + } + return { + ok: true, + value: transformer.transform(parsed.value), + }; + }, + json: (transformed, opts) => { + const parsed = transformer.untransform(transformed); + return schema.json(parsed, opts); + }, + getType: () => schema.getType(), + }; + + return { + ...baseSchema, + ...getSchemaUtils(baseSchema), + }; +} diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/schema-utils/index.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/schema-utils/index.ts new file mode 100644 index 000000000000..9d0eed7ae843 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/schema-utils/index.ts @@ -0,0 +1,4 @@ +export type { SchemaUtils } from "./getSchemaUtils"; +export { getSchemaUtils, optional, transform } from "./getSchemaUtils"; +export { JsonError } from "./JsonError"; +export { ParseError } from "./ParseError"; diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/schema-utils/stringifyValidationErrors.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/schema-utils/stringifyValidationErrors.ts new file mode 100644 index 000000000000..1066d00384aa --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/schema-utils/stringifyValidationErrors.ts @@ -0,0 +1,8 @@ +import type { ValidationError } from "../../Schema"; + +export function stringifyValidationError(error: ValidationError): string { + if (error.path.length === 0) { + return error.message; + } + return `${error.path.join(" -> ")}: ${error.message}`; +} diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/set/index.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/set/index.ts new file mode 100644 index 000000000000..f3310e8bdade --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/set/index.ts @@ -0,0 +1 @@ +export { set } from "./set"; diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/set/set.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/set/set.ts new file mode 100644 index 000000000000..bf31bc5fdb43 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/set/set.ts @@ -0,0 +1,43 @@ +import { type BaseSchema, type Schema, SchemaType } from "../../Schema"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; +import { maybeSkipValidation } from "../../utils/maybeSkipValidation"; +import { list } from "../list/index"; +import { getSchemaUtils } from "../schema-utils/index"; + +export function set(schema: Schema): Schema> { + const listSchema = list(schema); + const baseSchema: BaseSchema> = { + parse: (raw, opts) => { + const parsedList = listSchema.parse(raw, opts); + if (parsedList.ok) { + return { + ok: true, + value: new Set(parsedList.value), + }; + } else { + return parsedList; + } + }, + json: (parsed, opts) => { + if (!(parsed instanceof Set)) { + return { + ok: false, + errors: [ + { + path: opts?.breadcrumbsPrefix ?? [], + message: getErrorMessageForIncorrectType(parsed, "Set"), + }, + ], + }; + } + const jsonList = listSchema.json([...parsed], opts); + return jsonList; + }, + getType: () => SchemaType.SET, + }; + + return { + ...maybeSkipValidation(baseSchema), + ...getSchemaUtils(baseSchema), + }; +} diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/undiscriminated-union/index.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/undiscriminated-union/index.ts new file mode 100644 index 000000000000..75b71cb35656 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/undiscriminated-union/index.ts @@ -0,0 +1,6 @@ +export type { + inferParsedUnidiscriminatedUnionSchema, + inferRawUnidiscriminatedUnionSchema, + UndiscriminatedUnionSchema, +} from "./types"; +export { undiscriminatedUnion } from "./undiscriminatedUnion"; diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/undiscriminated-union/types.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/undiscriminated-union/types.ts new file mode 100644 index 000000000000..3ef19e2c09e1 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/undiscriminated-union/types.ts @@ -0,0 +1,10 @@ +import type { inferParsed, inferRaw, Schema } from "../../Schema"; + +export type UndiscriminatedUnionSchema = Schema< + inferRawUnidiscriminatedUnionSchema, + inferParsedUnidiscriminatedUnionSchema +>; + +export type inferRawUnidiscriminatedUnionSchema = inferRaw; + +export type inferParsedUnidiscriminatedUnionSchema = inferParsed; diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/undiscriminated-union/undiscriminatedUnion.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/undiscriminated-union/undiscriminatedUnion.ts new file mode 100644 index 000000000000..26ad5f9b9112 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/undiscriminated-union/undiscriminatedUnion.ts @@ -0,0 +1,67 @@ +import { + type BaseSchema, + type MaybeValid, + type Schema, + type SchemaOptions, + SchemaType, + type ValidationError, +} from "../../Schema"; +import { maybeSkipValidation } from "../../utils/maybeSkipValidation"; +import { getSchemaUtils } from "../schema-utils/index"; +import type { inferParsedUnidiscriminatedUnionSchema, inferRawUnidiscriminatedUnionSchema } from "./types"; + +export function undiscriminatedUnion, ...Schema[]]>( + schemas: Schemas, +): Schema, inferParsedUnidiscriminatedUnionSchema> { + const baseSchema: BaseSchema< + inferRawUnidiscriminatedUnionSchema, + inferParsedUnidiscriminatedUnionSchema + > = { + parse: (raw, opts) => { + return validateAndTransformUndiscriminatedUnion>( + (schema, opts) => schema.parse(raw, opts), + schemas, + opts, + ); + }, + json: (parsed, opts) => { + return validateAndTransformUndiscriminatedUnion>( + (schema, opts) => schema.json(parsed, opts), + schemas, + opts, + ); + }, + getType: () => SchemaType.UNDISCRIMINATED_UNION, + }; + + return { + ...maybeSkipValidation(baseSchema), + ...getSchemaUtils(baseSchema), + }; +} + +function validateAndTransformUndiscriminatedUnion( + transform: (schema: Schema, opts: SchemaOptions) => MaybeValid, + schemas: Schema[], + opts: SchemaOptions | undefined, +): MaybeValid { + const errors: ValidationError[] = []; + for (const [index, schema] of schemas.entries()) { + const transformed = transform(schema, { ...opts, skipValidation: false }); + if (transformed.ok) { + return transformed; + } else { + for (const error of transformed.errors) { + errors.push({ + path: error.path, + message: `[Variant ${index}] ${error.message}`, + }); + } + } + } + + return { + ok: false, + errors, + }; +} diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/union/discriminant.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/union/discriminant.ts new file mode 100644 index 000000000000..73cd62adeba5 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/union/discriminant.ts @@ -0,0 +1,14 @@ +export function discriminant( + parsedDiscriminant: ParsedDiscriminant, + rawDiscriminant: RawDiscriminant, +): Discriminant { + return { + parsedDiscriminant, + rawDiscriminant, + }; +} + +export interface Discriminant { + parsedDiscriminant: ParsedDiscriminant; + rawDiscriminant: RawDiscriminant; +} diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/union/index.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/union/index.ts new file mode 100644 index 000000000000..22289b66715c --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/union/index.ts @@ -0,0 +1,10 @@ +export type { Discriminant } from "./discriminant"; +export { discriminant } from "./discriminant"; +export type { + inferParsedDiscriminant, + inferParsedUnion, + inferRawDiscriminant, + inferRawUnion, + UnionSubtypes, +} from "./types"; +export { union } from "./union"; diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/union/types.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/union/types.ts new file mode 100644 index 000000000000..79753436d6cf --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/union/types.ts @@ -0,0 +1,26 @@ +import type { inferParsedObject, inferRawObject, ObjectSchema } from "../object/index"; +import type { Discriminant } from "./discriminant"; + +export type UnionSubtypes = { + [K in DiscriminantValues]: ObjectSchema; +}; + +export type inferRawUnion, U extends UnionSubtypes> = { + [K in keyof U]: Record, K> & inferRawObject; +}[keyof U]; + +export type inferParsedUnion, U extends UnionSubtypes> = { + [K in keyof U]: Record, K> & inferParsedObject; +}[keyof U]; + +export type inferRawDiscriminant> = D extends string + ? D + : D extends Discriminant + ? Raw + : never; + +export type inferParsedDiscriminant> = D extends string + ? D + : D extends Discriminant + ? Parsed + : never; diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/union/union.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/union/union.ts new file mode 100644 index 000000000000..7da4271a27c0 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/builders/union/union.ts @@ -0,0 +1,176 @@ +import { type BaseSchema, type MaybeValid, SchemaType } from "../../Schema"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; +import { isPlainObject } from "../../utils/isPlainObject"; +import { keys } from "../../utils/keys"; +import { maybeSkipValidation } from "../../utils/maybeSkipValidation"; +import { enum_ } from "../enum/index"; +import type { ObjectSchema } from "../object/index"; +import { getObjectLikeUtils, type ObjectLikeSchema } from "../object-like"; +import { getSchemaUtils } from "../schema-utils/index"; +import type { Discriminant } from "./discriminant"; +import type { + inferParsedDiscriminant, + inferParsedUnion, + inferRawDiscriminant, + inferRawUnion, + UnionSubtypes, +} from "./types"; + +export function union, U extends UnionSubtypes>( + discriminant: D, + union: U, +): ObjectLikeSchema, inferParsedUnion> { + const rawDiscriminant = + typeof discriminant === "string" ? discriminant : (discriminant.rawDiscriminant as inferRawDiscriminant); + const parsedDiscriminant = + typeof discriminant === "string" + ? discriminant + : (discriminant.parsedDiscriminant as inferParsedDiscriminant); + + const discriminantValueSchema = enum_(keys(union) as string[]); + + const baseSchema: BaseSchema, inferParsedUnion> = { + parse: (raw, opts) => { + return transformAndValidateUnion({ + value: raw, + discriminant: rawDiscriminant, + transformedDiscriminant: parsedDiscriminant, + transformDiscriminantValue: (discriminantValue) => + discriminantValueSchema.parse(discriminantValue, { + allowUnrecognizedEnumValues: opts?.allowUnrecognizedUnionMembers, + breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), rawDiscriminant], + }), + getAdditionalPropertiesSchema: (discriminantValue) => union[discriminantValue], + allowUnrecognizedUnionMembers: opts?.allowUnrecognizedUnionMembers, + transformAdditionalProperties: (additionalProperties, additionalPropertiesSchema) => + additionalPropertiesSchema.parse(additionalProperties, opts), + breadcrumbsPrefix: opts?.breadcrumbsPrefix, + }); + }, + json: (parsed, opts) => { + return transformAndValidateUnion({ + value: parsed, + discriminant: parsedDiscriminant, + transformedDiscriminant: rawDiscriminant, + transformDiscriminantValue: (discriminantValue) => + discriminantValueSchema.json(discriminantValue, { + allowUnrecognizedEnumValues: opts?.allowUnrecognizedUnionMembers, + breadcrumbsPrefix: [...(opts?.breadcrumbsPrefix ?? []), parsedDiscriminant], + }), + getAdditionalPropertiesSchema: (discriminantValue) => union[discriminantValue], + allowUnrecognizedUnionMembers: opts?.allowUnrecognizedUnionMembers, + transformAdditionalProperties: (additionalProperties, additionalPropertiesSchema) => + additionalPropertiesSchema.json(additionalProperties, opts), + breadcrumbsPrefix: opts?.breadcrumbsPrefix, + }); + }, + getType: () => SchemaType.UNION, + }; + + return { + ...maybeSkipValidation(baseSchema), + ...getSchemaUtils(baseSchema), + ...getObjectLikeUtils(baseSchema), + }; +} + +function transformAndValidateUnion< + TransformedDiscriminant extends string, + TransformedDiscriminantValue extends string, + TransformedAdditionalProperties, +>({ + value, + discriminant, + transformedDiscriminant, + transformDiscriminantValue, + getAdditionalPropertiesSchema, + allowUnrecognizedUnionMembers = false, + transformAdditionalProperties, + breadcrumbsPrefix = [], +}: { + value: unknown; + discriminant: string; + transformedDiscriminant: TransformedDiscriminant; + transformDiscriminantValue: (discriminantValue: unknown) => MaybeValid; + getAdditionalPropertiesSchema: (discriminantValue: string) => ObjectSchema | undefined; + allowUnrecognizedUnionMembers: boolean | undefined; + transformAdditionalProperties: ( + additionalProperties: unknown, + additionalPropertiesSchema: ObjectSchema, + ) => MaybeValid; + breadcrumbsPrefix: string[] | undefined; +}): MaybeValid & TransformedAdditionalProperties> { + if (!isPlainObject(value)) { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(value, "object"), + }, + ], + }; + } + + const { [discriminant]: discriminantValue, ...additionalProperties } = value; + + if (discriminantValue == null) { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: `Missing discriminant ("${discriminant}")`, + }, + ], + }; + } + + const transformedDiscriminantValue = transformDiscriminantValue(discriminantValue); + if (!transformedDiscriminantValue.ok) { + return { + ok: false, + errors: transformedDiscriminantValue.errors, + }; + } + + const additionalPropertiesSchema = getAdditionalPropertiesSchema(transformedDiscriminantValue.value); + + if (additionalPropertiesSchema == null) { + if (allowUnrecognizedUnionMembers) { + return { + ok: true, + value: { + [transformedDiscriminant]: transformedDiscriminantValue.value, + ...additionalProperties, + } as Record & TransformedAdditionalProperties, + }; + } else { + return { + ok: false, + errors: [ + { + path: [...breadcrumbsPrefix, discriminant], + message: "Unexpected discriminant value", + }, + ], + }; + } + } + + const transformedAdditionalProperties = transformAdditionalProperties( + additionalProperties, + additionalPropertiesSchema, + ); + if (!transformedAdditionalProperties.ok) { + return transformedAdditionalProperties; + } + + return { + ok: true, + value: { + [transformedDiscriminant]: discriminantValue, + ...transformedAdditionalProperties.value, + } as Record & TransformedAdditionalProperties, + }; +} diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/index.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/index.ts new file mode 100644 index 000000000000..60c0b5988d32 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/index.ts @@ -0,0 +1,2 @@ +export * from "./builders/index"; +export type { inferParsed, inferRaw, Schema, SchemaOptions } from "./Schema"; diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/utils/MaybePromise.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/utils/MaybePromise.ts new file mode 100644 index 000000000000..9cd354b3418e --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/utils/MaybePromise.ts @@ -0,0 +1 @@ +export type MaybePromise = T | Promise; diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/utils/addQuestionMarksToNullableProperties.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/utils/addQuestionMarksToNullableProperties.ts new file mode 100644 index 000000000000..59f9e658867b --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/utils/addQuestionMarksToNullableProperties.ts @@ -0,0 +1,9 @@ +export type addQuestionMarksToNullableProperties = { + [K in OptionalKeys]?: T[K]; +} & Pick>; + +export type OptionalKeys = { + [K in keyof T]-?: undefined extends T[K] ? K : never; +}[keyof T]; + +export type RequiredKeys = Exclude>; diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/utils/createIdentitySchemaCreator.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/utils/createIdentitySchemaCreator.ts new file mode 100644 index 000000000000..d299ada74c11 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/utils/createIdentitySchemaCreator.ts @@ -0,0 +1,21 @@ +import { getSchemaUtils } from "../builders/schema-utils/index"; +import type { BaseSchema, MaybeValid, Schema, SchemaOptions, SchemaType } from "../Schema"; +import { maybeSkipValidation } from "./maybeSkipValidation"; + +export function createIdentitySchemaCreator( + schemaType: SchemaType, + validate: (value: unknown, opts?: SchemaOptions) => MaybeValid, +): () => Schema { + return () => { + const baseSchema: BaseSchema = { + parse: validate, + json: validate, + getType: () => schemaType, + }; + + return { + ...maybeSkipValidation(baseSchema), + ...getSchemaUtils(baseSchema), + }; + }; +} diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/utils/entries.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/utils/entries.ts new file mode 100644 index 000000000000..2d5c93d657ce --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/utils/entries.ts @@ -0,0 +1,3 @@ +export function entries(object: T): [keyof T, T[keyof T]][] { + return Object.entries(object) as [keyof T, T[keyof T]][]; +} diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/utils/filterObject.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/utils/filterObject.ts new file mode 100644 index 000000000000..70527d10013b --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/utils/filterObject.ts @@ -0,0 +1,13 @@ +export function filterObject(obj: T, keysToInclude: K[]): Pick { + const keysToIncludeSet = new Set(keysToInclude); + return Object.entries(obj).reduce( + (acc, [key, value]) => { + if (keysToIncludeSet.has(key as K)) { + acc[key as K] = value as T[K]; + } + return acc; + // eslint-disable-next-line @typescript-eslint/prefer-reduce-type-parameter + }, + {} as Pick, + ); +} diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/utils/getErrorMessageForIncorrectType.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/utils/getErrorMessageForIncorrectType.ts new file mode 100644 index 000000000000..1a5c31027ce9 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/utils/getErrorMessageForIncorrectType.ts @@ -0,0 +1,25 @@ +export function getErrorMessageForIncorrectType(value: unknown, expectedType: string): string { + return `Expected ${expectedType}. Received ${getTypeAsString(value)}.`; +} + +function getTypeAsString(value: unknown): string { + if (Array.isArray(value)) { + return "list"; + } + if (value === null) { + return "null"; + } + if (value instanceof BigInt) { + return "BigInt"; + } + switch (typeof value) { + case "string": + return `"${value}"`; + case "bigint": + case "number": + case "boolean": + case "undefined": + return `${value}`; + } + return typeof value; +} diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/utils/isPlainObject.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/utils/isPlainObject.ts new file mode 100644 index 000000000000..db82a722c35b --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/utils/isPlainObject.ts @@ -0,0 +1,17 @@ +// borrowed from https://github.com/lodash/lodash/blob/master/isPlainObject.js +export function isPlainObject(value: unknown): value is Record { + if (typeof value !== "object" || value === null) { + return false; + } + + if (Object.getPrototypeOf(value) === null) { + return true; + } + + let proto = value; + while (Object.getPrototypeOf(proto) !== null) { + proto = Object.getPrototypeOf(proto); + } + + return Object.getPrototypeOf(value) === proto; +} diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/utils/keys.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/utils/keys.ts new file mode 100644 index 000000000000..2e0930e2d70b --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/utils/keys.ts @@ -0,0 +1,3 @@ +export function keys(object: T): (keyof T)[] { + return Object.keys(object) as (keyof T)[]; +} diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/utils/maybeSkipValidation.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/utils/maybeSkipValidation.ts new file mode 100644 index 000000000000..70be6478ba43 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/utils/maybeSkipValidation.ts @@ -0,0 +1,38 @@ +import type { BaseSchema, MaybeValid, SchemaOptions } from "../Schema"; + +export function maybeSkipValidation, Raw, Parsed>(schema: S): S { + return { + ...schema, + json: transformAndMaybeSkipValidation(schema.json), + parse: transformAndMaybeSkipValidation(schema.parse), + }; +} + +function transformAndMaybeSkipValidation( + transform: (value: unknown, opts?: SchemaOptions) => MaybeValid, +): (value: unknown, opts?: SchemaOptions) => MaybeValid { + return (value, opts): MaybeValid => { + const transformed = transform(value, opts); + const { skipValidation = false } = opts ?? {}; + if (!transformed.ok && skipValidation) { + // biome-ignore lint/suspicious/noConsole: allow console + console.warn( + [ + "Failed to validate.", + ...transformed.errors.map( + (error) => + " - " + + (error.path.length > 0 ? `${error.path.join(".")}: ${error.message}` : error.message), + ), + ].join("\n"), + ); + + return { + ok: true, + value: value as T, + }; + } else { + return transformed; + } + }; +} diff --git a/seed/ts-express/inferred-auth-implicit-reference/core/schemas/utils/partition.ts b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/utils/partition.ts new file mode 100644 index 000000000000..f58d6f3d35f3 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/core/schemas/utils/partition.ts @@ -0,0 +1,12 @@ +export function partition(items: readonly T[], predicate: (item: T) => boolean): [T[], T[]] { + const trueItems: T[] = [], + falseItems: T[] = []; + for (const item of items) { + if (predicate(item)) { + trueItems.push(item); + } else { + falseItems.push(item); + } + } + return [trueItems, falseItems]; +} diff --git a/seed/ts-express/inferred-auth-implicit-reference/errors/SeedInferredAuthImplicitError.ts b/seed/ts-express/inferred-auth-implicit-reference/errors/SeedInferredAuthImplicitError.ts new file mode 100644 index 000000000000..6888e0959ba7 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/errors/SeedInferredAuthImplicitError.ts @@ -0,0 +1,17 @@ +// This file was auto-generated by Fern from our API Definition. + +import type express from "express"; + +export abstract class SeedInferredAuthImplicitError extends Error { + constructor(public readonly errorName?: string) { + super(); + Object.setPrototypeOf(this, new.target.prototype); + if (Error.captureStackTrace) { + Error.captureStackTrace(this, this.constructor); + } + + this.name = this.constructor.name; + } + + public abstract send(res: express.Response): Promise; +} diff --git a/seed/ts-express/inferred-auth-implicit-reference/errors/index.ts b/seed/ts-express/inferred-auth-implicit-reference/errors/index.ts new file mode 100644 index 000000000000..33ba09736bba --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/errors/index.ts @@ -0,0 +1 @@ +export { SeedInferredAuthImplicitError } from "./SeedInferredAuthImplicitError"; diff --git a/seed/ts-express/inferred-auth-implicit-reference/index.ts b/seed/ts-express/inferred-auth-implicit-reference/index.ts new file mode 100644 index 000000000000..ce75783bb2da --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/index.ts @@ -0,0 +1,3 @@ +export * as SeedInferredAuthImplicit from "./api"; +export { SeedInferredAuthImplicitError } from "./errors"; +export { register } from "./register"; diff --git a/seed/ts-express/inferred-auth-implicit-reference/register.ts b/seed/ts-express/inferred-auth-implicit-reference/register.ts new file mode 100644 index 000000000000..13017b93a0ca --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/register.ts @@ -0,0 +1,26 @@ +// This file was auto-generated by Fern from our API Definition. + +import type express from "express"; +import type { AuthService } from "./api/resources/auth/service/AuthService"; +import type { ApiService as nested_ApiService } from "./api/resources/nested/resources/api/service/ApiService"; +import type { ApiService as nestedNoAuth_ApiService } from "./api/resources/nestedNoAuth/resources/api/service/ApiService"; +import type { SimpleService } from "./api/resources/simple/service/SimpleService"; + +export function register( + expressApp: express.Express | express.Router, + services: { + auth: AuthService; + simple: SimpleService; + nestedNoAuth: { + api: nestedNoAuth_ApiService; + }; + nested: { + api: nested_ApiService; + }; + }, +): void { + (expressApp as any).use("/", services.auth.toRouter()); + (expressApp as any).use("/nested-no-auth", services.nestedNoAuth.api.toRouter()); + (expressApp as any).use("/nested", services.nested.api.toRouter()); + (expressApp as any).use("/", services.simple.toRouter()); +} diff --git a/seed/ts-express/inferred-auth-implicit-reference/serialization/index.ts b/seed/ts-express/inferred-auth-implicit-reference/serialization/index.ts new file mode 100644 index 000000000000..3e5335fe4215 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/serialization/index.ts @@ -0,0 +1 @@ +export * from "./resources"; diff --git a/seed/ts-express/inferred-auth-implicit-reference/serialization/resources/auth/index.ts b/seed/ts-express/inferred-auth-implicit-reference/serialization/resources/auth/index.ts new file mode 100644 index 000000000000..eea524d65570 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/serialization/resources/auth/index.ts @@ -0,0 +1 @@ +export * from "./types"; diff --git a/seed/ts-express/inferred-auth-implicit-reference/serialization/resources/auth/types/GetTokenRequest.ts b/seed/ts-express/inferred-auth-implicit-reference/serialization/resources/auth/types/GetTokenRequest.ts new file mode 100644 index 000000000000..c6e3d4dfab77 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/serialization/resources/auth/types/GetTokenRequest.ts @@ -0,0 +1,26 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as SeedInferredAuthImplicit from "../../../../api/index"; +import * as core from "../../../../core"; +import type * as serializers from "../../../index"; + +export const GetTokenRequest: core.serialization.ObjectSchema< + serializers.GetTokenRequest.Raw, + SeedInferredAuthImplicit.GetTokenRequest +> = core.serialization.object({ + clientId: core.serialization.property("client_id", core.serialization.string()), + clientSecret: core.serialization.property("client_secret", core.serialization.string()), + audience: core.serialization.stringLiteral("https://api.example.com"), + grantType: core.serialization.property("grant_type", core.serialization.stringLiteral("client_credentials")), + scope: core.serialization.string().optional(), +}); + +export declare namespace GetTokenRequest { + export interface Raw { + client_id: string; + client_secret: string; + audience: "https://api.example.com"; + grant_type: "client_credentials"; + scope?: string | null; + } +} diff --git a/seed/ts-express/inferred-auth-implicit-reference/serialization/resources/auth/types/RefreshTokenRequest.ts b/seed/ts-express/inferred-auth-implicit-reference/serialization/resources/auth/types/RefreshTokenRequest.ts new file mode 100644 index 000000000000..89634a9943c2 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/serialization/resources/auth/types/RefreshTokenRequest.ts @@ -0,0 +1,28 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as SeedInferredAuthImplicit from "../../../../api/index"; +import * as core from "../../../../core"; +import type * as serializers from "../../../index"; + +export const RefreshTokenRequest: core.serialization.ObjectSchema< + serializers.RefreshTokenRequest.Raw, + SeedInferredAuthImplicit.RefreshTokenRequest +> = core.serialization.object({ + clientId: core.serialization.property("client_id", core.serialization.string()), + clientSecret: core.serialization.property("client_secret", core.serialization.string()), + refreshToken: core.serialization.property("refresh_token", core.serialization.string()), + audience: core.serialization.stringLiteral("https://api.example.com"), + grantType: core.serialization.property("grant_type", core.serialization.stringLiteral("refresh_token")), + scope: core.serialization.string().optional(), +}); + +export declare namespace RefreshTokenRequest { + export interface Raw { + client_id: string; + client_secret: string; + refresh_token: string; + audience: "https://api.example.com"; + grant_type: "refresh_token"; + scope?: string | null; + } +} diff --git a/seed/ts-express/inferred-auth-implicit-reference/serialization/resources/auth/types/TokenResponse.ts b/seed/ts-express/inferred-auth-implicit-reference/serialization/resources/auth/types/TokenResponse.ts new file mode 100644 index 000000000000..3946fd7ef8d6 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/serialization/resources/auth/types/TokenResponse.ts @@ -0,0 +1,22 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as SeedInferredAuthImplicit from "../../../../api/index"; +import * as core from "../../../../core"; +import type * as serializers from "../../../index"; + +export const TokenResponse: core.serialization.ObjectSchema< + serializers.TokenResponse.Raw, + SeedInferredAuthImplicit.TokenResponse +> = core.serialization.object({ + accessToken: core.serialization.property("access_token", core.serialization.string()), + expiresIn: core.serialization.property("expires_in", core.serialization.number()), + refreshToken: core.serialization.property("refresh_token", core.serialization.string().optional()), +}); + +export declare namespace TokenResponse { + export interface Raw { + access_token: string; + expires_in: number; + refresh_token?: string | null; + } +} diff --git a/seed/ts-express/inferred-auth-implicit-reference/serialization/resources/auth/types/index.ts b/seed/ts-express/inferred-auth-implicit-reference/serialization/resources/auth/types/index.ts new file mode 100644 index 000000000000..3f9dd021bed5 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/serialization/resources/auth/types/index.ts @@ -0,0 +1,3 @@ +export * from "./GetTokenRequest"; +export * from "./RefreshTokenRequest"; +export * from "./TokenResponse"; diff --git a/seed/ts-express/inferred-auth-implicit-reference/serialization/resources/index.ts b/seed/ts-express/inferred-auth-implicit-reference/serialization/resources/index.ts new file mode 100644 index 000000000000..a023a1ef6272 --- /dev/null +++ b/seed/ts-express/inferred-auth-implicit-reference/serialization/resources/index.ts @@ -0,0 +1,2 @@ +export * as auth from "./auth"; +export * from "./auth/types"; diff --git a/seed/ts-express/inferred-auth-implicit-reference/snippet.json b/seed/ts-express/inferred-auth-implicit-reference/snippet.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/test-definitions/fern/apis/inferred-auth-implicit-api-key/definition/api.yml b/test-definitions/fern/apis/inferred-auth-implicit-api-key/definition/api.yml new file mode 100644 index 000000000000..0208b68dc71b --- /dev/null +++ b/test-definitions/fern/apis/inferred-auth-implicit-api-key/definition/api.yml @@ -0,0 +1,7 @@ +name: inferred-auth-implicit-api-key + +auth: InferredAuthScheme +auth-schemes: + InferredAuthScheme: + scheme: bearer + get-token: POST /token diff --git a/test-definitions/fern/apis/inferred-auth-implicit-api-key/definition/auth.yml b/test-definitions/fern/apis/inferred-auth-implicit-api-key/definition/auth.yml new file mode 100644 index 000000000000..fdf289c4dcf4 --- /dev/null +++ b/test-definitions/fern/apis/inferred-auth-implicit-api-key/definition/auth.yml @@ -0,0 +1,24 @@ +types: + TokenResponse: + docs: | + An auth token response. + properties: + access_token: string + token_type: string + expires_in: integer + scope: optional + +service: + auth: false + base-path: / + endpoints: + getToken: + path: /token + method: POST + request: + name: GetTokenRequest + headers: + X-Api-Key: + name: api_key + type: string + response: TokenResponse diff --git a/test-definitions/fern/apis/inferred-auth-implicit-api-key/definition/nested-no-auth/api.yml b/test-definitions/fern/apis/inferred-auth-implicit-api-key/definition/nested-no-auth/api.yml new file mode 100644 index 000000000000..87767243f516 --- /dev/null +++ b/test-definitions/fern/apis/inferred-auth-implicit-api-key/definition/nested-no-auth/api.yml @@ -0,0 +1,7 @@ +service: + auth: false + base-path: /nested-no-auth + endpoints: + getSomething: + path: /get-something + method: GET diff --git a/test-definitions/fern/apis/inferred-auth-implicit-api-key/definition/nested/api.yml b/test-definitions/fern/apis/inferred-auth-implicit-api-key/definition/nested/api.yml new file mode 100644 index 000000000000..7cf493d0cd2e --- /dev/null +++ b/test-definitions/fern/apis/inferred-auth-implicit-api-key/definition/nested/api.yml @@ -0,0 +1,7 @@ +service: + auth: true + base-path: /nested + endpoints: + getSomething: + path: /get-something + method: GET diff --git a/test-definitions/fern/apis/inferred-auth-implicit-api-key/definition/simple.yml b/test-definitions/fern/apis/inferred-auth-implicit-api-key/definition/simple.yml new file mode 100644 index 000000000000..90eb1eb8ac09 --- /dev/null +++ b/test-definitions/fern/apis/inferred-auth-implicit-api-key/definition/simple.yml @@ -0,0 +1,7 @@ +service: + auth: true + base-path: / + endpoints: + getSomething: + path: /get-something + method: GET diff --git a/test-definitions/fern/apis/inferred-auth-implicit-api-key/generators.yml b/test-definitions/fern/apis/inferred-auth-implicit-api-key/generators.yml new file mode 100644 index 000000000000..6d90e9ca9955 --- /dev/null +++ b/test-definitions/fern/apis/inferred-auth-implicit-api-key/generators.yml @@ -0,0 +1,22 @@ +# yaml-language-server: $schema=https://schema.buildwithfern.dev/generators-yml.json +groups: + php-sdk: + generators: + - name: fernapi/fern-php-sdk + version: latest + ir-version: v61 + github: + token: ${GITHUB_TOKEN} + mode: push + uri: fern-api/php-sdk-tests + branch: inferred-auth-implicit + go-sdk: + generators: + - name: fernapi/fern-go-sdk + version: latest + ir-version: v61 + github: + token: ${GITHUB_TOKEN} + mode: push + uri: fern-api/go-sdk-tests + branch: inferred-auth-implicit diff --git a/test-definitions/fern/apis/inferred-auth-implicit-reference/definition/api.yml b/test-definitions/fern/apis/inferred-auth-implicit-reference/definition/api.yml new file mode 100644 index 000000000000..b4e5cb2960f4 --- /dev/null +++ b/test-definitions/fern/apis/inferred-auth-implicit-reference/definition/api.yml @@ -0,0 +1,7 @@ +name: inferred-auth-implicit + +auth: InferredAuthScheme +auth-schemes: + InferredAuthScheme: + scheme: bearer + get-token: POST /token diff --git a/test-definitions/fern/apis/inferred-auth-implicit-reference/definition/auth.yml b/test-definitions/fern/apis/inferred-auth-implicit-reference/definition/auth.yml new file mode 100644 index 000000000000..40adcb7a6c63 --- /dev/null +++ b/test-definitions/fern/apis/inferred-auth-implicit-reference/definition/auth.yml @@ -0,0 +1,45 @@ +types: + GetTokenRequest: + docs: | + A request to obtain an OAuth token. + properties: + client_id: string + client_secret: string + audience: literal<"https://api.example.com"> + grant_type: literal<"client_credentials"> + scope: optional + + RefreshTokenRequest: + docs: | + A request to refresh an OAuth token. + properties: + client_id: string + client_secret: string + refresh_token: string + audience: literal<"https://api.example.com"> + grant_type: literal<"refresh_token"> + scope: optional + + TokenResponse: + docs: | + An OAuth token response. + properties: + access_token: string + expires_in: integer + refresh_token: optional + +service: + auth: false + base-path: / + endpoints: + getTokenWithClientCredentials: + path: /token + method: POST + request: GetTokenRequest + response: TokenResponse + + refreshToken: + path: /token/refresh + method: POST + request: RefreshTokenRequest + response: TokenResponse diff --git a/test-definitions/fern/apis/inferred-auth-implicit-reference/definition/nested-no-auth/api.yml b/test-definitions/fern/apis/inferred-auth-implicit-reference/definition/nested-no-auth/api.yml new file mode 100644 index 000000000000..87767243f516 --- /dev/null +++ b/test-definitions/fern/apis/inferred-auth-implicit-reference/definition/nested-no-auth/api.yml @@ -0,0 +1,7 @@ +service: + auth: false + base-path: /nested-no-auth + endpoints: + getSomething: + path: /get-something + method: GET diff --git a/test-definitions/fern/apis/inferred-auth-implicit-reference/definition/nested/api.yml b/test-definitions/fern/apis/inferred-auth-implicit-reference/definition/nested/api.yml new file mode 100644 index 000000000000..7cf493d0cd2e --- /dev/null +++ b/test-definitions/fern/apis/inferred-auth-implicit-reference/definition/nested/api.yml @@ -0,0 +1,7 @@ +service: + auth: true + base-path: /nested + endpoints: + getSomething: + path: /get-something + method: GET diff --git a/test-definitions/fern/apis/inferred-auth-implicit-reference/definition/simple.yml b/test-definitions/fern/apis/inferred-auth-implicit-reference/definition/simple.yml new file mode 100644 index 000000000000..90eb1eb8ac09 --- /dev/null +++ b/test-definitions/fern/apis/inferred-auth-implicit-reference/definition/simple.yml @@ -0,0 +1,7 @@ +service: + auth: true + base-path: / + endpoints: + getSomething: + path: /get-something + method: GET diff --git a/test-definitions/fern/apis/inferred-auth-implicit-reference/generators.yml b/test-definitions/fern/apis/inferred-auth-implicit-reference/generators.yml new file mode 100644 index 000000000000..6d90e9ca9955 --- /dev/null +++ b/test-definitions/fern/apis/inferred-auth-implicit-reference/generators.yml @@ -0,0 +1,22 @@ +# yaml-language-server: $schema=https://schema.buildwithfern.dev/generators-yml.json +groups: + php-sdk: + generators: + - name: fernapi/fern-php-sdk + version: latest + ir-version: v61 + github: + token: ${GITHUB_TOKEN} + mode: push + uri: fern-api/php-sdk-tests + branch: inferred-auth-implicit + go-sdk: + generators: + - name: fernapi/fern-go-sdk + version: latest + ir-version: v61 + github: + token: ${GITHUB_TOKEN} + mode: push + uri: fern-api/go-sdk-tests + branch: inferred-auth-implicit